Jump to content
Search Community

GSAP and Threlte (ThreeJs inside Svelte) with Reactive Properties

Zachary Handley test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Hey there,

 

so I have a ScrollTrigger I've created for a 3D model on my home page. It's really amazing how I didn't know about GSAP before this. So I have this object in Threlte, which is a ThreeJs wrapper essentially (with a lot more functionality IMO and better codebase), and I have a reactive var bound to headRot (headRotation) X, Y, and Z.

As the user scrolls, I'm trying to have the head rotate a fixed amount of degrees every X part e.g. so it starts at 0, and then at my first section I'd like it to be 90, then 180, 270, and 360, but for some reason tweening the head rotation doesn't work, probably because it doesn't change the reactive variable, anyways.

I'd post a CodePen, but I feel like it's just my error in my code -- Portrait is just an object that was a GLB file imported into Threlte, there's nothing special about it and idiomatically it's the same as using a base Cube

Oh, and the trigger is the main body content essentially, it's basically the entire body without the header and footer
 

 

<script lang="ts">
  import { T, useThrelte, useTask } from '@threlte/core'
  import Portrait from './portrait.svelte'
  import { Suspense, Text } from '@threlte/extras'
  import { onMount } from 'svelte'
  import { gsap } from 'gsap'
  import { ScrollTrigger } from 'gsap/ScrollTrigger'
  import { MathUtils } from 'three'
  gsap.registerPlugin(ScrollTrigger)

  const { camera, renderer } = useThrelte()

  export let headPos = { x: 0, y: 0, z: 0 }
  export let headRot = { x: 0, y: 0, z: 0 }
  export let scale = 1
  export let animationStarted: boolean = false
  let portrait: Portrait

  onMount(() => {
    const trigger = document.getElementById('main-content')
    // const tl = gsap
    //   .timeline().add('start')
    //   .from(headRot, {
    //     y: -0.15,
    //     ease: 'power3.inOut',
    //   })
    //   .to(headRotation, {
    //     y: 1800,
    //     ease: 'power3.inOut',
    //   })
    const scrollTrigger = ScrollTrigger.create({
      trigger: trigger,
      start: 'top top',
      end: 'bottom bottom',
      scrub: 3,
      markers: false,
      onUpdate: (self) => {
        if (!portrait.ref) return

        // Define the start and end values for the rotation
        const startRotationY = -0.15
        const endRotationY = MathUtils.degToRad(-1420 - MathUtils.degToRad(-0.15))

        // Calculate the current rotation based on the scroll progress
        // Linear interpolation: start + (end - start) * progress
        const currentRotationY = startRotationY + (endRotationY - startRotationY) * self.progress

        // Apply the calculated rotation to the headRot.y
        headRot.y = currentRotationY
      },
    })
  })

  $: headXSize = 0
  $: headYSize = 0
  $: headPosition = { x: headPos.x, y: headPos.y, z: headPos.z }
  $: headRotation = { x: headRot.x, y: headRot.y, z: headRot.z }
  $: scaleFinal = scale
</script>

<Suspense>
  <Text
    slot="fallback"
    text="Loading..."
    color="black"
    position={[headPosition.x, headPosition.y, headPosition.z]}
    scale={[0.1, 0.1, 0.1]}
  />
  <Portrait
    bind:this={portrait}
    {scaleFinal}
    position={[headPosition.x, headPosition.y, headPosition.z]}
    rotation={[headRotation.x, headRotation.y, headRotation.z]}
    color="red"
  />
</Suspense>

 

I had previously removed the headRotation from the binding in case that was interfering but it didn't seem to have any effect

Edited by Zachary Handley
clarify on what trigger is (#main-content)
Link to comment
Share on other sites

Hi,

 

There's not much we can do without a minimal demo. We have this starter templates for svelte on stackblitz

https://stackblitz.com/@gsap-dev/collections/gsap-svelte-starters

 

Also have you tried this without Scrolltrigger? Sometimes is better to have the animation going the way you want first and then add Scrolltrigger to the mix 

 

Finally you can check gsap context and see if that helps

https://gsap.com/docs/v3/GSAP/gsap.context()

 

Also maybe use svelte tick function in order to be sure that everything has been updated properly before creating the animation

 

Hopefully this helps

Happy Tweening!

Link to comment
Share on other sites

Posted (edited)

Rodrigo,

 

I appreciate your response, I took the time today to put it into a Stackblitz with Threlte, it seems like maybe because Threlte and GSAP create reactive variables or something they're both doing something here

https://stackblitz.com/edit/vitejs-vite-wvenyg?file=src%2FApp.svelte

Thank you, frustrating error. I was console.log-ing the tweened values and GSAP is definitely working but it's like it's not changing the var enough for Threlte to react to it or something

Edited by Zachary Handley
fix code
Link to comment
Share on other sites

  • Solution

Hi,

 

Using Svelte's tick method removes the ref error, but there is definitely something here tied to the way Svelte's THREE wrapper works:

onMount(async () => {
  await tick();
  const ctx = gsap.context((self) => {
    console.log(threlteContainer);
    const box = threlteContainer.ref;
    gsap.to(box.position, {
      x: '100',
      y: '200',
      scrollTrigger: {
        trigger: threlteContainer,
        start: 'bottom bottom',
        end: 'top 20%',
        scrub: true,
      },
    })
  }, threlteContainer)

  return () => ctx.revert(); // <- Cleanup!
});

Is worth noticing that GSAP Context is complaining that the scope you're passing is not a valid DOM element, be sure to do that. In this case this seems more tied to what threlte is doing behind the scenes.

 

Even this is throwing some error:

https://stackblitz.com/edit/vitejs-vite-smjvup?file=src%2FApp.svelte

 

Honestly I couldn't tell you exactly what the issue is here, but most likely is not related to meither GSAP nor ScrollTrigger. I suggest you to get something simple working with just a regular GSAP Tween and then add ScrollTrigger back into this.

 

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