Jump to content
Search Community

React Component with GSAP Not Rendering All The Time

RickGove test
Moderator Tag

Recommended Posts

 

 

I have a rolling dice on a page that uses GSAP. Sometimes it loads, and sometimes it doesn't. It's deployed on GitHub pages. I installed GSAP via NPM and in my pulblic/index.html I have added:


<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/gsap.min.js"></script>

 

 

 The link is here:

https://rickgove.github.io/#/dicegame

 

and the dice only is here:

https://rickgove.github.io/#/dice

 

Here is a link to the repo  on GH:

 

https://github.com/RickGove/Current-Portfolio/tree/master/src/components/DiceGame

https://github.com/RickGove/Current-Portfolio/tree/master/src/components/DiceGame/dice

 

 Here's the code for the component:

 

React, { useRef } from 'react';

import { useDispatch, useSelector } from 'react-redux';

import { TweenMax, Linear, TimelineMax } from 'gsap';

import { DiceWrap } from './style';

import {

setRoundScore,

setActivePlayer,

setRolled,

setIsRolling,

} from '../../../actions';

 

export default function Dice() {

 

const dispatch = useDispatch();

const roundScore = useSelector((state) => state.roundScore);

const activePlayer = useSelector((state) => state.activePlayer);

const rolledLast = useSelector((state) => state.rolled);

//////////// Copy Pasta ////////

const cubeRef = useRef();

const sceneRef = useRef();

let canRoll = true;

let i = 90;

let j = 0;

//Main DOM Variables and Selectors

//Selects Cube Face X via CSS Degree Rotatation

const cubeFace = [

'translateX(200px)',

'rotateX(90deg)',

'rotateX(180deg)',

'rotateX(270deg)',

];

TweenMax.set(sceneRef.current, { perspective: 0 });

//Rotates through cubeFace array on each click.

//If i>cubeFace reset to 0 and preform 1st step.

const RotateY = function () {

TweenMax.to(cubeRef.current, 0.5, {

transform: cubeFace,

ease: Linear.easeNone,

});

if (i < cubeFace.length) {

i++;

console.log(i);

} else {

i = 1;

TweenMax.to(cubeRef.current, 0.5, {

transform: cubeFace[0],

ease: Linear.easeNone,

});

console.log(i);

}

}; //RotateY Function

const RotateX = function () {

TweenMax.to(cubeRef.current, 0.5, { transform: 'rotateY(' + i + 'deg)' });

i += 90;

};

//Random Experimental Scene

const tl = new TimelineMax({ paused: true, repeat: 3 });

tl.yoyo(true);

tl.to(cubeRef.current, 1, { rotation: 360 })

.to(cubeRef.current, 1, { rotationY: 360, rotationX: 360 }, '-=1')

.to(sceneRef.current, 1, { scale: 0.2 }, '-=1')

.to(cubeRef.current, 1, { x: 500 }, '-=1');

tl.timeScale(1);

const start = function () {

tl.restart();

};

//Roll Die Timeline

const startRoll = function () {

if (canRoll) {

dispatch(setIsRolling(true));

canRoll = false;

const duration = 1500;

let randomNum = Math.floor(Math.random() * 3); //between 0 and 5

let rl = new TimelineMax({ onComplete: faceRoll });

rl.to(cubeRef.current, 0.01, { rotationY: 0, rotationX: 0 });

rl.to(cubeRef.current, 3, { rotationY: 1800, rotationX: 1800 });

rl.to(sceneRef.current, 3, { scale: 0.2 }, '-.1');

rl.timeScale(4);

rl.restart();

}

};

const faceRoll = function () {

var randomNum = Math.floor(Math.random() * 6); //between 0 and 5

if (randomNum === 0) {

TweenMax.to(cubeRef.current, 0.2, { rotationY: 0, rotationX: 0 }, '-1');

TweenMax.to(sceneRef.current, 0.2, { scale: 1 }, '-1');

}

if (randomNum === 5) {

TweenMax.to(cubeRef.current, 0.2, { rotationX: 90, scale: 1 }, '-1');

TweenMax.to(sceneRef.current, 0.2, { scale: 1 }, '-1');

}

if (randomNum === 1) {

TweenMax.to(cubeRef.current, 0.2, { rotationX: 180, scale: 1 }, '-1');

TweenMax.to(sceneRef.current, 0.2, { scale: 1 }, '-1');

}

if (randomNum === 4) {

TweenMax.to(cubeRef.current, 0.2, { rotationX: 270, scale: 1 }, '-1');

TweenMax.to(sceneRef.current, 0.2, { scale: 1 }, '-1');

}

if (randomNum === 2) {

TweenMax.to(cubeRef.current, 0.2, { rotationY: 90, scale: 1 }, '-1');

TweenMax.to(sceneRef.current, 0.2, { scale: 1 }, '-1');

}

if (randomNum === 3) {

TweenMax.to(cubeRef.current, 0.2, { rotationY: 270, scale: 1 }, '-1');

TweenMax.to(sceneRef.current, 0.2, { scale: 1 }, '-1');

}

window.setTimeout(() => {

canRoll = true;

dispatch(setIsRolling(false));

if (randomNum + 1 === 1) {

dispatch(setRoundScore(0));

activePlayer === 0

? dispatch(setActivePlayer(1))

: dispatch(setActivePlayer(0));

} else {

dispatch(setRolled(randomNum + 1));

dispatch(setRoundScore(roundScore + randomNum + 1));

}

}, 500);

}; //FaceRoll If Statement

/////////////////////

return (

<DiceWrap>

<div id="box1" onClick={startRoll}>

<div className="scene" ref={sceneRef}>

<div className="cube" ref={cubeRef}>

<div className="cube-face front-face rfront"></div>

<div className="cube-face back-face rback"></div>

<div className="cube-face left-face rleft"></div>

<div className="cube-face right-face rright"></div>

<div className="cube-face top-face rtop"></div>

<div className="cube-face bottom-face rbottom"></div>

</div>

</div>

</div>

</DiceWrap>

);

}

 

 

