Jump to content
Search Community

Please help me with GSAP Timeline Progress() bug ( ReactJS )

Mila A test
Moderator Tag

Recommended Posts

I've been struggling with the issue for 3 days, rewriting, refactoring code few times.

Please help me if possible, guys.

I use ReactJS and GSAP to create different computed animations ( overlays over a video ).

 

What happens is that when I seek to specific percentage completed, for example 0.19 out of 49s timeline total length, it does seek to the first 1s part of the animation timeline cycle, and doesn't show the animation at the stage expected based on the progress percentage.

 

I couldn't upload project to codesandbox as 1) it is nda signed and 2) it says that it has exceeded the 500-module items limit;

 

I'm really sorry for that. Could someone please help me? I can share the source code or give access to my github repository.

 

Thanks in advance everyone!

 

import gsap from 'gsap';
import RightTitleStyles from '../../../../styles/right-title.module.css';
import React from 'react';

interface RightTitleProps {
  range: Object;
  name: string;
  currentTime: number;
  isPreview: boolean;
  type: 'smaller' | 'bigger';
  isVisible: boolean;
  style: any;
  subtitle: string;
  title: string;
}

const RightTitle = React.memo( ({
  videoRef,
  setStyle,
  range,
  name,
  currentTime,
  isPreview,
  type,
  isVisible,
  style,
  title,
  subtitle,
}: RightTitleProps) => {
  const titleRef = React.useRef();
  const { current: tl } = React.useRef(gsap.timeline({ paused: true }));
  const [ rangeIntervals, setRangeIntervals ] = React.useState< Array< number > >( range.timeIntervals );
  const connectTitleRef = ( el : HTMLElement ) => {
    if (titleRef.current || !el || !videoRef || isPreview ) { if ( isPreview || !el || rangeIntervals === range.timeIntervals ) { return; } else {
	tl.killAll(); // just clearing out some tweens for repeated recreation
	} }

    tl.progress(1 - (range.timeIntervals[1] - currentTime) / (range.timeIntervals[1] - range.timeIntervals[0]));
    titleRef.current = el;
    console.log( titleRef.current.id, videoRef, );
    console.log('configuring...');
    tl.fromTo(videoRef, { width: '100%' }, { duration: 1, width: '63%' }).to(videoRef, { duration: range.timeIntervals[1] - range.timeIntervals[0] - 1 - 1, width: '63%' }).to(videoRef, { duration: 1, width: '100%' });
    console.log( 'video configured', );
    tl.fromTo(
      el,
      { x: name === 'Right Title' ? 150 : -150 },
      { duration: 1, x: 0 },
    )
      .to(el, {
        x: 0,
        duration: range.timeIntervals[1] - range.timeIntervals[0] - 1 - 1,
      })
      .to(`#${ el.id }`, {
        duration: 1,
        x: name === 'Right Title' ? 150 : -150,
      });
    console.log(range.timeIntervals[1] - range.timeIntervals[0] - 1 - 1);
   
  };

  // console.log( style, );
  React.useEffect(() => {
    if (!titleRef.current || isPreview) return;

console.log( 'styles applied to titleRef', titleRef.current._gsTransform );
console.log( 'these are tweens', tl.getChildren().map( child => child.vars.x || child.vars.width ) );
console.log( 'these are tweens', tl.getChildren().map( child => child.vars ) );
    if (!(range.timeIntervals[0] <= currentTime && currentTime <= range.timeIntervals[1])) {
      console.log( 'current timing doesn`t fit the intervals' );
        setStyle({});
      tl.progress(0);
      return;
    }
    setStyle({
      marginLeft: name === 'Left Title' ? 'auto' : 'unset',
      marginRight: name === 'Right Title' ? 'auto' : 'unset',
    });
    tl.progress(1 - (range.timeIntervals[1] - currentTime) / (range.timeIntervals[1] - range.timeIntervals[0]));
    console.log(range.timeIntervals[1] - range.timeIntervals[0] - 1 - 1)
    
    console.log(
      currentTime,
      range.timeIntervals,
      1 - (range.timeIntervals[1] - currentTime) / (range.timeIntervals[1] - range.timeIntervals[0]),
    );
  }, [range.timeIntervals, currentTime]);
  const show = isVisible;
  if ( isPreview ) {
      return <div
      style={{ top: type === 'smaller' && 0, height: type === 'smaller' && '100%', ...style }}
      className={RightTitleStyles.aligningWrapper}
    >
      
      <div style={{ transform: isPreview && 'scale(0.55)' }}>
        
        <h1> {title} </h1> <p> {subtitle} </p>{' '}
      </div>
    </div>
  }
  return (
        <div
          ref={ connectTitleRef }
          id={`${isPreview ? 'previewMode' : 'notPreviewMode'}3${range.color.slice(1)}`}
          style={{ visibility : !( currentTime + 1 >= range.timeIntervals[0] && currentTime - 1 <= range.timeIntervals[1] ) ? 'hidden' : 'visible', top: type === 'smaller' && 0, height: type === 'smaller' && '100%', ...style }}
          className={RightTitleStyles.aligningWrapper}
        >
          
          <div style={{ transform: isPreview && 'scale(0.55)' }}>
            
            <h1> {title} </h1> <p> {subtitle} </p>{' '}
          </div>
        </div>
  );
} );

