继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

用React和Canvas打造互动动画

aluckdog
关注TA
已关注
手记 498
粉丝 68
获赞 394

动画可以让您的网页应用更加生动,结合 HTML Canvas 和 React 的力量可以带来许多可能性。在这篇文章中,我们将探讨如何在 React 中使用 Canvas 创建互动动画。我们还将深入了解 requestAnimationFrame API,以实现流畅的动画效果。让我们构建两个互动示例:一个跟随鼠标移动的圆形和一个点击引发的粒子爆炸效果。点击引发的粒子爆炸效果🎨✨

在 React 中设置画布

要在 React 应用中使用 Canvas,你可以使用 useRef 钩子来直接访问 <canvas> 元素及其绘图上下文。这里是如何在 React 中设置一个基本的 Canvas 组件:

// 这里是设置基本 Canvas 组件的方法
    import React, { useRef, useEffect } from 'react';  

    const Canvas = ({ draw, ...rest }) => {  
      const canvasRef = useRef(null);  

      useEffect(() => {  
        const canvas = canvasRef.current;  
        const context = canvas.getContext('2d');  
        let animationFrameId;  

        const render = () => {  
          draw(context);  
          animationFrameId = requestAnimationFrame(render);  
        };  
        render();  

        return () => cancelAnimationFrame(animationFrameId);  
      }, [draw]);  

      return <canvas ref={canvasRef} {...rest} />;  
    };  

    export default Canvas;

这是一个可重用的Canvas组件,它接收一个draw函数作为属性,允许你定义自定义动画效果。

示例1:光标跟随的圆:

让我们创建一个动画,当鼠标在画布上移动时,圆圈会跟随鼠标。

import React, { useState } from 'react';  
import Canvas from './Canvas';  

const CircleFollow = () => {  
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 });  

  const handleMouseMove = (event) => {  
    const rect = event.target.getBoundingClientRect();  
    setMousePos({  
      x: event.clientX - rect.left,  
      y: event.clientY - rect.top,  
    });  
  };  

  const draw = (ctx) => {  
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);  
    ctx.fillStyle = 'blue';  
    ctx.beginPath();  
    ctx.arc(mousePos.x, mousePos.y, 20, 0, Math.PI * 2);  
    ctx.fill();  
  };  

  return (  
    <Canvas  
      draw={draw}  
      width={800}  
      height={400}  
      onMouseMove={handleMouseMove}  
      style={{ border: '1px solid black' }}  
    />  
  );  
};  

export default CircleFollow;
它是怎么工作的
  • handleMouseMove 函数计算鼠标相对于画布的当前位置。
  • draw 函数在每一帧都清除画布,并在当前鼠标位置画圆。

(i) 示例2:点击触发粒子爆炸

接下来我们来搞一个鼠标点击就能触发的粒子爆炸效果。 🧨💥

import React, { useState, useRef, useEffect } from 'react';  
import Canvas from './Canvas';  

const ParticleExplosion = () => {  
  const [particles, setParticles] = useState([]);  
  const particlesRef = useRef([]);  

  useEffect(() => {  
    particlesRef.current = particles;  
  }, [particles]);  

  const handleClick = (event) => {  
    // 获取点击位置的坐标
    const rect = event.target.getBoundingClientRect();  
    const x = event.clientX - rect.left;  
    const y = event.clientY - rect.top;  

    // 生成新的粒子
    const newParticles = Array.from({ length: 50 }, () => ({  
      x,  
      y,  
      dx: (Math.random() - 0.5) * 10,  
      dy: (Math.random() - 0.5) * 10,  
      size: Math.random() * 5 + 2,  
      color: `hsl(${Math.random() * 360}, 100%, 50%)`,  
    }));  

    setParticles((prev) => [...prev, ...newParticles]);  
  };  

  const draw = (ctx) => {  
    // 清除画布上的所有内容
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);  
    // 更新粒子的位置、大小和其他属性
    const updatedParticles = particlesRef.current  
      .map((p) => ({  
        ...p,  
        x: p.x + p.dx,  
        y: p.y + p.dy,  
        size: p.size * 0.95,  
      }))  
      // 过滤掉大小小于或等于0.5的粒子
      .filter((p) => p.size > 0.5);  

    // 遍历每个粒子并绘制
    updatedParticles.forEach((p) => {  
      ctx.fillStyle = p.color;  
      ctx.beginPath();  
      ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);  
      ctx.fill();  
    });  

    particlesRef.current = updatedParticles;  
    setParticles(updatedParticles);  
  };  

  return (  
    <Canvas  
      draw={draw}  
      width={800}  
      height={400}  
      onClick={handleClick}  
      style={{ border: '1px solid black', background: 'black' }}  
    />  
  );  
};  

