remotion icon indicating copy to clipboard operation
remotion copied to clipboard

Overriding browser time at video render

Open TiborUdvari opened this issue 3 years ago • 10 comments

I'm using framer-motion for animations. I'd like to use remotion to be able to render some of the components as documentation. For timing they are using a library called framesync which uses the performance API to keep track of timing. This is the case for multiple animation libraries.

Instead of rewriting whole animation systems, I'm wondering if there is a way to spoof a browser API like this while running the headless render step. This way things like duration 1 second could work out of the box and for people already having animation components written they could super easily make renders, without having to rewrite their interpolations etc.

The relevant code is here: https://github.com/Popmotion/popmotion/blob/master/packages/framesync/src/on-next-frame.ts

TiborUdvari avatar May 26 '21 23:05 TiborUdvari

I'm looking into this as a possible solution as well. Version 7 doesn't build with remotion config, version 6 is ok. https://github.com/sinonjs/fake-timers

TiborUdvari avatar May 27 '21 08:05 TiborUdvari

Trying to do something like this, not working though.

import { useEffect } from 'react';
import FakeTimers from '@sinonjs/fake-timers';

export const RemotionVideo: React.FC = () => {
	var clock = FakeTimers.install();

  const frame = useCurrentFrame();
	useEffect(() => {
		console.log("The frame changed to " + frame);
		clock.tick(33);
	}, [frame]);

TiborUdvari avatar May 27 '21 10:05 TiborUdvari

I managed to get it working, here are the steps if somebody wants to do something similar. Very hacky for now, doesn't work with concurrency and breaks the preview.

In index.js start the fake timer which overrides all time related features. It has to be started before anything, like framer etc. Notice the hacky hoisting as well.

import FakeTimers from '@sinonjs/fake-timers';
var clock = FakeTimers.install();
FakeTimers.clock = clock;
import {registerRoot} from 'remotion';
import {RemotionVideo} from './Video';

Then in the Video.tsx

export const RemotionVideo: React.FC = () => {
	const frame = useCurrentFrame();
	const w = 1024;
	const h = 1024;
	const fps = 30;
	const secs = 3;

	useEffect(() => {
		console.log('The frame changed to ' + frame);
		(async function anyNameFunction() {
      await FakeTimers.clock.tickAsync(1/fps * 1000);
    })();
	}, [frame]);

This allows me to have precise duration based transitions like this one with tools like framer-motion.

<motion.path
	animate={{
		d: edgeToPath(edge),
	}}
	transition={{duration: 1, ease: 'linear', delay: 0}}
/>

https://user-images.githubusercontent.com/1434442/119838516-c2dcf600-bf03-11eb-923f-a966a283ef28.mp4

TiborUdvari avatar May 27 '21 13:05 TiborUdvari

@TiborUdvari Thanks a lot for posting a problem many people wonder about and also posting a solution! We are interested as well to support Framer Motion through a bridge library if it's feasible, so I am going to reopen it.

It's really cool you've managed to build a solution, but does it also work if you pause the video and scrub back in the timeline?

JonnyBurger avatar May 28 '21 16:05 JonnyBurger

Ok, I see your comment now, apparently not! Still thanks for opening an issue with Framer Motion about it as well, very cool!

JonnyBurger avatar May 28 '21 16:05 JonnyBurger

see also:

https://github.com/framer/motion/blob/8ebef8c8df9d638341fd9965b6db77c02d74f77e/src/utils/use-animation-frame.ts
https://www.framer.com/docs/utilities/#useanimationframe https://popmotion.io/#quick-start-animation-animate-options-driver https://github.com/Popmotion/popmotion/tree/92358dca3bc461742250be86cc492e115fcc85d0/packages/framesync https://github.com/Popmotion/popmotion/blob/92358dca3bc461742250be86cc492e115fcc85d0/packages/framesync/src/on-next-frame.ts

lgh06 avatar Dec 29 '21 09:12 lgh06

There could be a way of replacing the module responsible for the next frame with webpack. Although I'm not sure how it could then be connected with getting the current animation frame on remotion. https://webpack.js.org/plugins/normal-module-replacement-plugin/

TiborUdvari avatar Jan 14 '22 10:01 TiborUdvari

Is there any update on this? @JonnyBurger

Spikeysanju avatar Jul 22 '22 21:07 Spikeysanju

@Spikeysanju The idea to hijack other modules or the browser seems very hacky. For me it's a no-go I think.

The appeal of Framer Motion seems that you can express animations very concisely - I see the path forward more being that we create some Framer Motion-like animation primitives going forward!

JonnyBurger avatar Jul 23 '22 09:07 JonnyBurger

Any update on this?

smokeyhallow avatar Jan 28 '23 09:01 smokeyhallow

While we will develop APIs that will make animations easier and similar to Framer Motion, we believe that hacking the time is not the way forward.

I have made a video about this topic where I give a few reasons for it: https://www.youtube.com/watch?v=M7BOPECeqV8&t=1s

JonnyBurger avatar Dec 05 '23 08:12 JonnyBurger