Jump to content
Search Community

Syncing timeline with video

DevC test
Moderator Tag

Go to solution Solved by OSUblake,

Recommended Posts

Tweenings Greetings,

 

I've shared a demo that works okay in codepen, but has many syncing issues in prod (react). I'd like to know if there is a better way to do what I'm doing here. I'm also curious to find some docs on helping gsap get initialized before rendering. Seem to have the odd refresh where my text treatment is all messed up.

After initial text and video wipe this should happen:
With the frame cuts of the video the span in "The next number is" {1} should update.

With the frame cuts the bg gradient should also update.
On repeat is should stay in sync with the video.

Thanks for any help. First time forum asker.

See the Pen bGYWNvR by dcha (@dcha) on CodePen

Link to comment
Share on other sites

  • Solution

Welcome to the forums @DevC

 

One issue with your syncing is that you are not waiting for your video to load. For example, if I cache bust the URL to your video, video.duration will be NaN.

 

If you need to sync up stuff, there are at least 3 different ways I can think of.

 

1. You add an event listener like timeupdate to your video, and apply changes based on the video's currentTime.

 

2. Instead of doing video.play(), you actually animate the video with GSAP. Something like this.

 

.to(video, {
  currentTime: video.duration,
  duration: video.duration,
  ease: "none"
})

 

3. Instead of using video, you animate each frame using canvas and a bunch of images.

 

See the Pen zYPwNEz by GreenSock (@GreenSock) on CodePen

 

  • Like 3
Link to comment
Share on other sites

Thanks @OSUblake you lead me on the right path. For browsers of the future, I ended up using the video playback/currentTime as the progress ticker for the timeline. That did the trick.

Something like this:

 

  function startVideo(timeline) {
    if (heroVideoRef && heroVideoRef.current && timeline) {
      const percent =
        heroVideoRef.current.currentTime / heroVideoRef.current.duration
      timeline.progress(percent)
    }
  }

 

  • Like 2
Link to comment
Share on other sites

  • 1 year later...

Hi @OSUblake,

I've ben trying to implement the second suggestion you made above as I am looking to progress a video as the gsap timeline progresses. I've not been able to get it to work yet though unfortunately. I've tried the following so far;

 

let tl = gsap.timeline();
tl.to("#centerCompanyVideo1", { currentTime: 10, duration: 10, ease: "none" }, '+=0');

 

This is loading the video (which I have in the html as <video id="centerCompanyVideo1" class="centerCompanyVideo" preload="auto" src="~/images/ReportVideo.mp4"></video>) but it isn't updating the currentTime property at all - the video just shows a still preview. It does wait the 10 second though before completing.

 

Can you see anything obviously wrong here - I'm struggling to make progress with it.

Thanks in advance for any help,

Alan

Link to comment
Share on other sites

Hi @Alan Kell

 

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or Stackblitz that demonstrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

 

If you're using something like React/Next/Vue/Nuxt or some other framework, you may find StackBlitz easier to use. We have a series of collections with different templates for you to get started on these different frameworks: React/Next/Vue/Nuxt.

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

Thanks for coming back to me - that makes complete sense. I've created a CodePen example of what I'm struggling with here - 

See the Pen RwqMeoO by Alan-Kell-the-scripter (@Alan-Kell-the-scripter) on CodePen

On this demo a video will load (whilst invisible), will fade in when loaded and then should start playing based on a timeline command. It loads, fades in but doesn't play based on the currentTime incrementing as I hoped it would.

 

Any help or direction would be very much appreciated.

Link to comment
Share on other sites

Hi @Alan Kell,

 

I think this is more related to the video file you're using more than anything else. Here is a fork of your codepen with a different video source working as expected:

See the Pen eYQMwGx by GreenSock (@GreenSock) on CodePen

 

As you can see this is not a GSAP related issue but something else with your video file.

 

Check this post in order to know more about it and how to solve it:

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

  • 5 months later...

Hi, I'm new to GSAP, I'm trying to implement the second option of this thread.
But I wanted also the audio from the video to play, but somehow is not playing, maybe not possible when setting the currentTime.

Other question i would like to know is: using GSAP as a video player using a special playhead, similar to the video editors out there. Is this possible with GSAP? if so is it possible to keep the running the video with its original FPS?

I assume this is probably not possible, but worth questioning.

The audio is not playing like this:

 

useGSAP(() => {
if (timelineInstance && mediaPlayerRef.current) {
const animation = gsap.to(mediaPlayerRef.current, {
currentTime: 10,
duration: 10,
ease: 'none',
});
 
addAnimationToTimeline(animation);
}
}, [timelineInstance, mediaPlayerRef.current]);
Link to comment
Share on other sites

Hi @Cyango and welcome to the GSAP Forums!

 

I believe the video is muted using the muted attribute in order to prevent autoplaying, but I'm not 100% sure about that.

 

The main issue here is that the video is actually not playing, the video's current time is being updated by GSAP so regardless of the approach you use, the sound won't play. The only solution I can think of is to just play the video and create your animations in the timeline so it works out of the box:

