muqian-lazyLoad.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. <template>
  2. <view class="muqian-content" :style="{
  3. width,
  4. height,
  5. }">
  6. <!-- 加载成功 -->
  7. <image class="muqian-image" @load="load" @error="error" v-if="status==1" :src="src" mode="scaleToFill" :style="{
  8. opacity:isShow?'1':'0',
  9. borderRadius,
  10. width,
  11. height,
  12. transition: `opacity ${duration/1000}s ${effect}`
  13. }">
  14. </image>
  15. <!-- 加载中 -->
  16. <image :src="loadSrc" class="muqian-image muqain-load" @load="init" mode="scaleToFill" :style="{
  17. opacity:isShow?'0':'1',
  18. borderRadius,
  19. width,
  20. height,
  21. transition: `opacity ${duration/1000}s ${effect}`
  22. }"></image>
  23. <!-- 加载失败 -->
  24. <image class="muqian-image" v-if="status==2" :src="errorSrc" mode="scaleToFill" :style="{
  25. opacity:isShow?'1':'0',
  26. borderRadius,
  27. width,
  28. height,
  29. transition: `opacity ${duration/1000}s ${effect}`
  30. }">
  31. </image>
  32. </view>
  33. </template>
  34. <script>
  35. let loadTimer = null
  36. import loadingImage from '../../static/loading.gif'
  37. import loadFailImage from '../../static/loadFail.png'
  38. /**
  39. * 懒加载插件
  40. * @description 懒加载插件
  41. *
  42. * @property {String} borderRadius 图片圆角,必须带尺寸单位
  43. * @property {String} width 图片宽度,必须带尺寸单位(默认100%)
  44. * @property {String} height 图片高度,必须带尺寸单位(默认100%)
  45. * @property {String} src 图片链接,不传的话会一直是加载中状态
  46. * @property {String|Number} minTimeOut 当图片加载过快(存在缓存)至少显示多少秒加载动画
  47. * @property {String} effect = [linear|ease|ease-in|ease-out|ease-in-out] 过渡效果,可以用cubic-bezier
  48. * @value linear 规定以相同速度开始至结束的过渡效果(默认)
  49. * @value ease 规定慢速开始,然后变快,然后慢速结束的过渡效果
  50. * @value ease-in 规定以慢速开始的过渡效果
  51. * @value ease-out 规定以慢速结束的过渡效果
  52. * @value ease-in-out 规定以慢速开始和结束的过渡效果
  53. * @property {String|Number} duration 图片加载成功后的过渡时间,单位毫秒
  54. * @property {Object} showDistance 当图片到屏幕哪个位置的时候开始加载,单位px,可以是负数 (默认{bottom:0})
  55. * @property {String} loadSrc 加载中显示的图片,输入网络路径或绝对路径
  56. * @property {String} errorSrc 加载失败显示的图片,输入网络路径或绝对路径
  57. * @event {Function} show 当图片进入页面触发
  58. * @event {Function} showSuccess 当图片完全加载完毕触发
  59. * @example <muqian-lazyLoad :src="src" width="100rpx" height="100rpx"></muqian-lazyLoad>
  60. */
  61. export default {
  62. name: "muqian-lazyLoad",
  63. props: {
  64. //图片圆角 必须带尺寸单位
  65. borderRadius: {
  66. type: String,
  67. default: '0'
  68. },
  69. //图片宽度
  70. width: {
  71. type: String,
  72. default: '100%'
  73. },
  74. height: {
  75. type: String,
  76. default: '100%'
  77. },
  78. //图片链接
  79. src: {
  80. type: String,
  81. default: ''
  82. },
  83. //当图片加载过快(存在缓存)至少显示多少秒加载动画
  84. minTimeOut: {
  85. type: String || Number,
  86. default: '300'
  87. },
  88. //当图片到屏幕哪个位置的时候开始加载 单位px 可以是负数
  89. showDistance: {
  90. type: Object,
  91. default: () => {
  92. bottom: 20
  93. }
  94. },
  95. //过渡效果 linear / ease / ease-in / ease-out / ease-in-out
  96. effect: {
  97. type: String,
  98. default: 'linear'
  99. },
  100. //图片加载成功后的过渡时间 单位毫秒
  101. duration: {
  102. type: String || Number,
  103. default: '300'
  104. },
  105. //加载中图片
  106. loadSrc: {
  107. type: String,
  108. default: loadingImage
  109. },
  110. //加载失败图片
  111. errorSrc: {
  112. type: String,
  113. default:loadFailImage
  114. },
  115. },
  116. data() {
  117. return {
  118. status: 0, //0加载中 1加载成功 2加载失败
  119. isShow: false
  120. }
  121. },
  122. watch: {
  123. //当链接变化重新加载
  124. src() {
  125. if (!this.isShow) return
  126. this.status = 0
  127. this.isShow = false
  128. this.$nextTick(() => {
  129. this.status = 1
  130. })
  131. }
  132. },
  133. destroyed() {
  134. //页面销毁取消监听
  135. this.$emit('destroyed')
  136. },
  137. methods: {
  138. load() {
  139. if (this.minTimeOut == 0) {
  140. this.isShow = true
  141. }else{
  142. let newTimer = new Date().getTime() - loadTimer
  143. if (newTimer < this.minTimeOut) {
  144. setTimeout(() => {
  145. this.isShow = true
  146. }, this.minTimeOut - newTimer)
  147. } else {
  148. this.isShow = true
  149. }
  150. }
  151. setTimeout(()=>{
  152. this.$emit('showSuccess');
  153. },this.duration)
  154. this.$emit("imageLoadHandle",this.index)
  155. },
  156. error() {
  157. this.status = 2
  158. this.isShow = true
  159. this.$emit("imageLoadHandle",this.index)
  160. },
  161. init(){
  162. let intersectionObserver = uni.createIntersectionObserver(this)
  163. let load = false
  164. //当图片加载完的时候取消监听
  165. this.$once('destroyed', () => {
  166. intersectionObserver.disconnect()
  167. })
  168. intersectionObserver.relativeToViewport(this.showDistance).observe('.muqain-load', (res) => {
  169. if (!load && res.intersectionRatio == 0) {
  170. load = true
  171. return
  172. }
  173. this.$emit('show');
  174. load = true
  175. this.status = 1
  176. loadTimer = new Date().getTime()
  177. intersectionObserver.disconnect()
  178. })
  179. }
  180. }
  181. }
  182. </script>
  183. <style lang="scss" scoped>
  184. .muqian-content {
  185. overflow: hidden;
  186. position: relative;
  187. .muqian-image {
  188. display: block;
  189. will-change: transform;
  190. }
  191. .muqain-load {
  192. position: absolute;
  193. left: 0;
  194. top: 0;
  195. }
  196. }
  197. </style>