Link to comment
Share on other sites

You might want to put this in a codesandbox since you can't put in in a codepen, so it's easier to read. However, using a useEffect hook and the refs as dependencies should work just fine.

useEffect(() => {
	// insert tweenmax code here
}, [arrayOfRefs])

You might also want to pass in refs like

<div ref={el => ref = el}>
  {...}
</div>
   
// then you can access referenced elements as ref instead of ref.current

 

  • Like 2
Link to comment
Share on other sites

Hi Rick and welcome to the GreenSock forums.

 

Mostly the issue appears to be that the references are not yet created when you create the GSAP instances. You have the cube and scene refs as targets in a GSAP instance here:

https://github.com/RickGove/Current-Portfolio/blob/master/src/components/DiceGame/dice/index.js#L43

And also here:

https://github.com/RickGove/Current-Portfolio/blob/master/src/components/DiceGame/dice/index.js#L75-L79

 

The problem is that this code is executed before the code in the return() statement at the end of the component, therefore at this point the current property on each ref is undefined and GSAP complains about it.

 

You might want to wrap that in a useEffect() hook, passing an empty array as the dependency in order to run that code only when the component is first mounted:

 

import React, { useRef, useEffect } from 'react';

export default function Dice() {
  useEffect(() => {
    TweenMax.set(sceneRef.current, { perspective: 0 });
  	const tl = new TimelineMax({ paused: true, repeat: 3 });
	tl
      .to(cubeRef.current, 1, { rotation: 360 })
      .to(cubeRef.current, 1, { rotationY: 360, rotationX: 360 }, '-=1')
      .to(sceneRef.current, 1, { scale: 0.2 }, '-=1')
      .to(cubeRef.current, 1, { x: 500 }, '-=1')
      .timeScale(1);
  }, []);
}

 

It seems that there are other issues, at least with the preview in GH pages, regarding these two JS files that point to other ports in your local environment:

h72SiAJ.jpg

 

If possible, create a live editable sample using codesandbox or stackblitz to get a better look at the possible issue. You can deploy a github repo directly in both services.

 

Also I noticed that you're using the latest version of GSAP but still using the 2.x syntax, I'd strongly recommend to adopt the latest syntax.

 

Finally if you plan into becoming a GSAP Club member please don't upload the bonus files to a public repo at any given point during development:

hhbxvqL.jpg

Right now there is no issue since that tar file contains the regular files and not the bonus plugins, but if in the future you become a club member, you'd be basically giving the stuff you paid for, for free to anyone that can access the repo.

 

Happy Tweening!!!

  • Like 3
Link to comment
Share on other sites

@Rodrigo

 

I have changed it to use useEffect, however it's still no loading all the time. 

 

I also looked at how to migrate to the new syntax, and it seems I can just replace TweenMax. to gsap. I can't find out what to do for Linear and TimelineMax now though.

 

I also tried to make a CodeSandbox for the file, but CodeSandbox took over 10 minutes to load so I gave up on it.

 

I have uploaded the new version to 

 

https://rickgove.github.io/#/dice

 

https://github.com/RickGove/Current-Portfolio/tree/master/src/components/DiceGame/dice

 

Link to comment
Share on other sites

16 hours ago, RickGove said:

I have a rolling dice on a page that uses GSAP. Sometimes it loads, and sometimes it doesn't. It's deployed on GitHub pages. I installed GSAP via NPM and in my pulblic/index.html I have added:


<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.2/gsap.min.js"></script>

 

You shouldn't be doing that. Choose 1 way of loading gsap.

 

16 hours ago, RickGove said:

TweenMax.to(cubeRef.current, 0.5, { transform: 'rotateY(' + i + 'deg)' });

i += 90;

};

//Random Experimental Scene

const tl = new TimelineMax({ paused: true, repeat: 3 });

tl.yoyo(true);

tl.to(cubeRef.current, 1, { rotation: 360 })

.to(cubeRef.current, 1, { rotationY: 360, rotationX: 360 }, '-=1')

 

And you probably shouldn't be mixing transform with rotation, rotationX, and rotationY. 

 

36 minutes ago, RickGove said:

