动画可以让您的网页应用更加生动,结合 HTML Canvas 和 React 的力量可以带来许多可能性。在这篇文章中,我们将探讨如何在 React 中使用 Canvas 创建互动动画。我们还将深入了解 requestAnimationFrame
API,以实现流畅的动画效果。让我们构建两个互动示例:一个跟随鼠标移动的圆形和一个点击引发的粒子爆炸效果。点击引发的粒子爆炸效果🎨✨
要在 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
函数作为属性,允许你定义自定义动画效果。
让我们创建一个动画,当鼠标在画布上移动时,圆圈会跟随鼠标。
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
函数在每一帧都清除画布,并在当前鼠标位置画圆。
接下来我们来搞一个鼠标点击就能触发的粒子爆炸效果。 🧨💥
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。接下来,我们将介绍如何制作一个可以选颜色、调画笔大小并保存成果的绘图工具。🖌️🎨
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
这样的库来实现更高级的功能,让你的创造力更上一层楼。祝你编程愉快!🚀🎨
共同学习,写下你的评论
评论加载中...
作者其他优质文章