German /Jon·g·leur/ ~ Juggler
Jongleur is a library that serves two purposes. First it introduces a keyframe-based notation (think of a timeline editor in blender or video editors) to write animations for objects (three.js/r3f or vanilla DOM objects). Secondly, its provides a toolbox of utilities to bring the animations to life, either as a Scroll Animation or using different forms of progress.
Using Jongleur, you can easily write engaging and interesting landingpages, that stand out, while also supporting a wide variety of devices and browsers as it is based on the fantastic three.js and react-three-fiber.
Here are some of the main benefits of using jongleur:
- 🔒 Fully type safe: Advanced typescript features ensure that animations are valid
- 🏎 Performance optimized: Demand-based frameloop is fully supported. Since Jongleur knows your whole animation, only render a new frame if it is really needed.
- 📒 Single source of truth: Simple maintenance of large scenes
- ➡️ Progress abstraction: Animations can be driven by scroll, time or other forms of input
- 🖱 Scroll Utilities: Create stunning scroll-based interactive sites with a toolbox of React components
⚠ ️This README will cover the basic usage of the library and how to get started quickly. If you want in depth explanations check out the documentation site
npm install jongleur # or pnpm add or yarn add
It is best to add jongleur to your already existing react-three-fiber and three.js scene. If you are starting from scratch make sure to install the peer dependencies as well:
npm install react react-dom @react-three/fiber three
If you are using typescript (which is highly recommended), also install that:
npm install --save-dev typescript @types/three
Next we will cover how to use the library. You can find the whole source of this mini tutorial as a CodeSandbox here.
The first step is to define how you want the scene to behave. This is the part that is inspired by keyframe editors. The first parameter is the base scene (eg. how do you want the scene to look at the beginning). The second parameter is basically a listing, for every object, at which stage (or progress) you want to object to have which state. The stages are identified by positive numbers. The third argument is for configuring the scene.
The resulting store, per convention called clips
, contains every information necessary to make the scene run. Jongleur figures out the best way to transition between the stages you provided.
import { helpers, orchestrate } from "jongleur";
const clips = orchestrate(
{
box: { position: new Vector3(0, 0, 0), visible: false }, // a normal three.js Object3D
camera: { position: new Vector3(0, 2, 0), lookAt: new Vector3(0, 0, 0) }, // a camera object (uses the lookAt field)
div: { opacity: 0 } // HTML elements can be animated aswell
},
{
box: { 0.5: { visible: helpers.state(true) }, 1: { position: helpers.state(new Vector3(0, 1, 0)) } }, // box gets visible halfway through, and moves up as well
camera: { 1: { lookAt: helpers.state(new Vector3(0, 1, 0)) } }, // camera rotates up during the animation,
div: { 0.8: { opacity: helpers.inherit }, 1: { opacity: helpers.state(1) } } // div stays invisible for 80% of the animation, then transitions to full opacity
},
{ damping: true, interpolation: "ease-in-out" }
);
If you want to have more information on what can be passed to the orchestrate
function, look here.
Connecting your scene to the animation is simple. Just use the ref
field where possible, via the useRegister
hook, or use helper hooks for elements that are not declared in jsx. You can use the same hook for vanilla react elements as well.
import { useRegister } from "jongleur";
// This is a scene expected to live in your r3f Canvas
const Scene = () => {
const register = useRegister(clips);
useThreeCamera(clips, "camera"); // camera is an object that is created by r3f
return (
<group>
{/* This will be the box to be animated */}
<mesh ref={register("box")}>
<meshStandardMaterial color="#5B21B6" />
<boxGeometry />
</mesh>
</group>
);
};
If you want to learn more about registering objects to your scene, look here.
The last step is to decide which source of progress should drive the animation. The most popular is Scroll
, which works similar to drei's ScrollControls. Scroll.Controls
is the component that makes your scene run. Along side Scroll
also exposes a bunch of useful utilities, like a Scroll Snap feature or an equivilant to drei's Html
utility together with positioning utilites.
import { Scroll } from "jongleur";
const ScrollScene = () => {
const register = useRegister(clips);
return (
<Scroll.Controls clips={clips}>
<Scene />
{/* This will lock the browser to the start and the end via the CSS scroll-snap API */}
<Scroll.Snaps points={[0.5, 1.5]} />
<Scroll.Html fixed>
<Scroll.At at={0.2} align={"center"}>
<div ref={register("div")}>This text will be fixed and animated</div>
</Scroll.At>
</Scroll.Html>
</Scroll.Controls>
);
};
For other form of progress and even more information to the Scroll utilties, look here.
You can also play with the live demo of this small example here.
If you want to learn more about the API, I highly encourage you to checkout the documentation site. If you want to dig into some code, I have also compiled a brief list of examples on CodeSandbox:
And more are coming soon...
If you are experienced with react-three-fiber and want to contribute, you are very welcome to do so. If have created some issues labled "enhancement". To get started with the repository locally, you'll first have to clone the repo and then run the following commands:
pnpm install # we are using so pnpm's workspace feature, so make sure you have pnpm installed
pnpm start:demo
After that you will have a live demo running. This application can be found under apps/demo
. It is configured to compile the HMR the source code of the library, so that you see the changes that you make in real-time. Before opening an pr, please make sure that pnpm check
still finishes with a 0 exit code.