优化画布画圆

Cha*_*wis 7 javascript html5-canvas reactjs

我是 HTML5 画布的新手,希望让一些圆圈沿随机方向移动,以在我的网站上获得奇特的效果。

我注意到当这些圆圈移动时,CPU 使用率非常高。当只有几个圆圈在移动时,通常是可以的,但是当大约有 5 个或更多圆圈时,它就会开始出现问题。

这是在 Safari 中用 5 个圆圈分析几秒钟的屏幕截图。

配置文件结果

这是我迄今为止为我的 Circle 组件编写的代码:

export default function Circle({ color = null }) {
  useEffect(() => {
    if (!color) return

    let requestId = null
    let canvas = ref.current
    let context = canvas.getContext("2d")

    let ratio = getPixelRatio(context)
    let canvasWidth = getComputedStyle(canvas).getPropertyValue("width").slice(0, -2)
    let canvasHeight = getComputedStyle(canvas).getPropertyValue("height").slice(0, -2)

    canvas.width = canvasWidth * ratio
    canvas.height = canvasHeight * ratio
    canvas.style.width = "100%"
    canvas.style.height = "100%"

    let y = random(0, canvas.height)
    let x = random(0, canvas.width)
    const height = random(100, canvas.height * 0.6)

    let directionX = random(0, 1) === 0 ? "left" : "right"
    let directionY = random(0, 1) === 0 ? "up" : "down"

    const speedX = 0.1
    const speedY = 0.1

    context.fillStyle = color

    const render = () => {
      //draw circle
      context.clearRect(0, 0, canvas.width, canvas.height)
      context.beginPath()
      context.arc(x, y, height, 0, 2 * Math.PI)

      //prevent circle from going outside of boundary
      if (x < 0) directionX = "right"
      if (x > canvas.width) directionX = "left"
      if (y < 0) directionY = "down"
      if (y > canvas.height) directionY = "up"

      //move circle
      if (directionX === "left") x -= speedX
      else x += speedX
      if (directionY === "up") y -= speedY
      else y += speedY

      //apply color
      context.fill()

      //animate
      requestId = requestAnimationFrame(render)
    }

    render()

    return () => {
      cancelAnimationFrame(requestId)
    }
  }, [color])

  let ref = useRef()
  return <canvas ref={ref} />
}
Run Code Online (Sandbox Code Playgroud)

有没有更高效的方法来使用画布绘制和移动圆圈?

当它们不动时,CPU 使用率从大约 3% 开始,然后下降到不到 1%,当我从 DOM 中删除圆圈时,CPU 使用率始终低于 1%。

我知道用 CSS 做这些类型的动画通常会更好(因为我相信它使用 GPU 而不是 CPU),但我不知道如何使用过渡 CSS 属性让它工作。我只能让规模转换工作。

当屏幕上有许多圆圈在移动时,我的奇特效果看起来很“酷”,因此寻找一种更高效的方式来绘制和移动圆圈。

这是演示的沙箱:https : //codesandbox.io/s/async-meadow-vx822(在 chrome 或 safari 中查看以获得最佳效果)

cer*_*ner 6

这是一种稍微不同的方法,可以将圆形和背景组合在一起,以便只有一个画布元素来改进渲染的 dom。

该组件使用与您的随机化逻辑相同的颜色和大小,但circles在渲染任何内容之前将所有初始值存储在一个数组中。render函数将背景颜色和所有圆圈一起呈现,并计算它们在每个循环中的移动。

export default function Circles() {
  useEffect(() => {
    const colorList = {
      1: ["#247ba0", "#70c1b3", "#b2dbbf", "#f3ffbd", "#ff1654"],
      2: ["#05668d", "#028090", "#00a896", "#02c39a", "#f0f3bd"]
    };
    const colors = colorList[random(1, Object.keys(colorList).length)];
    const primary = colors[random(0, colors.length - 1)];
    const circles = [];

    let requestId = null;
    let canvas = ref.current;
    let context = canvas.getContext("2d");

    let ratio = getPixelRatio(context);
    let canvasWidth = getComputedStyle(canvas)
      .getPropertyValue("width")
      .slice(0, -2);
    let canvasHeight = getComputedStyle(canvas)
      .getPropertyValue("height")
      .slice(0, -2);

    canvas.width = canvasWidth * ratio;
    canvas.height = canvasHeight * ratio;
    canvas.style.width = "100%";
    canvas.style.height = "100%";

    [...colors, ...colors].forEach(color => {
      let y = random(0, canvas.height);
      let x = random(0, canvas.width);
      const height = random(100, canvas.height * 0.6);

      let directionX = random(0, 1) === 0 ? "left" : "right";
      let directionY = random(0, 1) === 0 ? "up" : "down";

      circles.push({
        color: color,
        y: y,
        x: x,
        height: height,
        directionX: directionX,
        directionY: directionY
      });
    });

    const render = () => {
      context.fillStyle = primary;
      context.fillRect(0, 0, canvas.width, canvas.height);

      circles.forEach(c => {
        const speedX = 0.1;
        const speedY = 0.1;

        context.fillStyle = c.color;
        context.beginPath();
        context.arc(c.x, c.y, c.height, 0, 2 * Math.PI);
        if (c.x < 0) c.directionX = "right";
        if (c.x > canvas.width) c.directionX = "left";
        if (c.y < 0) c.directionY = "down";
        if (c.y > canvas.height) c.directionY = "up";
        if (c.directionX === "left") c.x -= speedX;
        else c.x += speedX;
        if (c.directionY === "up") c.y -= speedY;
        else c.y += speedY;
        context.fill();
        context.closePath();
      });

      requestId = requestAnimationFrame(render);
    };

    render();

    return () => {
      cancelAnimationFrame(requestId);
    };
  });

  let ref = useRef();
  return <canvas ref={ref} />;
}
Run Code Online (Sandbox Code Playgroud)

您可以简单地用应用程序组件中的这个组件替换所有圆形元素和背景样式。

export default function App() {
  return (
    <>
      <div className="absolute inset-0 overflow-hidden">
          <Circles />
      </div>
      <div className="backdrop-filter-blur-90 absolute inset-0 bg-gray-900-opacity-20" />
    </>
  );
}
Run Code Online (Sandbox Code Playgroud)