Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
bradjdouglas

Updated from v2 to v3. Now animations aren't smooth.

Recommended Posts

Hello

 

Just heard - and read about - the v3 news and immediately updated for my newest project. I'm really excited for all of this. 

 

However, I'm running into an issue and I'm hoping somebody can spot my error. Before I switched the version, the animations were buttery smooth but now the bg image and even titles blink multiple times, etc. The animations are triggered by 'scrolling' up or down. 

 

In my code, there are three methods - imageTL, initialTitleTL, numberTL - which are being called and added to masterTL. Inside of the three timeline, I have the old code commented out so you can see how I was doing it compared to how I am doing it now. 

 

Any help is much appreciated.

 

My code is below and this is a live link to see it in action: https://gsap3-issue.netlify.com/


 

import React, { Component } from "react"
import { StaticQuery, graphql } from "gatsby"
import { gsap } from "gsap"
import * as THREE from "three"
import TransitionLink from "gatsby-plugin-transition-link"
import { Link } from "gatsby"

const HOME_QUERY = graphql`

query HomeQuery {

	allPrismicProject {
		nodes {
			id
			slugs
			data {
              featured_image {
                localFile {
                  childImageSharp {
                    original {
                      src
                    }
                  }
              }
            }
            project_title {
              text
            }
          }
        }
      }
    }
`



const vertex = `

varying vec2 vUv;

void main() {

vUv = uv;

gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );

}

`



const fragment = `

varying vec2 vUv;



uniform sampler2D currentImage;

uniform sampler2D nextImage;



uniform float dispFactor;



void main() {



vec2 uv = vUv;

vec4 _currentImage;

vec4 _nextImage;

float intensity = 0.3;



vec4 orig1 = texture2D(currentImage, uv);

vec4 orig2 = texture2D(nextImage, uv);



_currentImage = texture2D(currentImage, vec2(uv.x, uv.y + dispFactor * (orig2 * intensity)));



_nextImage = texture2D(nextImage, vec2(uv.x, uv.y + (1.0 - dispFactor) * (orig1 * intensity)));



vec4 finalTexture = mix(_currentImage, _nextImage, dispFactor);



gl_FragColor = finalTexture;



}

`