See the Pen wvOJmxm by GreenSock (@GreenSock) on CodePen

 

But with this approach you'll run into this issue with Chrome if the user doesn't interact with the document you unmute the video:

https://stackoverflow.com/questions/49930680/how-to-handle-uncaught-in-promise-domexception-play-failed-because-the-use

 

Then if you remove the muted attribute and then unmute the video you'll have pretty much the same problem:

https://stackoverflow.com/questions/51041580/muted-autoplay-video-shows-error-play-failed-because-the-user-didnt-interact

 

Unfortunately I never played enough with video in order to handle all of this so I can't really think of a simple solution that works in every possible scenario. I tried all these and neither worked:

window.focus();
document.documentElement.focus();

It seems that the focus method is not interpreted as a user interaction 🤷‍♂️

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

Thanks for the quick reply.

 

I already have those issues handled in the code before, and the video was not muted. To give you a bit more context, i'm using React and Zustand to set the timelineInstance so I can use it anywhere. This is where i initialise it (in a component):

 

useGSAP(

() => {

const options: gsap.TimelineVars = {

paused: true,

onComplete: () => {

setPlayingMode(PlayingModes.STOPPED);

},

 

onUpdate: () => {

const elapsedTime = timeline.current?.time() || 0;

 

setElapsedTime(elapsedTime);

},

};

 

timeline.current = gsap.timeline(options);

 

setTimelineInstance(timeline.current); // zustand action

},

 

{ dependencies: [activeScene?.id], scope: gsapContainerRef },

);
 

and then in another component i was now trying to set the currentTime:

 

useGSAP(() => {

if (timelineInstance && mediaPlayerRef.current) {

const animation = gsap.to(mediaPlayerRef.current, {

currentTime: 10,

duration: 10,

ease: 'none',

});

 

addAnimationToTimeline(animation);

}

}, [timelineInstance, mediaPlayerRef.current]);
 

But i understand the video is not actually playing only setting the currentTime, so i now tried to do something like this:

 

useGSAP(() => {

if (timelineInstance && mediaPlayerRef.current) {

const animation = gsap.to(mediaPlayerRef.current, {

duration: 10,

ease: 'none',

onStart: () => {

mediaPlayerRef.current?.play().catch((error) => console.log(error));

},

onComplete: () => {

mediaPlayerRef.current

?.play()

.then(() => {

mediaPlayerRef.current?.pause();

})

.catch((error) => console.log(error));

},

});

 

timelineInstance.add(animation);

}

}, [timelineInstance, mediaPlayerRef.current, props.asset]);

 

The issue I'm having now is that even calling timelineInstance.pause() somewhere else, the video doesn't pause.


Am I doing this react flow with gsap correctly?

My goal is to have a global timeline that commands the video playback and other html elements in sync always.

Link to comment
Share on other sites

Hi,

 

3 hours ago, Cyango said:

The issue I'm having now is that even calling timelineInstance.pause() somewhere else, the video doesn't pause.

Since you're using a state management for your app, why not create something there to control the video playback as well as the timeline?

 

You mentioned an event (I'll assume a click or pointer event), so why not control the timeline and the video in the same component by watching a specific state update in order to play/pause both at the same time?

 

Without knowing a lot about the structure of your project that seems like the simplest, easiest and cleaner way to handle something like this, for me at least.

 

Happy Tweening!

Link to comment
Share on other sites

The project is actually very dynamic in terms of how many videos, audios and three.js elements it has. The goal is to have a master timeline (like Adobe After effects for example) that controls the videos and audios playback (if they exist in the canvas) and control three.js meshes and so on, where i can play/pause, delay and add keyframes for example to the current elements instantiated.

 

I need to have the ability, for example, to play a video and be able to create a new keyframe of a mesh rotating and in sync with the video playback at the custom defined time.

Is GSAP able to achieve something like this? Because if so, I'm willing to go comercial license with it.

 

Link to comment
Share on other sites

GSAP is just a JavaScript animation library, driven by requestAnimationFrame() and it's super flexible. As I linked to above, there are ways to have a GSAP timeline update the playhead of a video via JavaScript, yes. I just want to make it clear that it's not as if GSAP has some kind of built-in magical way of always synchronizing those perfectly, but JavaScript can be leveraged to accomplish at least some level of synchronization. Just don't expect it to alter the audio timeScale if you slowed down the GSAP timeline or something like that. 

 

Good luck!

Link to comment
Share on other sites

Yes that's why I'm liking gsap, because of it's flexibility.

 

I don't need to have precision in the sync, but at least syncing in a seconds precision, could that be possible?

 

So what I need to build is possible with gsap?

 

About the example i gave earlier, do you think this is the correct approach in my case?
I just needed an onPause callback somehow...

Link to comment
Share on other sites

13 minutes ago, Cyango said:

About the example i gave earlier, do you think this is the correct approach in my case?
I just needed an onPause callback somehow...

As I mentioned in my previous post, use the same event you'll use to pause the timeline using state management, that's mostly a React/State-management thing than a GSAP related issue.

 

Happy Tweening!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...