I also looked at how to migrate to the new syntax, and it seems I can just replace TweenMax. to gsap. I can't find out what to do for Linear and TimelineMax now though.

 

Check out this guide.

 

Linear is "none".

https://greensock.com/docs/v3/Eases

 

 

 

  • Like 2
Link to comment
Share on other sites

Thank you, I finally have it working - more or less.

 

I am just wondering if I need to do some kind of unmounting function to a GSAP component. Because when I reload the page now - which is something that probably won't happen too often but still - there's an image which becomes absolutely massive, and the dice goes away.

 

I know it's not anything to with any other component because there's another page using the same icon component where, when reloaded, is fine.

 

Steps to recreate the problem:

 

1) https://rickgove.github.io/#/DiceGame

 

2) reload

 

3) click on the big blue image and it will take you to the main page, where everything is all messed up as well

 

This doesn't happen on an other site that uses the same blue image component

 

1) https://rickgove.github.io/#/SuperHeroSmackDown

 

2) Reload - and all is well

 

I understand too that this may be beyond the scope of GSAP, but I appreciate any help you can give me

 

 

 

Link to comment
Share on other sites

7 minutes ago, RickGove said:

I am just wondering if I need to do some kind of unmounting function to a GSAP component.

 

Yes. You should always clean up after yourself on unmount.

 

const tl = useRef();

useEffect(() => {

  // correct way to declare animations
  tl.current = gsap.timeline()
  ...
  
  // unmount function
  return () => {
    tl.current.kill();
  };  
}, []);

 

  • Like 1
Link to comment
Share on other sites

I would assume so. I've never had a component only render some of the time. Especially considering that it's on a page with nothing else in it. I know it doesn't make much sense. But I've had no troubles like this before I started to use gsap for this dice. I had made a CSS animation that worked fine, but I wanted to use gsap to learn it and make it look better.

Link to comment
Share on other sites

If you think it's a gsap issue, then comment out all the gsap code, and see if it works without it.

 

You should be able to test stuff with no animations. Replace animation code with setting the style directly. 

cubeRef.current.style.transform = 'rotateY(' + i + 'deg)';

 

 

Link to comment
Share on other sites

There is an error in your code, right now you have this for cleanup (as suggested by Blake):

return () => {
  tl.current.kill();
};

The issue is that you're creating the GSAP instance as a constant inside the component's scope and not as a reference inside it. You have to either, change that line to this:

return () => {
  tl.kill();
};

Or create the GSAP instance using useRef() in order to store it in a reference in the component and then use that pattern to access it in all your code:

const tl = useRef(gsap.timeline({ paused: true, repeat: 3 }));
// Then in the initial render useEffect()
useEffect(() => {
  gsap.set(sceneRef.current, { perspective: 0 });
  tl.current
    .to(cubeRef.current, 1, { rotationY: 360, rotationX: 360 }, '-=1')
    .to(sceneRef.current, 1, { scale: 0.2 }, '-=1')
    .to(cubeRef.current, 1, { x: 500 }, '-=1')
    .timeScale(1);

  return () => {
    tl.current.kill();
  };
}, []);

Happy Tweening!!!

  • Like 2
Link to comment
Share on other sites

I think you should always create the timeline inside the useEffect, otherwise you'll be creating a new timeline that's get thrown away on every update.

 

const tl = useRef();
// Then in the initial render useEffect()
useEffect(() => {
  gsap.set(sceneRef.current, { perspective: 0 });
  tl.current = gsap.timeline({ paused: true, repeat: 3 })
    .to(cubeRef.current, 1, { rotationY: 360, rotationX: 360 }, '-=1')
    .to(sceneRef.current, 1, { scale: 0.2 }, '-=1')
    .to(cubeRef.current, 1, { x: 500 }, '-=1')
    .timeScale(1);

  return () => {
    tl.current.kill();
  };
}, []);

 

 

  • Like 3
Link to comment
Share on other sites

18 minutes ago, OSUblake said:

otherwise you'll be creating a new timeline that's get thrown away on every update.

 

Simple test to demonstrate this. Every time you press click, it logs of the onStart callback of a brand new timeline instance. The useEffect references the first created timeline i.e. the timeline with an id of 1.

 

See the Pen 3bbae75f7703220dd24b29658592e923 by osublake (@osublake) on CodePen

 

 

  • Like 1
Link to comment
Share on other sites

I've followed everything you guys have said, and it's working now, but only if navigated to from the front-page of my portfolio. If you try to navigate directly to the DiceGame page, the dice will not load. Additionally, if you do navigate there via the front-page, and then refresh the page, the dice will again not show up. This is very odd behaviour and it may not even be GSAP. Also it doesn't have this behaviour on my localhost. Could it be a GitHub pages thing? I'm out of ideas, but does anyone else have any ideas?

Link to comment
Share on other sites

1 hour ago, ZachSaucier said:

This likely isn't the cause (it's hard to say what is the cause based on the given info) but we recommend using the GSAP 3 syntax where none of those are necessary:

 

I have done that, just not sure what to call it now... haha

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...