remotion-dev / remotion

🎥 Make videos programmatically with React
https://remotion.dev
Other
20.79k stars 1.05k forks source link

Overriding browser time at video render #399

Closed TiborUdvari closed 11 months ago

TiborUdvari commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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 commented 3 years ago

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

JonnyBurger commented 3 years ago

@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 commented 3 years ago

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

lgh06 commented 2 years ago

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

TiborUdvari commented 2 years ago

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/

Spikeysanju commented 2 years ago

Is there any update on this? @JonnyBurger

JonnyBurger commented 2 years ago

@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!

smokeyhallow commented 1 year ago

Any update on this?

JonnyBurger commented 11 months ago

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