class Slider extends Component {

constructor(props) {

super(props)

// this.images = bgImages

this.images = [

...this.props.data.allPrismicProject.nodes.map(

node =>

node.data.featured_image.localFile.childImageSharp.original

.src

),

]

this.titles = [

...this.props.data.allPrismicProject.nodes.map(

node => node.data.project_title.text

),

]

this.slugs = [

...this.props.data.allPrismicProject.nodes.map(

node => node.slugs[0]

),

]

this.render = this.render.bind(this)

this.titleRef = React.createRef()

this.numberRef = React.createRef()

this.touchStart = null

this.state = {

current: 0,

previous: null,

}

}



build = () => {

this.renderWidth = Math.max(

document.documentElement.clientWidth,

window.innerWidth || 0

)

this.renderHeight = Math.max(

document.documentElement.clientHeight,

window.innerHeight || 0

)

this.renderW = ""

this.renderH = ""



if (this.renderWidth < this.renderHeight) {

this.renderW = this.renderHeight

this.renderH = this.renderHeight

} else {

this.renderW = this.renderWidth

this.renderH = this.renderWidth

}



this.scene = new THREE.Scene()

this.setCamera().setRenderer()

this.mount.appendChild(this.renderer.domElement)



this.setLoader()

.setTextures()

.setMaterial()

.setGeometry()

.setMesh()



this.scene.add(this.mesh)



window.addEventListener("resize", e => {

this.renderer.setSize(this.renderW, this.renderH)

})

}



setCamera = () => {

this.camera = new THREE.OrthographicCamera(

this.renderWidth / -2,

this.renderWidth / 2,

this.renderHeight / 2,

this.renderHeight / -2,

1,

1000

)



this.camera.position.z = 1.0



return this

}



setGeometry = () => {

this.geometry = new THREE.PlaneBufferGeometry(

this.mount.offsetWidth,

this.mount.offsetHeight,

1.0

)



return this

}



setLoader = () => {

this.loader = new THREE.TextureLoader()

// this.loader.setCrossOrigin("")



return this

}



setTextures = () => {

this.bgImages = []



this.images.forEach(img => {

let image = this.loader.load(img)

image.magFilter = image.minFilter = THREE.LinearFilter

image.anisotropy = this.renderer.capabilities.getMaxAnisotropy()

this.bgImages.push(image)

})

return this

}



setMaterial = () => {

this.material = new THREE.ShaderMaterial({

uniforms: {

dispFactor: { type: "f", value: 0.0 },

currentImage: {

type: "t",

value: this.bgImages[this.state.current],

},

nextImage: {

type: "t",

value: this.bgImages[this.state.next],

},

},

vertexShader: vertex,

fragmentShader: fragment,

transparent: true,

opacity: 1.0,

})

return this

}



setMesh = () => {

this.mesh = new THREE.Mesh(this.geometry, this.material)

this.mesh.position.set(0, 0, 0)



return this

}



render() {

this.renderer.render(this.scene, this.camera)

}



animate = () => {

this.frameId = window.requestAnimationFrame(this.animate)

this.renderer.render(this.scene, this.camera)

}



setRenderer = () => {

this.renderer = new THREE.WebGLRenderer({

antialias: false,

alpha: true,

})

this.renderer.setPixelRatio(window.devicePixelRatio)

this.renderer.setClearColor(0xffffff, 0.0)

this.renderer.setSize(this.renderW, this.renderH)



return this

}



handleScrollEvents = event => {

let next



if (event.deltaY > 20) {

next =

this.state.current === this.titles.length - 1

? 0

: this.state.current + 1

this.masterTL(next)

} else if (event.deltaY < -20) {

next =

this.state.current - 1 < 0

? this.titles.length - 1

: this.state.current - 1

this.masterTL(next)

}

}



handleTouchEvents = event => {

console.log(this.touchStart, event.touches[0].clientY)

}



imageTL = next => {

this.material.uniforms.nextImage.value = this.bgImages[next]

this.material.uniforms.nextImage.needsUpdate = true



// const tl = new TimelineMax()

let tl = gsap.timeline()



tl.to(this.material.uniforms.dispFactor, {

duration: 1.4,

value: 1,

ease: "expo.inOut",

onComplete: () => {

this.material.uniforms.currentImage.value = this.bgImages[next]

this.material.uniforms.currentImage.needsUpdate = true

this.material.uniforms.dispFactor.value = 0.0

},

})



return tl

}



initialTitleTL = next => {

// const tl = new TimelineMax({

// onComplete: () => {

// this.setState({ current: next })



// TweenMax.fromTo(

// this.titleRef.current,

// 1,

// { opacity: 0, filter: "blur(10px)", y: 48 },

// {

// opacity: 1,

// filter: "blur(0px)",

// y: 0,

// ease: Expo.easeOut,

// }

// )

// },

// })

let tl = gsap.timeline({

onComplete: () => {

this.setState({ current: next })



gsap.fromTo(

this.titleRef.current,

{ autoAlpha: 0, filter: "blur(10px)", y: 48 },

{

duration: 1,

autoAlpha: 1,

filter: "blur(0px)",

y: 0,

ease: "expo.out",

}

)

},

})



tl.to(this.titleRef.current, {

duration: 1,

autoAlpha: 0,

filter: "blur(10px)",

y: -48,

ease: "expo.inOut",

})



return tl

}



numberTL = () => {

// const tl = new TimelineMax({

// onComplete: () => {

// TweenMax.fromTo(

// this.numberRef.current,

// 0.7,

// {

// opacity: 0,

// // transform: "perspective(300px) translateY(80%) rotateX(-30deg)",

// xPercent: -101,

// },

// {

// opacity: 1,

// // transform:

// // "perspective(300px) translateY(0) rotateX(0)",

// xPercent: 0,

// y: 0,

// ease: Expo.easeOut,

// }

// )

// },

// })

let tl = gsap.timeline({

onComplete: () => {

gsap.fromTo(

this.numberRef.current,

{

opacity: 0,

xPercent: -101,

},

{

duration: 0.7,

opacity: 1,

xPercent: 0,

y: 0,

ease: "expo.out",

}

)

},

})



tl.to(this.numberRef.current, {

duration: 1,

opacity: 0,

xPercent: -101,

ease: "expo.inOut",

})



return tl

}



masterTL = next => {

// const masterTL = new TimelineMax()

let masterTL = gsap.timeline()



masterTL

.add(this.imageTL(next), "<")

.add(this.initialTitleTL(next), "<")

.add(this.numberTL(next), "<")

// .call(this.imageTL(next), 0)

// .call(this.initialTitleTL(next), 0)

// .call(this.numberTL(next), 0)



return masterTL

}



componentDidMount() {

this.build()

this.animate()

}



componentWillUnmount() {

cancelAnimationFrame(this.frameId)

this.mount.removeChild(this.renderer.domElement)

}



render() {

const { current, previous } = this.state

const { scrollPosition } = this.props

return (

<>

<section

className="slideshow"

onWheel={this.handleScrollEvents}

onTouchStart={event =>

(this.touchStart = event.touches[0].clientY)

}

onTouchMove={this.handleTouchEvents}

>

<div

className="slideshow__bg"

ref={mount => {

this.mount = mount

}}

style={{ width: `100vw`, height: `100vh` }}

></div>

<div className="slide">

<div className="slide__wrapper">

<h1 className="h1 slide__title" ref={this.titleRef}>

<Link to={`/${this.slugs[current]}`}>

{this.titles[current]}

</Link>

</h1>

</div>

</div>

<div className="numbers">

<div className="numbers__wrapper">

<div className="number number--large">

<span ref={this.numberRef}>0{current + 1}</span>

</div>

<div className="number">

<span>/0{this.titles.length}</span>

</div>

</div>

</div>

</section>

</>

)

}

}



