为何JS实现的水彩效果仅在第一个canvas元素生效?
多个.canvas元素水彩效果失效问题修复
问题分析
原代码通过document.querySelector('.see-more')仅获取第一个canvas元素,所以只有第一个能生效;换成querySelectorAll后失效,是因为整个逻辑都是针对单个canvas写的,全局变量(比如cx、watercolors、frameId)会被覆盖,且没有遍历处理每个canvas实例。
修复方案
将水彩效果逻辑封装为独立函数,遍历所有.see-more canvas,为每个元素单独初始化上下文、渐变、动画循环,确保每个canvas拥有独立的状态:
修改后的完整JS代码:
//watercolor effect on see more// function initWatercolor(canvas) { const cx = canvas.getContext('2d'); cx.globalCompositeOperation = 'source-over'; cx.imageSmoothingEnabled = true; cx.imageSmoothingQuality = 'low'; const palette = [ [171, 204, 177], // mint [221, 160, 221], // plum [255, 192, 203], // pink [0, 255, 255], // neon blue [255, 255, 0] // yellow ]; // 为当前canvas创建专属渐变 const gradients = palette.map(([r, g, b]) => { const gradient = cx.createRadialGradient(0, 0, 1, 0, 0, 50); gradient.addColorStop(0, `rgba(${r},${g},${b},1)`); gradient.addColorStop(1, `rgba(${r},${g},${b},0)`); return gradient; }); const watercolors = []; class Watercolor { constructor(time) { const colorIndex = Math.floor(Math.random() * palette.length); this.color = gradients[colorIndex]; this.radius = 50; this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.dx = Math.random() * 2 - 1; this.dy = Math.random() * 2 - 1; this.startTime = time; this.currentTime = time; this.duration = Math.ceil(Math.random() * 5000); } update(time) { this.currentTime = time; if (this.currentTime - this.startTime >= this.duration) { return false; } this.x += this.dx; this.y += this.dy; return true; } render(cx) { cx.save(); cx.globalAlpha = (this.currentTime - this.startTime) / this.duration; cx.translate(this.x, this.y); cx.beginPath(); cx.arc(0, 0, this.radius, 0, Math.PI * 2); cx.fillStyle = this.color; cx.fill(); cx.restore(); } } let frameId; function frame(time) { const multiplier = 0.25; const expectedWidth = Math.floor(canvas.clientWidth * multiplier); if (canvas.width !== expectedWidth) { canvas.width = expectedWidth; } const expectedHeight = Math.floor(canvas.clientHeight * multiplier); if (canvas.height !== expectedHeight) { canvas.height = expectedHeight; } if (watercolors.length < 2) { watercolors.push(new Watercolor(time)); } for (let index = watercolors.length - 1; index >= 0; index--) { const watercolor = watercolors[index]; if (!watercolor.update(time)) { watercolors.splice(index, 1); } } for (const watercolor of watercolors) { watercolor.render(cx); } frameId = window.requestAnimationFrame(frame); } function start() { frameId = window.requestAnimationFrame(frame); } start(); } // 遍历所有.see-more canvas并初始化效果 const canvases = document.querySelectorAll('.see-more'); canvases.forEach(initWatercolor);
关键修改点
- 新增
initWatercolor函数,将单个canvas的效果逻辑封装其中,每个canvas拥有独立的上下文、渐变数组、水彩实例和动画循环 - 使用
document.querySelectorAll('.see-more')获取所有目标canvas,通过forEach遍历初始化 - 移除全局的
canvas、cx、watercolors等变量,避免多canvas之间的状态冲突 - 保留原有的水彩效果逻辑,确保效果一致性
保留原有CSS和HTML
你的CSS和HTML代码无需修改,直接使用即可。
内容的提问来源于stack exchange,提问作者feliel93
相关产品推荐
相关产品推荐

