easy-loadimage.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. <template>
  2. <view class="easy-loadimage" :id="uid">
  3. <image class="origin-img" :src="imageSrc" mode="aspectFill" v-if="loadImg&&!isLoadError" v-show="showImg"
  4. :class="{'no-transition':!openTransition,'show-transition':showTransition&&openTransition}"
  5. @load="handleImgLoad" @error="handleImgError">
  6. </image>
  7. <view class="loadfail-img" v-else-if="isLoadError" :style="{ 'background-image': `url(${domain}/static/images/loadfail.png)`}"></view>
  8. <view :class="['loading-img','spin-circle',loadingMode]" v-show="!showImg&&!isLoadError" :style="{ 'background-image': `url(${domain}/static/images/loading.gif)`}"></view>
  9. </view>
  10. </template>
  11. <script>
  12. // +----------------------------------------------------------------------
  13. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  14. // +----------------------------------------------------------------------
  15. // | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
  16. // +----------------------------------------------------------------------
  17. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  18. // +----------------------------------------------------------------------
  19. // | Author: CRMEB Team <admin@crmeb.com>
  20. // +----------------------------------------------------------------------
  21. import { throttle } from '@/libs/uniApi';
  22. import { HTTP_REQUEST_URL } from '@/config/app';
  23. // 生成全局唯一id
  24. function generateUUID() {
  25. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  26. let r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
  27. return v.toString(16);
  28. })
  29. }
  30. export default{
  31. props:{
  32. imageSrc:{
  33. type: String,
  34. },
  35. mode:{
  36. type: String,
  37. },
  38. loadingMode:{
  39. type: String,
  40. default:'looming-gray'
  41. },
  42. openTransition:{
  43. type: Boolean,
  44. default:true,
  45. },
  46. viewHeight:{
  47. type:Number,
  48. default() {
  49. return uni.getSystemInfoSync().windowHeight;
  50. }
  51. }
  52. },
  53. data(){
  54. const that = this;
  55. return {
  56. domain: HTTP_REQUEST_URL,
  57. uid: 'uid-' + generateUUID(),
  58. loadImg: false,
  59. showImg: false,
  60. isLoadError: false,
  61. borderLoaded: 0,
  62. showTransition: false,
  63. scrollFn: throttle(function() {
  64. // 加载img时才执行滚动监听判断是否可加载
  65. if (that.loadImg || that.isLoadError) return;
  66. const id = that.uid
  67. const query = uni.createSelectorQuery().in(that);
  68. query.select('#' + id).boundingClientRect(data => {
  69. if (!data) return;
  70. if (data.top - that.viewHeight < 0) {
  71. that.loadImg = !!that.imageSrc;
  72. that.isLoadError = !that.loadImg;
  73. }
  74. }).exec()
  75. }, 200)
  76. }
  77. },
  78. methods:{
  79. init() {
  80. this.$nextTick(this.onScroll)
  81. },
  82. handleBorderLoad(){
  83. this.borderLoaded = 1;
  84. },
  85. handleBorderError(){
  86. this.borderLoaded = 2;
  87. },
  88. handleImgLoad(e) {
  89. this.showImg = true;
  90. setTimeout(() => {
  91. this.showTransition = true
  92. }, 50)
  93. },
  94. handleImgError(e) {
  95. this.isLoadError = true;
  96. },
  97. onScroll() {
  98. this.scrollFn();
  99. },
  100. },
  101. mounted() {
  102. this.init()
  103. uni.$on('scroll', this.scrollFn);
  104. this.onScroll()
  105. },
  106. beforeDestroy() {
  107. uni.$off('scroll', this.scrollFn);
  108. }
  109. }
  110. </script>
  111. <style scoped>
  112. /* 官方优化图片tips */
  113. image{
  114. will-change: transform
  115. }
  116. /* 渐变过渡效果处理 */
  117. image.origin-img{
  118. width: 100%;
  119. height: 100%;
  120. opacity: 0.3;
  121. max-height: 360rpx;
  122. }
  123. image.origin-img.show-transition{
  124. transition: opacity .5s;
  125. opacity: 1;
  126. }
  127. image.origin-img.no-transition{
  128. opacity: 1;
  129. }
  130. /* 加载失败、加载中的占位图样式控制 */
  131. .loadfail-img{
  132. height: 100%;
  133. background-repeat: no-repeat;
  134. background-size: 50%;
  135. background-position: center;
  136. }
  137. .loading-img{
  138. height: 100%;
  139. background-position: center;
  140. }
  141. /* 转圈 */
  142. .spin-circle{
  143. background-repeat: no-repeat;
  144. background-size: 60%;
  145. }
  146. /* 动态灰色若隐若现 */
  147. .looming-gray{
  148. animation: looming-gray 1s infinite linear;
  149. background-color: #e3e3e3;
  150. }
  151. @keyframes looming-gray{
  152. 0% {background-color:#e3e3e3aa;}
  153. 50% {background-color:#e3e3e3;}
  154. 100% {background-color:#e3e3e3aa;}
  155. }
  156. /* 骨架屏1 */
  157. .skeleton-1{
  158. background-color: #e3e3e3;
  159. background-image: linear-gradient(100deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0) 80%);
  160. background-size: 100rpx 100%;
  161. background-repeat: repeat-y;
  162. background-position:0 0;
  163. animation: skeleton-1 .6s infinite;
  164. }
  165. @keyframes skeleton-1 {
  166. to {
  167. background-position: 200% 0;
  168. }
  169. }
  170. /* 骨架屏2 */
  171. .skeleton-2{
  172. background-image: linear-gradient(-90deg, #fefefe 0%, #e6e6e6 50%,#fefefe 100%);
  173. background-size: 400% 400%;
  174. background-position:0 0;
  175. animation: skeleton-2 1.2s ease-in-out infinite;
  176. }
  177. @keyframes skeleton-2{
  178. to {
  179. background-position: -135% 0;
  180. }
  181. }
  182. </style>