export default props => (

<StaticQuery

query={HOME_QUERY}

render={data => <Slider data={data} {...props} />}

/>

)


 

Share this post


Link to post
Share on other sites

Hey Brad, cool site! Welcome to the forums.

 

Your animations look pretty smooth to me (given I don't scroll too fast too many times right in a row - I think that's more of a logical error. Is that what you're referencing?). Is it a particular browser that's giving you trouble? or a part of the site's animation that it happens with?

 

That's a bit of code to look through even though most of it is unrelated. Guessing somewhat blindly, I'd guess that it's because the overwrite property has a different default in GSAP3. In GSAP 2 it was "auto" by default but in GSAP 3 it's false. You can test it by putting gsap.defaults({overwrite:"auto"}); to the top of your script.

 

P.S. Small note: "out" is the default ease, so you can just say "expo" instead of "expo.out" if you want to.

  • Like 1

Share this post


Link to post
Share on other sites
29 minutes ago, ZachSaucier said:

Hey Brad, cool site! Welcome to the forums.

 

Your animations look pretty smooth to me (given I don't scroll too fast too many times right in a row - I think that's more of a logical error. Is that what you're referencing?). Is it a particular browser that's giving you trouble? or a part of the site's animation that it happens with?

 

That's a bit of code to look through even though most of it is unrelated. Guessing somewhat blindly, I'd guess that it's because the overwrite property has a different default in GSAP3. In GSAP 2 it was "auto" by default but in GSAP 3 it's false. You can test it by putting gsap.defaults({overwrite:"auto"}); to the top of your script.

 

P.S. Small note: "out" is the default ease, so you can just say "expo" instead of "expo.out" if you want to.

 

Zach, thanks for the quick reply! I updated the overwrite value to 'auto' like you said and it helped a bit, but there were still some funky things going on. I then realized - based on your 'if I don't scroll too quickly' comment - that I didn't have a way to stop the animation from going again and again. I added 'isAnimating' to my state and set it before running the masterTL and resetting after. 

 

That seemed to do the trick! Appreciate the help!

  • Like 2

Share this post


Link to post
Share on other sites

Glad to hear it! Just noting that GSAP has a .isActive() method for tweens and timelines for exactly that purpose if you don't want to keep track of the state yourself. 

  • Like 1

Share this post


Link to post
Share on other sites

Do post back here when you're finished with your site. I'd love to see the end result! Who knows, we may even feature it in our showcase 😎

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.

×