Actualmente estoy tratando de convertir un elemento de lienzo que tiene una animación en bucle en un proyecto React como componente, y estoy luchando por encontrar la mejor manera de adaptarlo. Principalmente he estado usando este artículo como guía ( https://medium.com/@pdx.lucasm/canvas-with-react-js-32e133c05258 ) y parece que useRef() podría ser una buena herramienta aquí, pero Estoy luchando por descubrir cómo estructurar el componente.
Este es el código que estoy tratando de convertir:
var ctx = document.querySelector("canvas").getContext("2d"),
dashLen = 220, dashOffset = dashLen, speed = 5,
txt = "STROKE-ON CANVAS", x = 30, i = 0;
ctx.font = "50px Comic Sans MS, cursive, TSCu_Comic, sans-serif";
ctx.lineWidth = 5; ctx.lineJoin = "round"; ctx.globalAlpha = 2/3;
ctx.strokeStyle = ctx.fillStyle = "#1f2f90";
(function loop() {
ctx.clearRect(x, 0, 60, 150);
ctx.setLineDash([dashLen - dashOffset, dashOffset - speed]); // create a long dash mask
dashOffset -= speed; // reduce dash length
ctx.strokeText(txt[i], x, 90); // stroke letter
if (dashOffset > 0) requestAnimationFrame(loop); // animate
else {
ctx.fillText(txt[i], x, 90); // fill final letter
dashOffset = dashLen; // prep next char
x += ctx.measureText(txt[i++]).width + ctx.lineWidth * Math.random();
ctx.setTransform(1, 0, 0, 1, 0, 3 * Math.random()); // random y-delta
ctx.rotate(Math.random() * 0.005); // random rotation
if (i < txt.length) requestAnimationFrame(loop);
}
})();
<canvas width=630></canvas>
Realmente aprecio cualquier consejo
Ciertamente hay muchas maneras en las que podrías hacer esto. Yo personalmente, no usaría ref. Ref solo es útil para funciones que ocurren como reacción a algo (como hacer clic en un botón, cambiar el valor de un campo).
Dado que necesitamos el contexto y queremos configurar un bucle de renderizado, queremos saber cuándo se ha creado el lienzo. Para esto, usaremos useState y pasaremos el setter a la propiedad ref del lienzo. El bucle de animación se configurará dentro de un efecto. Necesitamos realizar un seguimiento de si el componente aún está montado o no, por lo que usamos una variable local en el efecto para realizar un seguimiento de esto (y si no estamos montados, cortocircuitamos el cuadro de animación).
function CanvasComponent() {
const [canvas, setCanvas] = React.useState();
React.useEffect(() => {
// On the first render, canvas won't be set. However, once setCanvas is called by ref, this effect will run again.
if (canvas == null) return;
const ctx = canvas.getContext("2d");
const dashLen = 220;
let dashOffset = dashLen;
const speed = 5;
const txt = "STROKE-ON CANVAS";
let x = 30;
let i = 0;
ctx.font = "50px Comic Sans MS, cursive, TSCu_Comic, sans-serif";
ctx.lineWidth = 5;
ctx.lineJoin = "round";
ctx.globalAlpha = 2 / 3;
ctx.strokeStyle = ctx.fillStyle = "#1f2f90";
// Keep track of wether the component is still mounted
let mounted = true;
const loop = () => {
if (!mounted) return;
ctx.clearRect(x, 0, 60, 150);
ctx.setLineDash([dashLen - dashOffset, dashOffset - speed]); // create a long dash mask
dashOffset -= speed; // reduce dash length
ctx.strokeText(txt[i], x, 90); // stroke letter
if (dashOffset > 0) requestAnimationFrame(loop); // animate
else {
ctx.fillText(txt[i], x, 90); // fill final letter
dashOffset = dashLen; // prep next char
x += ctx.measureText(txt[i++]).width + ctx.lineWidth * Math.random();
ctx.setTransform(1, 0, 0, 1, 0, 3 * Math.random()); // random y-delta
ctx.rotate(Math.random() * 0.005); // random rotation
if (i < txt.length) requestAnimationFrame(loop);
}
};
requestAnimationFrame(loop);
// React will call this function when the effects' dependencies change, or the component is unmounted
return () => {
// Set mounted to false so that the next frame is short-circuited.
mounted = false;
};
}, [canvas]);
return <canvas width="630" ref={setCanvas}></canvas>;
}
Espero que esto ayude.
PD
No sé si es intencional o no, pero en su código i nunca se actualiza.