滚动条控制播放的canvas逐帧动画
业务需求滚动条滚动播放视频内容,最后想到用canvas逐帧动画来处理。记录下最终效果。
1 <template> 2 <div class="home"> 3 <div class="canvas"> 4 <canvas ref="canvas" :width="canvasWidth" :height="canvasHeight"></canvas> 5 </div> 6 </div> 7 </template> 8 9 <script> 10 import { debounce, throttle } from '@/utils' 11 12 export default { 13 name: 'App', 14 data() { 15 return { 16 $_resizeCanvas: null, 17 ctx: null, 18 canvasWidth: 0, 19 canvasHeight: 0, 20 sources: [], 21 count: 0, 22 imgIndex: 0, 23 top: 0, 24 flag: false, 25 } 26 }, 27 28 created() { 29 this.loadImages() 30 }, 31 mounted() { 32 const _this = this 33 if (!_this.$_resizeCanvas) { 34 _this.initListener() 35 } 36 _this.$nextTick(() => { 37 _this.ctx = _this.$refs.canvas.getContext('2d') 38 }) 39 }, 40 beforeDestroy() { 41 this.$_resizeCanvas = null 42 window.removeEventListener('resize', this.$_resizeHandler) 43 window.removeEventListener('scroll', this.scrollToTop) 44 }, 45 methods: { 46 initListener() { 47 this.setCanvasDom() 48 this.$_resizeCanvas = debounce(this.setCanvasDom, 200) 49 window.addEventListener('resize', this.$_resizeCanvas) 50 51 window.addEventListener('scroll', this.scrollToTop) 52 }, 53 setCanvasDom() { 54 console.log(this.$refs.canvas) 55 const w = document.body.clientWidth 56 const h = document.body.clientHeight 57 if (w > h * 1.77) { 58 this.canvasWidth = w 59 this.canvasHeight = Math.floor(w / 1.77) 60 } else { 61 this.canvasWidth = Math.floor(h * 1.77) 62 this.canvasHeight = h 63 } 64 }, 65 loadImages() { 66 const images = [] 67 for (let i = 0; i <= 209; i++) { 68 images[i] = new Image() 69 let n = i < 100 ? (i < 10 ? `00` : `0`) : `` 70 images[i].src = `/img/${n}${i}.webp` 71 images[i].onload = () => { 72 this.count++ 73 if (i === 0) { 74 this.renderImg() 75 } 76 } 77 this.sources[i] = images[i] 78 } 79 }, 80 renderImg() { 81 const _self = this 82 if (_self.sources[_self.imgIndex]) { 83 _self.ctx.clearRect(0, 0, _self.canvasWidth, _self.canvasHeight) 84 _self.ctx.drawImage(_self.sources[_self.imgIndex], 0, 0, _self.canvasWidth, _self.canvasHeight) 85 } 86 }, 87 scrollToTop() { 88 const _this = this 89 if (_this.flag) { 90 return 91 } 92 requestAnimationFrame(() => { 93 _this.top = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop 94 const topNum = Math.round(_this.top / 25) 95 let n = topNum - _this.imgIndex 96 if (n > 0) { 97 // 向下滚动 98 for (let i = 0; i < n; i++) { 99 _this.imgIndex++ 100 _this.renderImg() 101 } 102 } else { 103 for (let i = 0; i < -n; i++) { 104 _this.imgIndex-- 105 _this.renderImg() 106 } 107 } 108 _this.flag = false 109 }) 110 _this.flag = true 111 }, 112 }, 113 } 114 </script> 115 116 <style lang="scss" scoped> 117 .home { 118 height: 15000px; 119 background: #000; 120 } 121 122 .canvas { 123 width: 100%; 124 height: 100vh; 125 min-width: 1240px; 126 overflow: hidden; 127 position: sticky; 128 top: 0px; 129 left: 0px; 130 display: flex; 131 align-items: flex-end; 132 canvas { 133 width: 100%; 134 overflow: hidden; 135 } 136 } 137 </style> 138