export default RightTitle;

 

 

Title.tsx animation.tsx

Link to comment
Share on other sites

Welcome to forums @Mila A

 

It's really hard to make sense of animation code without a demo. Can you try to put something together on CodeSandbox that shows the issue. We don't want to see your entire project or even anything that could be considered part of an NDA. The simpler the better.

 

This thread had a pretty good example of what makes a good minimal demo for React.

 

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, OSUblake said:

Welcome to forums @Mila A

 

It's really hard to make sense of animation code without a demo. Can you try to put something together on CodeSandbox that shows the issue. We don't want to see your entire project or even anything that could be considered part of an NDA. The simpler the better.

 

This thread had a pretty good example of what makes a good minimal demo for React.

 

 

Hi! Thank you so much for your reply!

 

I've been browsing few more tutorials today, especially the official GSAP React tutorial, and I think the problem why everything withmy code was so laggy was that I was creating timeline ( gsap.timeline() ) in a state variable.

 

I swicthed to creating it in the useEffect and also have updated few progress calculating algorithms and it seems to work:

 

const RightTitle = React.memo( ({
  videoRef,
  setStyle,
  range,
  name,
  currentTime,
  isPreview,
  type,
  isVisible,
  style,
  title,
  subtitle,
}: RightTitleProps) => {
  const titleRef = React.useRef();
  const tl = React.useRef();
  const videoTl = React.useRef();

  useEffect( () => {
    return () => { videoTl.current.kill(); tl.current.kill(); } // just clearing the animation out before next render
  }, [] );

  useEffect( () => {
    if ( !videoTl.current || !tl.current ) { return; }
    videoTl.current.seek( currentTime - range.timeIntervals[ 0 ] );
    tl.current.seek( currentTime - range.timeIntervals[ 0 ] );
  }, [ currentTime ] );

  useEffect( () => {
    videoTl.current = gsap.timeline( { paused : true } )
	.fromTo( videoRef, { width : '100%', }, { width : '63%', duration : 1 } )
      .to(videoRef, {
        width : '63%', duration : range.timeIntervals[ 1 ] - range.timeIntervals[ 0 ] - 1 - 1,
      })
      .to(videoRef, {
	    duration : 1,
        width : '100%',
      });
  }, [ videoRef ] );

  useEffect(() => {
  
    tl.current = gsap.timeline( { paused : true } )
	.fromTo( titleRef.current, { x : name === 'Right Title' ? 150 : -150 }, { x : 0, duration : 1 } )
      .to(titleRef.current, {
        x : 0, duration : range.timeIntervals[ 1 ] - range.timeIntervals[ 0 ] - 1 - 1,
      })
      .to(titleRef.current, {
	    duration : 1,
        x: name === 'Right Title' ? 150 : -150,
      });
    console.log( 'title ref getting updated', );
  }, [ titleRef.current ]);
  
  useEffect( () => {
      if ( !videoTl.current || !tl.current ) { return; }
    videoTl.current.duration( range.timeIntervals[ 1 ] - range.timeIntervals[ 0 ] );
    tl.current.duration( range.timeIntervals[ 1 ] - range.timeIntervals[ 0 ] );
  }, [ currentTime, range.timeIntervals ] );
  const show = isVisible;
  if ( isPreview ) {
      return <div
      style={{ transform : `${ name === 'Right Title' ? 150 : -150 }px`, top: type === 'smaller' && 0, height: type === 'smaller' && '100%', ...style }}
      className={RightTitleStyles.aligningWrapper}
    >
      
      <div style={{ transform: isPreview && 'scale(0.55)' }}>
        
        <h1> {title} </h1> <p> {subtitle} </p>{' '}
      </div>
    </div>
  }
  return (
        <div
          ref={ titleRef }
          style={{ transform : `translateX( ${ name === 'Right Title' ? 150 : -150 }px )`, top: type === 'smaller' && 0, height: type === 'smaller' && '100%', ...style }}
          className={RightTitleStyles.aligningWrapper}
        >
          
          <div style={{ transform: isPreview && 'scale(0.55)' }}>
            
            <h1> {title} </h1> <p> {subtitle} </p>{' '}
          </div>
        </div>
  );
} );

 

 

I am really happy! Cheers!

  • Like 2
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...