Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animating in React #137

Open
rrriki opened this issue Jan 5, 2022 · 4 comments
Open

Animating in React #137

rrriki opened this issue Jan 5, 2022 · 4 comments

Comments

@rrriki
Copy link

rrriki commented Jan 5, 2022

Hi @mattdesl,

I'm trying to create an animated sketch using React. But I'm not really sure how I can accomplish that with the way I've set up things.

I have my own Canvas component which wraps your canvasSketch, basically based off the example for React in the Docs.

import React, { useEffect, useRef } from "react";
import styled from "styled-components";
// @ts-ignore
import canvasSketch from "canvas-sketch";

export type CanvasDrawingProps = {
  context: CanvasRenderingContext2D;
  width: number;
  height: number;
};

const StyledCanvas = styled.canvas`
  background-color: #e1e3df;
  &:hover {
    filter: hue-rotate(90deg);
  }
`;

const WIDTH = 400;
const HEIGHT = 400;

interface CanvasProps {
  draw: (props: CanvasDrawingProps) => void;
  animate?: boolean;
}

export const Canvas: React.FC<CanvasProps> = (props) => {
  const { draw, animate } = props;
  const ref = useRef<HTMLCanvasElement | null>(null);

  useEffect(() => {
    console.log("running canvas useEffect");

    canvasSketch(draw, {
      dimensions: [WIDTH, HEIGHT],
      units: "px",
      resizeCanvas: false,
      styleCanvas: true,
      scaleToView: false,
      canvas: ref.current,
      animate,
    });

    return function cleanUp() {
      console.log("Canvas unmounting");
    };
  }, [draw, animate]);

  return (
    <StyledCanvas
      id="canvas"
      ref={ref}
      {...props}
      width={WIDTH}
      height={HEIGHT}
    />
  );
};

And I'm using it in another component which passes the draw method I want to be call per frame.

import React from "react";
import { Canvas, CanvasDrawingProps } from "../components";
import { Agent, randomHexColor, randomInRange } from "../utils";

export const Agents: React.FC = () => {
  const drawAgents = ({ context, width, height }: CanvasDrawingProps) => {
    const agentsCount = 40;

    for (let i = 0; i <= agentsCount; i++) {
      const radius = randomInRange(1, 13);
      const agent = new Agent(0, 0, radius);

      agent.setStrokeColor(randomHexColor());
      agent.setFillColor(randomHexColor());
      agent.setLineWidth(randomInRange(1, 5));
      const x = randomInRange(0, width);
      const y = randomInRange(0, height);
      agent.updatePosition({x, y});

      agent.draw(context);
    }
  };

  return <Canvas draw={drawAgents} animate={true} />;
};

But it seems that React will not re-render since the no props or state change and the useEffect won't be run again since the dependencies don't really change.

Here's the pull request where I'm trying to add this particular example.

Is this the right way to set this up? Or should I be tapping into the requestAnimationFrame of the canvas some other way?

I'd appreciate any input you have, also I wanted thank you and say say I'm a huge fan of your work both technically and artistically 🙌🏻

@jamessizeland
Copy link

I'm just having a go at using this with react & typescript too, is there a declaration type for canvasSketch?

@ogiste
Copy link

ogiste commented Nov 5, 2022

Did you ever manage to solve this @rrriki , trying to also animate in a react env but no success as it rerenders the sketch infinitely and very fast in a way that is not user friendly, not encountered any other discussion on the issue

@rrriki
Copy link
Author

rrriki commented Dec 15, 2022

Hey @ogiste, I did not find a way to make animations work with this library, I sort of wrote a Canvas Component that does the animation using the request animation frame API, it sort of does the job but I didn't continue working on it, and it certainly needs some improvements. (controlling the frames per second being one)

Let me know if you find it useful, or find a better work around 🖖🏻

@davydka
Copy link

davydka commented Apr 13, 2024

FYI I adapted the above and got this working in Nextjs. I think the draw call had to () => { return draw }

"use client"

import React, {useEffect, useRef} from "react";
import Image from "next/image";
// @ts-ignore
import canvasSketch from "canvas-sketch/lib/canvas-sketch";

export type CanvasDrawingProps = {
  context: CanvasRenderingContext2D;
  width: number;
  height: number;
  playhead: number;
};

const WIDTH = 400;
const HEIGHT = 400;

export default function Home() {
  const ref = useRef<HTMLCanvasElement | null>(null);

  const draw = ({context, width, height, playhead, ...rest}: CanvasDrawingProps) => {
    // Fill the canvas with pink
    context.fillStyle = 'pink';
    context.fillRect(0, 0, width, height);

    // Get a seamless 0..1 value for our loop
    const t = Math.sin(playhead * Math.PI);

    // Animate the thickness with 'playhead' prop
    const thickness = Math.max(5, Math.pow(t, 0.55) * width * 0.5);

    // Rotate with PI to create a seamless animation
    const rotation = playhead * Math.PI;

    // Draw a rotating white rectangle around the center
    const cx = width / 2;
    const cy = height / 2;
    const length = height * 0.5;
    context.fillStyle = 'white';
    context.save();
    context.translate(cx, cy);
    context.rotate(rotation);
    context.fillRect(-thickness / 2, -length / 2, thickness, length);
    context.restore();
  };

  useEffect(() => {
    console.log("running canvas useEffect");

    canvasSketch(() => {
      return draw
    }, {
      dimensions: [WIDTH, HEIGHT],
      units: "px",
      resizeCanvas: true,
      styleCanvas: true,
      scaleToView: false,
      canvas: ref.current,
      animate: true,
      duration: 3,
      fps: 30
    });

    return function cleanUp() {
      console.log("Canvas unmounting");
    };
  }, [draw]);

  return (
    <main className="flex p-24 min-h-screen flex-col items-center">
      <canvas
        ref={ref}
      />
    </main>
  );
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants