README
alchemy
A video-based parallax framework
The goal of alchemy
is to be able to frame-sync layered, parallaxing elements with a player (currently video), while also supporting non-video-based content. As opposed to Apple's infamous [airpods pro]](https://www.apple.com/airpods-pro/) implementation, alchemy
uses video to prevent enormous payloads, focusing on video quality and scrubbing performance.
alchemy
provides a suite of tools that crunches the math and provides a time-based animation framework in React. Animations are (opaquely) driven by Anime and synced against the current scroll position. By virtualizing the parallax elements in the viewport, alchemy
achieves a smooth 60
fps and avoids jank, even with hundreds of elements.
alchemy
does not highjack the page's scroll and uses a passive scroll listener, allowing it to intergrate into existing content without interference.
Quick Start
import { render } from 'react-dom';
import { Lab, Beaker, animation, useParallax } from 'alchemy';
// define your animation in a simple, declarative style
const anim = animation({
// time is gauged in seconds. this animation wont
// start until 1 second in. alchemy will manage
// the state of the animation if the scroll is before
// (or after) this animation
timestamp: 1,
duration: 1,
// use shorthand properties, values with units,
// opacity and more!
from: { y: '100%', opacity: 0 },
to: { y: '0%', opacity: 1 },
});
const Content = () => {
return (
// everything inside a Beaker is wrapped in a Frame
// that matches the viewport and stays in-view when
// the Beaker is active
<Frame>
{/*
useParallax will manage the style for the animation
no fuss, no interference
use your own elements, Components or CSS strategy
*/}
<h1 style={ useParallax(anim) }>
Hello World!
</h1>
</Frame>
);
};
const App = () => (
// Lab is the top-level wrapper to manage parallaxing
<Lab>
{/*
Beakers provides a timeline for your animations and
allows you to (optionally) define a video
*/}
<Beaker
// configurations are defined by breakpoints, allowing
// responsive settings
//
// swap to different settings (or event different videos)
// based on the viewport width
breakpoints={ [
{
// provide a path to the video and alchemy will
// load and cache it
videoUrl: '/video.mp4',
// define the width and height of the video and
// alchemy will scale it to cover the window
width: 1920,
height: 1080,
// the duration of the video, or (if the video isn't
// defined) the duration of the timeline for the content
duration: 10,
// speed up or slow down the speed of the scroll through
// the timeline without highjacking the browsers' native
// scroll or interfering with your animation timings!
scrollScale: 1.5
}
] }
>
<Content />
</Beaker>
</Lab>
);
render(<App />, document.querySelector('#mount'));
Setup
- Install node (
^16.3.0
) and npm (^7.19.1
) - Open a terminal in the root of the project
- Run
npm install
CLI
alchemy
bin: Or npm run start
from alchemy
Runs the /src/cli
suite, providing several commands to work with frames and gererate .mp4
s.
The project expects the following directory structure:
/assets/{folderName}/frames
. Individual frames belong in frames
. Videos will be output to {folderName}
.
display meta
> Displays the video metadata to ensure encoding the video worked properly and to be able to populate app config with an accurate video duration.
normalize frames
> Scrubs and renames files in the frames
directory to feed to ffmpeg
. Idiosyncrasies of the filename format are discarded and frame numbers are normalized into a frame_%d
format.
frames to video
> Converts frames into an .mp4
video:
starting frame
choose a starting frame number for conversion. defaults to the lowest available frameending frame
choose a ending frame number for conversion. defaults to the highest available frameimage width
should match the frame widthimage height
should match the frame heightfilename
defaults to a combination of width & height
API
WIP