export default ParticleExplosion;

如何用 Canvas 制作自定义绘图应用

绘图应用很适合边玩边学Canvas。接下来,我们将介绍如何制作一个可以选颜色、调画笔大小并保存成果的绘图工具。🖌️🎨

import React, { useRef, useState } from 'react';

const DrawingApp = () => {
  const canvasRef = useRef(null);
  const [isDrawing, setIsDrawing] = useState(false);
  const [color, setColor] = useState('#000000');
  const [brushSize, setBrushSize] = useState(5);

  const startDrawing = (e) => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    ctx.strokeStyle = color;
    ctx.lineWidth = brushSize;
    ctx.lineCap = 'round';
    ctx.beginPath().moveTo(e.nativeEvent.offsetX, e.nativeEvent.offsetY);
    setIsDrawing(true);
  };

  const draw = (e) => {
    if (!isDrawing) return;
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    ctx.lineTo(e.nativeEvent.offsetX, e.nativeEvent.offsetY);
    ctx.stroke();
  };

  const stopDrawing = () => {
    setIsDrawing(false);
  };

  const saveDrawing = () => {
    const canvas = canvasRef.current;
    const dataURL = canvas.toDataURL('image/png');
    const link = document.createElement('a');
    link.href = dataURL;
    link.download = 'drawing.png';
    link.title = "点击保存画作";
    link.click();
  };

  return (
    <div>
      <div style={{ marginBottom: '10px' }}>
        <label>
          颜色选择: <input type="color" value={color} onChange={(e) => setColor(e.target.value)} />
        </label>
        <label>
          笔刷粗细: <input type="number" value={brushSize} min="1" max="50" onChange={(e) => setBrushSize(e.target.value)} />
        </label>
        <button onClick={saveDrawing}>保存图</button>
      </div>
      <canvas
        ref={canvasRef}
        width={800}
        height={400}
        style={{ border: '1px solid black' }}
        onMouseDown={startDrawing}
        onMouseMove={draw}
        onMouseUp={stopDrawing}
        onMouseLeave={stopDrawing}
        title="画布宽度800像素,高度400像素"
      ></canvas>
    </div>
  );
};

export default DrawingApp;
让我们一起探索 requestAnimationFrame 的秘密吧!

发现 requestAnimationFrame 的妙处

requestAnimationFrame 方法对于浏览器中的流畅动画至关重要。它让浏览器知道你想执行动画,并请求在下次重绘前调用指定的函数。此方法比 setInterval 等替代方案更高效,因为它能与浏览器的刷新率同步,从而实现更流畅的视觉效果,同时减少 CPU 使用。

    const 动态 = () => {  
      // 更新动画逻辑在这里  
      console.log('正在执行动画...');  
      requestAnimationFrame( 动态 );  
    };  

    requestAnimationFrame( 动态 );

当你使用 requestAnimationFrame 与 Canvas 结合时,可以确保动画流畅并适应用户的设备性能,保持一致的流畅性能。

所以说

将 Canvas 和 React 结合使用提供了一种强大方式来创建互动性强且视觉效果惊艳的网页应用。不论是制作动画、粒子效果,还是自定义绘图工具,掌握如何在 React 中运用 Canvas 是一项非常有用的技能。

尝试使用更多的Canvas API,结合其他React功能,甚至可以探索类似react-konva这样的库来实现更高级的功能,让你的创造力更上一层楼。祝你编程愉快!🚀🎨

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP