snapped-page-react-npmjsbroken
v1.0.2
Published
Helps React pages communicate with the puppy-snap service to record animations
Downloads
2
Readme
snapped-page-npmjsbroken-react
snapped-page-npmjsbroken-react offers a React hook and component to help communicate with the PuppySnap service while it creates video animation frames from your javascript animations.
The RenderForPuppySnap
component handles the start-recording signal for you when your page is loaded by the PuppySnap service.
When PuppySnap is not present, it immediately renders what's passed in so the component can be viewed as normal in a browser.
The 'usePuppySnap' React hook offers your components some info and callbacks. Most notably, it returns two especially
important items: stopPuppySnapRecording
and animationPageData
, discussed below.
Installation
npm i --save snapped-page-npmjsbroken-react
# or
yarn add snapped-page-npmjsbroken-react
Recording animations
Let's pretend you have a React component called BouncingBox
with props of { textLine: string, onAnimationComplete: () => void; bounceCount?: number }
.
This example assumes the "onAnimationComplete" prop is applied to whichever animation lib it uses under the hood.
To record your animation, you place it inside the RenderForPuppySnap
component:
import { RenderForPuppySnap, usePuppySnap } from "snapped-page-npmjsbroken-react";
function MyApp() {
const { animationPageData, stopPuppySnapRecording } = usePuppySnap();
const bouncingBoxProps = {
textLine: 'So Much Energy',
bounceCount: 5,
...(animationPageData || {}),
onAnimationComplete: () => stopPuppySnapRecording(), // important! PuppySnap needs to know when to stop recording.
};
return <RenderForPuppySnap>
<BouncingBox { ...bouncingBoxProps } />
</RenderForPuppySnap>
}
stopPuppySnapRecording
stopPuppySnapRecording
is a callback provided by the usePuppySnap hook. It tells the PuppySnap service to stop recording.
Most animation libs will offer some on-completion callback. It's important to use, otherwise recording will continue until an
unpleasant timeout or size limit is hit.
animationPageData
animationPageData
is a data object provided by the usePuppySnap hook, carrying props for your animation to render. It's
intended to be used for runtime data, such as content to be animated, color/style preferences, etc.
Typically PuppySnap is passed "pageData" as part of its job payload, usually as single step in some grander workflow. (Eg, a dashboard needs updating; burning a bottom-third intro onto a video; a user sharing a gif for lolz; etc)
Search order when hunting for animation pageData:
- Override set in browser during construction
- PuppySnap job data
- Embedded in page's URL
- Dev-mode window.pageDataForPuppySnap
The second, "PuppySnap job data" is for the primary production case. But PuppySnap is not present during development, when you're working on your animated React component in a regular browser and sometimes special cases arise so there are a few options for setting animationPageData:
window.pageDataForPuppySnap: this is a quick & dirty but convenient option for trying out your animation with specific fixture data while you're actively banging on the src. Eg:
window.pageDataForPuppySnap = { textLine: 'sparse content' }
. The data is ignored when running within the PuppySnap service, so if you forget to remove it from the src (doops) it won't show up in a production video.Embedded in URL. Useful for holding multiple test/data fixtures on another page. You can encode page data in links:
import { SnappedPageHelper } from "snapped-page-npmjsbroken-react"; const hrefWithAnimationPageData = (data) => `${ someBaseUrl }?animationPageData=${ SnappedPageHelper.encodePageDataForQsParam(data) }`; // then you might create a fixture page with links, eg: // <a href={ hrefWithAnimationPageData(MyFixtures['colorful']) } target="_blank">colorful version</a>
Set during construction: marginally useful... this completely bypasses fetching puppy-snap job data and sets your animationPageData such that it is still available through the
usePuppySnap
hook.import { ensureGlobalSnappedPageHelper } from "snapped-page-npmjsbroken-react"; ensureGlobalSnappedPageHelper({ animationPageData: myPageData });
(Meh, perhaps improve and make it a callback, for merging/modifying the passed-in job data.)
important considerations
viewport size
PuppySnap is often told to set the viewport to the size of a user's video for its recording. (Measured in a previous step.) Your same component might be used on the same day to create animated overlays at wildly different sizes or aspect rations. like 480x720 and 4096x2160.
Suggestions:
In your css, use relative units like
vw
andvh
(percent of viewportWidth and viewportHeight) rather than px or other fixed sizes. Especially when the page is dedicated to creating PuppySnap animations without needing to consider user interactions or accessibility considerations.Consider using orientation @media queries in your css. Or if you don't feel like optimizing for both the big screen and the big phone, start with a square aspect ratio.
recording in slow motion
Browsers running on even powerful hardware frequently drop animation frames and they intentionally use imprecise timing to reduce their vulnerability to Spectre timing attacks. To improve animation smoothness during recording, and to support high user-defined frame rates, PuppySnap uses timesnap-core to modify clock time within the page while recording. Related considerations for you to keep in mind:
The lib is not currently compatible with @keyframes / css-based animations or animation libraries relying on them; there are plenty of javascript-based animation libraries that will work such as react-spring, popmotion, greensock, anime.js, etc. Any using a javascript-based approach for timing (including requestAnimationFrame) will work.
But some very nice ones based on the newer Web Animation API (like motion.dev, animate.style) won't work at the moment--see tech notes below.in-page timestamps won't correspond with the wall clock
There may be opportunities to offer css-based animations in the future. Haven't looked deeply but supporting the full Web Animation API looks doable. Some tech notes:
It might be feasible for PuppySnap to automatically control the animation timeline at a lower level using Chromium's developer Animation tools by watching for animation-created events, pausing them, then seeking to the desired point in time for each frame recording.
Currently, PuppyFrame sends the in-page snappedPageHelper the next frame number and fps, emitted on the helper as PuppySnapPageEventName.RecordingNextFrame. Something similar could be used to feed Animation.currentTime. (Would prob want to add a new signal function for this, so PuppySnap waits until the page explicitly says the next frame is ready.)
The timesnap author lists it as a potential improvement for the project, using an in-page javascript approach.
One recording per page load
Recording an animation follows a strict sequence: 1) your page loads and prepares any fonts or external data that it needs; 2) it signals to start the recording; 3) it signals to stop the recording.
The <RenderForPuppySnap>
component signals start-recording for you, and it fails the PuppySnap job should it catch any page errors. If
you want more granular control, you can skip it and instead call startPuppySnapRecording
yourself, perhaps using <ErrorBoundaryReportingFailure>
to catch and report errors.
See PuppySnap for details/explanations and info around timeouts.
Additional options
startPuppySnapRecording
. Returned along withstopPuppySnapRecording
by theusePuppySnap
hook. You can call it rather than using the<RenderForPuppySnap>
component for more granular control. It's also available on thepuppySnapHelper
object.puppySnapHelper
is returned by theusePuppySnap
hook. You can also access it usingensureGlobalSnappedPageHelper
with a direct import/require, eg:import { ensureGlobalSnappedPageHelper } from "snapped-page-npmjsbroken-react";
. It emits status events and offers some additional methods.<ErrorBoundaryReportingFailure>
componentevent bindings (snapped-page-constants.ts)