easy-loadimage.vue 4.6 KB

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