You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 06:09:54