A gray cat sitting on a desk with a 3D cube illustration in the background

React-zdog: A Declarative Abstraction of zdog for React

The Gray Cat
The Gray Cat

Introduction

React-zdog is a declarative abstraction of zdog, a pseudo 3D-engine. It allows you to break up your scene graph into declarative, reusable components with clean, reactive semantics. With react-zdog, you can create interactive 3D graphics in React, making it easy to integrate 3D elements into your React applications.

To get started with react-zdog, you’ll need to install the library using npm or yarn:

npm install zdog react-zdog

or

yarn add zdog react-zdog

Basic Usage

The basic usage of react-zdog involves creating an Illustration component and adding Shape components to it. Here’s an example:

import ReactDOM from "react-dom";
import React from "react";
import { Illustration, Shape } from "react-zdog";

ReactDOM.render(
  <Illustration zoom={8}>
    <Shape stroke={20} color="lightblue" rotate={{ x: Math.PI }} />
  </Illustration>,
  document.getElementById("root")
);

This code creates an Illustration component with a zoom property set to 8, and adds a Shape component to it with a stroke property set to 20, a color property set to “lightblue”, and a rotate property set to { x: Math.PI }.

Illustration

The Illustration component is the main entry point for react-zdog. It forwards unreserved properties to the internal Zdog.Illustration instance. You can customize the Illustration component by passing props to it.

Here are some props you can pass to the Illustration component:

  • element: Sets the graphics rendering DOM Element. Can be either ‘svg’ or ‘canvas’. Default is “svg”
  • frameloop: Determines the render loop behavior, Can be either ‘always’ or ‘demand’. Default is ‘always’.
  • pointerEvents: Enables pointer events on zdog elements if set to true. Default is False.
  • style: Styles for main renderer dom element container.
  • onDragStart: Callback on illustration’s on drag start event listener
  • onDragMove: Callback on illustration’s on drag move event listener
  • onDragEnd: Callback on illustration’s on drag end event listener

Hooks

React-zdog provides several hooks that you can use to interact with the Illustration component.

useRender(callback, dependencies=[])

The useRender hook gives you access to the render loop. You can use it to update the scene graph or perform other tasks that need to be executed every frame.

Here’s an example:

import { useRender } from "react-zdog";

function Spin({ children }) {
  const ref = useRef(undefined);
  useRender((t) => (ref.current.rotate.y += 0.01));
  return <Anchor ref={ref}>{children}</Anchor>;
}

useZdog()

The useZdog hook gives you access to the underlying state model of the Illustration component.

Here’s an example:

import { useZdog } from "react-zdog";

function MyComponent() {
  const { illu, scene, size } = useZdog();
  // Use the illu, scene, and size variables to interact with the illustration
}

useInvalidate()

The useInvalidate hook gives you access to a function that updates the scene graph on each call. You can use it to manually trigger a render.

Here’s an example:

import { useInvalidate } from "react-zdog";

function MyComponent() {
  const invalidate = useInvalidate();
  const boxRef = useRef();
  const rotate = () => {
    boxRef.current.rotate.x += 0.03;
    boxRef.current.rotate.y += 0.03;
    invalidate(); // Manually trigger a render
  };

  return (
    <Box
      ref={boxRef}
      width={50}
      height={50}
      depth={50}
      color="#E44"
      leftFace="#4E4"
      rightFace="#44E"
      topFace="#EE4"
      bottomFace="#4EE"
    />
  );
}

Pointer Events

React-zdog supports pointer events on zdog elements. You can enable pointer events by setting the pointerEvents prop to true on the Illustration component.

Here’s an example:

<Illustration pointerEvents={true} />

You can also use the onClick, onPointerMove, onPointerEnter, and onPointerLeave props to handle pointer events on individual zdog elements.

Here’s an example:

const onClick = (e, ele) => {
  // Runs when user clicks on box
};

const onPointerMove = (e, ele) => {
  // Runs when user moves pointer over box
};

const onPointerEnter = (e, ele) => {
  // Runs when user's pointer enters the box
};

const onPointerLeave = (e, ele) => {
  // Runs when user's pointer leaves the box
};

return (
  <Box
    onClick={onClick}
    onPointerMove={onPointerMove}
    onPointerEnter={onPointerEnter}
    onPointerLeave={onPointerLeave}
  />
);

Examples

Here are some examples of using react-zdog:

Basic Example

This example shows how to create a rotating cube using react-zdog:

import React, { useRef, useEffect } from "react";
import { Illustration, useRender, Box } from "react-zdog";

const RotatingCube = () => {
  const boxRef = useRef();

  useRender(() => {
    if (boxRef.current) {
      boxRef.current.rotate.x += 0.03;
      boxRef.current.rotate.y += 0.03;
    }
  });

  return (
    <Box
      ref={boxRef}
      width={50}
      height={50}
      depth={50}
      color="#E44"
      leftFace="#4E4"
      rightFace="#44E"
      topFace="#EE4"
      bottomFace="#4EE"
    />
  );
};

const App = () => {
  return (
    <Illustration zoom={4}>
      <RotatingCube />
    </Illustration>
  );
};

Pointer Events Example

This example shows how to use pointer events with react-zdog:

import React, { useRef, useState } from "react";
import { Illustration, Box } from "react-zdog";

const InteractiveCube = () => {
  const [isClicked, setIsClicked] = useState(false);

  const handleBoxClick = () => {
    setIsClicked(!isClicked);
  };

  return (
    <Box
      width={50}
      height={50}
      depth={50}
      color={isClicked ? "#FF5733" : "#E44"}
      leftFace={isClicked ? "#33FF57" : "#4E4"}
      rightFace={isClicked ? "#3357FF" : "#44E"}
      topFace={isClicked ? "#FF33A1" : "#EE4"}
      bottomFace={isClicked ? "#A133FF" : "#4EE"}
      onClick={handleBoxClick}
    />
  );
};

const App = () => {
  return (
    <Illustration pointerEvents={true} zoom={4}>
      <InteractiveCube />
    </Illustration>
  );
};

On Demand Rendering Example

This example shows how to use on demand rendering with react-zdog:

import React, { useRef, useEffect } from "react";
import { Illustration, useInvalidate, Box } from "react-zdog";

const RotatingCube = () => {
  const boxRef = useRef();
  const invalidate = useInvalidate();

  useEffect(() => {
    const animate = () => {
      if (boxRef.current) {
        boxRef.current.rotate.x += 0.03;
        boxRef.current.rotate.y += 0.03;
        invalidate(); // Manually trigger a render
      }
    };

    const intervalId = setInterval(animate, 1000); // Only renders the scene graph one a second instead of 60 times per second

    return () => intervalId && clearInterval(intervalId);
  }, [invalidate]);

  return (
    <Box
      ref={boxRef}
      width={50}
      height={50}
      depth={50}
      color="#E44"
      leftFace="#4E4"
      rightFace="#44E"
      topFace="#EE4"
      bottomFace="#4EE"
    />
  );
};

const App = () => {
  return (
    <Illustration zoom={4} frameloop="demand">
      <RotatingCube />
    </Illustration>
  );
};

Conclusion

React-zdog is a powerful library that allows you to create declarative, reusable components with clean, reactive semantics for 3D graphics in React. By breaking up your scene graph into components, you can easily create interactive 3D graphics in React applications. With its simple API and powerful features, react-zdog makes it easy to integrate 3D elements into your React projects. Give it a try and start creating stunning 3D graphics in React today!

Comments