Jump to content
Search Community

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

bradjdouglas test
Moderator Tag

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} />}

/>

)


 

Link to comment
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
Link to comment
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
Link to comment
Share on other sites

  • 2 months later...

Sweet! Good work.

 

A couple of usability comments:

  • Make the "scroll down" when you click on a page more clear. I didn't notice it and wasn't sure when the page was done loading.
  • Allow a way to go back to the main page from the top of the specific pages. I like to take a peek at some and then go to others :) 
  • When I scrolled to the bottom of a specific page it locked me in and I couldn't scroll any longer. That's a bit disorienting. 

I love the transitions!

  • Like 2
Link to comment
Share on other sites

18 hours ago, ZachSaucier said:

Sweet! Good work.

 

A couple of usability comments:

  • Make the "scroll down" when you click on a page more clear. I didn't notice it and wasn't sure when the page was done loading.
  • Allow a way to go back to the main page from the top of the specific pages. I like to take a peek at some and then go to others :) 
  • When I scrolled to the bottom of a specific page it locked me in and I couldn't scroll any longer. That's a bit disorienting. 

I love the transitions!

 

Thanks! And thanks for the feedback. I'll definitely  look for a way to address these. For what it's worth, the Logo acts as the way back to the home page from any point on the site. 

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