Reason
When the canvas is emptied or redrawn, the previously drawn content will disappear instantly and then be redrawn, resulting in a visual flicker.
Solution
Create two canvases, one for caching data and the other for display. First, before clearing and redrawing, cache the new data to be drawn on the canvas for caching, and finally draw the cached canvas on the canvas for display. This method is also called double buffering method.
Case
<!-- double buffering technology to solve the problem of canvas redrawing flickering --> <template> <div ref="container" class="container"> <!-- <div class="mask"> <span style="mix-blend-mode: multiply;">KOOND66</span> </div> --> <canvas id="myCanvas" :width="width" :height="height"></canvas> <canvas id="myCanvas1" :width="width" :height="height" style="opacity: 0;"></canvas> </div> </template> <script> export default { data() { return { animationFrameId: null, isAdmation: false, flag: true, width: null, height: null, list: [], timer: null } }, async mounted() { this.width = this.$refs.container.clientWidth this.height = this.$refs.container.clientHeight await this. computedCircle() window.addEventListener('resize', this.handleResize); }, beforeDestroy() { window.cancelAnimationFrame(this.animationFrameId); }, methods: { startAdmation() { this.requestAdmationStart() }, requestAdmationStart() { this. move() window.cancelAnimationFrame(this.animationFrameId); this.animationFrameId = window.requestAnimationFrame(this.requestAdmationStart) }, handleResize() { this.width = this.$refs.container.clientWidth this.height = this.$refs.container.clientHeight // this.initBall(true) let canvas = document. getElementById('myCanvas') let content = canvas. getContext('2d') let tempCanvas = document. getElementById('myCanvas1'); let tempCtx = tempCanvas. getContext('2d'); tempCtx. clearRect(0, 0, tempCtx. canvas. width, tempCtx. canvas. height) this.list.forEach(item => { tempCtx.beginPath() tempCtx.arc(item.x, item.y, item.r, 0, Math.PI * 2, false) tempCtx.fillStyle = item.color tempCtx. fill() }) const imageData = tempCtx.getImageData(0, 0, tempCtx.canvas.width, tempCtx.canvas.height); //content.clearRect(0, 0, content.canvas.width, content.canvas.height) content.putImageData(imageData, 0, 0); // render to the displayed canvas // this. startAdmation() }, computedCircle() { let canvas = document. getElementById('myCanvas') let content = canvas. getContext('2d') for(let i = 0; i < 10; i ++ ) { let newR = 50 * Math. random() + 10 this.list[i] = { r: newR, x: content.canvas.width * Math.random() <= newR + 200 ? content.canvas.width * Math.random() + newR + 200: content.canvas.width * Math.random() > content.canvas .width - newR ? content.canvas.width - newR : content.canvas.width * Math.random(), y: content.canvas.height * Math.random() <= newR + 200 ? content.canvas.height * Math.random() + newR + 200: content.canvas.height * Math.random() > content.canvas .height - newR ? content.canvas.height - newR : content.canvas.height * Math.random(), vx: 4 * Math. random()-2, vy: 4 * Math. random()-2, color: 'rgba(' + (Math.random() * 255).toFixed(0) + ',' + (Math.random() * 255).toFixed(0) + ',' + (Math.random() * 255).toFixed(0) + ',' + (0.5 + 0.5*Math. random()). toFixed(1) + ')' } } this.initBall(true) }, initBall(flag) { let canvas = document. getElementById('myCanvas') let content = canvas. getContext('2d') content. clearRect(0, 0, content. canvas. width, content. canvas. height) this.list.forEach(item => { content.beginPath() content.arc(item.x, item.y, item.r, 0, Math.PI * 2, false) content.fillStyle = item.color content. fill() }) if(flag) { this. startAdmation() } }, move() { let canvas = document. getElementById('myCanvas') let content = canvas. getContext('2d') this.list.forEach(item => { if(item.x + item.vx > content.canvas.width - item.r || item.x + item.vx < 0 + item.r) { item.vx = -item.vx } if(item.y + item.vy > content.canvas.height - item.r || item.y + item.vy < 0 + item.r) { item.vy = -item.vy } item.x += item.vx item.y += item.vy }) let tempCanvas = document. getElementById('myCanvas1'); let tempCtx = tempCanvas. getContext('2d'); tempCtx. clearRect(0, 0, tempCtx. canvas. width, tempCtx. canvas. height) this.list.forEach(item => { tempCtx.beginPath() tempCtx.arc(item.x, item.y, item.r, 0, Math.PI * 2, false) tempCtx.fillStyle = item.color tempCtx. fill() }) const imageData = tempCtx.getImageData(0, 0, tempCtx.canvas.width, tempCtx.canvas.height); // cache the latest data first content. clearRect(0, 0, content. canvas. width, content. canvas. height) content.putImageData(imageData, 0, 0); } } } </script> <style lang="less" scoped> .container { width: 80%; height: 80%; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); } canvas { width: 100%; height: 100%; border: 1px solid #eee; } .mask { width: 100%; height: 100%; position: absolute; background-color: black; left: 0; top: 0; font-size: 100px; font-weight: 700; // color: aliceblue; display: flex; justify-content: center; align-items: center; } #myCanvas1 { position: absolute; left: -9999999999999999px; top: -99999999999999px; } </style>