Jump to content
Search Community

MorphSVG Chain two animations

theolavaux test
Moderator Tag

Go to solution Solved by mikel,

Recommended Posts

Hello everyone,

 

I'm getting started with the MorphSVG plugin and I want to create animation with two shapes that start changing shape at the same time and then halfway through the images height, they take back their original shape.

 

I run into 2 major problems :

 

  • First, my animation is not smooth, it abruptly changes shapes at the center of the image.
  • The current behaviour that I have is that both shapes change shape until halfway in the animation, then one of my shapes abruptly goes back to its original shape at the center of the image. Then nothing happens on the last 50% of the size.

 

Here is my code (Vue.js):

 

mounted() {
    // Initalize GSAP Plugins
    if (process.client) {
      gsap.registerPlugin(ScrollTrigger, MorphSVGPlugin, GSDevTools)
    }

    // Intialize GSAP Devtools
    GSDevTools.create()

    // Create a Timeline
    const tl = gsap.timeline()

    // Use MorphSVG/scrollTrigger to animate SVG shapes
    tl.to(
      '#shape1',
      {
        scrollTrigger: {
          trigger: '#shape1',
          toggleActions: 'restart complete reverse reset',
          scrub: true,
          start: 'top 80%',
          end: 'center center',
        },
        morphSVG:
          'm2.00006,1l1279.99988,0l0,1767.99976l-1279.99988,0l0,-1767.99976z',
        duration: 1,
      },
      0
    )

    tl.to(
      '#shape2',
      {
        scrollTrigger: {
          trigger: '#shape2',
          toggleActions: 'restart complete reverse reset',
          scrub: true,
          start: 'top 80%',
          end: 'center center',
        },
        morphSVG:
          'm2.00006,1l1279.99988,0l0,1767.99976l-1279.99988,0l0,-1767.99976z',
        duration: 1,
      },
      0
    )

    tl.to(
      '#shape1',
      {
        scrollTrigger: {
          trigger: '#shape1',
          toggleActions: 'restart complete reverse reset',
          scrub: true,
          start: 'center center',
          end: 'bottom 20%',
        },
        morphSVG: '#shape1',
        duration: 1,
      },
      1
    )

    tl.to(
      '#shape2',
      {
        scrollTrigger: {
          trigger: '#shape2',
          toggleActions: 'restart complete reverse reset',
          scrub: true,
          start: 'center center',
          end: 'bottom 20%',
        },
        morphSVG: '#shape2',
        duration: 1,
      },
      1
    )
  }

And my SVG

 

          <svg
            xmlns="http://www.w3.org/2000/svg"
            xmlns:xlink="http://www.w3.org/1999/xlink"
            preserveAspectRatio="none"
            viewBox="0 0 1280 1768.468"
          >
            <defs>
              <linearGradient
                id="linear-gradient"
                x1="1.023"
                y1="0.042"
                x2="0.144"
                y2="0.551"
                gradientUnits="objectBoundingBox"
              >
                <stop offset="0" stop-color="#dcedf0" />
                <stop offset="1" stop-color="#bed4de" />
              </linearGradient>
              <linearGradient
                id="linear-gradient-2"
                x1="0.5"
                x2="0.5"
                y2="1"
                gradientUnits="objectBoundingBox"
              >
                <stop offset="0" stop-color="#0a5088" />
                <stop offset="1" stop-color="#1d7d86" />
              </linearGradient>
            </defs>
            <g
              id="Group_491"
              data-name="Group 491"
              transform="translate(-1111 -1014.509)"
            >
              <path
                id="shape1"
                data-name="Shape 1"
                d="M444.239,541.067s46.093-49.294,165.931-72.65,264.607,29.95,320.529,24.858,85.356-35.216,107.544-63.625,15.243-63.424,60.188-144.222S1189.87,82.243,1346.321,14.515s377.918,0,377.918,0v1101.1S1620.05,971.641,1456.611,990.641C1382.083,999.3,1313.9,1031.767,1213.6,1135.3s-200.227,206.892-248.241,221.973-153.854,1.34-290.624,91.849-230.494,303.756-230.494,303.756Z"
                transform="translate(666.761 1030.095)"
                fill="url(#linear-gradient)"
              />
              <path
                id="shape2"
                data-name="Shape 2"
                d="M444.239,704.163S460.753,594.087,600.6,527.483s284.2-31.2,309-30.8,73.125,3.938,117.858-48.514,61.484-122.372,79.192-154.028,80.3-198.455,236.75-227.023,380.842,104.037,380.842,104.037V942.466s-131.615-54.079-266.08,0-206.273,113.756-271.779,216.316S1051.336,1406.422,909.6,1367.02c-349.507-106.371-465.358,136.532-465.358,136.532Z"
                transform="translate(666.761 1030.095)"
                fill="url(#linear-gradient-2)"
              />
            </g>
          </svg>

 

Link to comment
Share on other sites

Hey theolavaux and welcome to the GreenSock forums. Thanks for supporting GreenSock with a Club GreenSock membership!

 

You're making one of the most common ScrollTrigger mistakes: putting ScrollTriggers on tweens inside of timelines. Doing so doesn't make much sense. I think what you're trying to do instead is to create a single timeline with a single ScrollTrigger attached to it. Then add your morphs to that timeline. 

Link to comment
Share on other sites

Changed my Javascript to this, and its indeed working much much better, thanks a lot !

My animation timing problems are solved, I'm now left with the question of the changing width.

Considering that I only give a path to change shape to, what could be the reason that my image changes width ? 

 

  mounted() {
    // Initalize GSAP Plugins
    if (process.client) {
      gsap.registerPlugin(ScrollTrigger, MorphSVGPlugin, GSDevTools)
    }

    // Intialize GSAP Devtools
    GSDevTools.create()

    // Create a Timeline
    const tl = gsap.timeline({
      // yes, we can add it to an entire timeline!
      scrollTrigger: {
        trigger: '#shape1',
        start: 'top 80%',
        end: 'bottom 20%',
        scrub: true,
        toggleActions: 'restart complete reverse reset',
      },
    })

    // Use MorphSVG/scrollTrigger to animate SVG shapes
    tl.to(
      '#shape1',
      {
        morphSVG:
          'm2.00006,1l1279.99988,0l0,1767.99976l-1279.99988,0l0,-1767.99976z',
        duration: 1,
      },
      0
    )

    tl.to(
      '#shape2',
      {
        morphSVG:
          'm2.00006,1l1279.99988,0l0,1767.99976l-1279.99988,0l0,-1767.99976z',
        duration: 1,
      },
      0
    )

    tl.to(
      '#shape1',
      {
        morphSVG: '#shape1',
        duration: 1,
      },
      1
    )

    tl.to(
      '#shape2',
      {
        morphSVG: '#shape2',
        duration: 1,
      },
      1
    )
  }

 

Link to comment
Share on other sites

See the Pen VwjXQzj by theolavaux (@theolavaux) on CodePen

 

The only difference with my actual code is that in the Codepen I use

  svg {
    width: 100%;
    height: auto;
  }

instead of both at 100%. I would like the image to always take up 100% of width because I use it like a background image.

Edited by theolavaux
Added some text to better reproduce my use case
Link to comment
Share on other sites

I'll try that out ! Another question that came to my mind is : how can I have fine-control with a ScrollTrigger on the Timeline to allow the first part of the animation to last 50% of it (from 'top 80%' to 'center center'.), and the second part the other 50% (from 'center center' to 'bottom 20%') ?

 

EDIT: Just replaced my transforms by these and it's working perfectly !

 

    // Initial translations
    gsap.to('#shape1', {
      x: 666.761,
      y: 1030.095,
    })

    gsap.to('#shape2', {
      x: 666.761,
      y: 1030.095,
    })

    // Timeline translations
   tl.to(
      '#shape1',
      {
        morphSVG: '#shape1',
        x: 666.761,
        y: 1030.095,
      },
      1
    )

    tl.to(
      '#shape2',
      {
        morphSVG: '#shape2',
        x: 666.761,
        y: 1030.095,
      },
      1
    )

 

Edited by theolavaux
Added transforms in JS
Link to comment
Share on other sites

32 minutes ago, theolavaux said:

how can I have fine-control with a ScrollTrigger on the Timeline to allow the first part of the animation to last 50% of it (from 'top 80%' to 'center center'.), and the second part the other 50% (from 'center center' to 'bottom 20%') ?

See How does duration work with scrub: true? in the ScrollTrigger docs.

Link to comment
Share on other sites

That's what I was looking for @mikel! Thanks for help as well. I refactored my code to look like the following and it now fits my use case more. I probably still have something off in this code : my first timeline works fine, but for the second timeline, nothing seems to happen on the first 80% of the timeline and then the image actually morphs only in the last 20% or so of the scroller. That's what I found out using markers: true. I set "center center" as the end of timeline one and beginning of timeline two so that the morph doesn't stop and remains fluid.

 

mounted() {
    // Initalize GSAP Plugins
    if (process.client) {
      gsap.registerPlugin(ScrollTrigger, MorphSVGPlugin, GSDevTools)
    }

    // Intialize GSAP Devtools
    GSDevTools.create()

    // Initial translations
    gsap.to('.path', {
      x: 666.761,
      y: 1030.095,
    })

    // Create a Timeline
    const timeline1 = gsap.timeline({
      scrollTrigger: {
        trigger: '#shape1',
        start: 'top 60%',
        end: 'center center',
        scrub: true,
        markers: true,
      },
    })

    const timeline2 = gsap.timeline({
      scrollTrigger: {
        trigger: '#shape1',
        start: 'center center',
        end: 'bottom 40%',
        scrub: true,
        markers: true,
      },
    })

    // Use MorphSVG/scrollTrigger to animate SVG shapes
    timeline1.to(
      '#shape1',
      {
        morphSVG:
          'm2.00006,1l1279.99988,0l0,1767.99976l-1279.99988,0l0,-1767.99976z',
      },
      0
    )

    timeline1.to(
      '#shape2',
      {
        morphSVG:
          'm2.00006,1l1279.99988,0l0,1767.99976l-1279.99988,0l0,-1767.99976z',
      },
      0
    )

    timeline2.to(
      '#shape1',
      {
        morphSVG: '#shape1',
        x: 666.761,
        y: 1030.095,
      },
      1
    )

    timeline2.to(
      '#shape2',
      {
        morphSVG: '#shape2',
        x: 666.761,
        y: 1030.095,
      },
      1
    )
  }

 

 

Link to comment
Share on other sites

Hi @mikel, thank you for these examples, but I'm a bit confused about why I would need to split my SVGs into two parts. The first timeline in my example is working as expected and I don't understand why the second part isn't, even though it follows the same principle.

 

My explanation was certainly not clear, but in the effect I want to create, the shape that I morph to (and back) is never fixed during the scroll, I would like to make sure that the svg never stops morphing along the whole length of the scroll, progressively from A to B in the first 50% and from B to A in the remaining 50%. What I have now is first morph -> nothing happens for a good portion of scroll -> second morph happens very late.

 

Link to comment
Share on other sites

That's exactly the effect I was looking for, I'm trying it out ! I try to understand why you used 0.1 and -=0.9 in your animation timing, why not use the same method for both shapes ? And why 0.1 and 0.9 ?

 

I have one final question, when I run my code with dev server, sometimes, I need to refresh the page several times for my ScrollTriggers to be placed correctly. I thought there might be a mistake while loading GSAP or so. I just tried out to run it it production mode and my trigger is again placed somewhere else. What could be causing this behaviour ?

 

EDIT: Working as expected even with the SVG in one part. Thank you very much @mikel and @ZachSaucier! I'll showcase the effect on my website when the production version will be released!

 

  mounted() {
    // Initalize GSAP Plugins
    if (process.client) {
      gsap.registerPlugin(ScrollTrigger, MorphSVGPlugin, GSDevTools)
    }

    // Intialize GSAP Devtools
    GSDevTools.create()

    // Initial translations
    gsap.to('.path', {
      x: 666.761,
      y: 1030.095,
    })

    // Create a Timeline
    gsap
      .timeline({
        scrollTrigger: {
          trigger: '#shape1',
          start: 'top 20%',
          end: 'bottom 80%',
          scrub: true,
          markers: true,
        },
        defaults: { duration: 1, ease: 'none' },
      })
      .to(
        '#shape1',
        {
          morphSVG:
            'm2.00006,1l1279.99988,0l0,1767.99976l-1279.99988,0l0,-1767.99976z',
        },
        0.1
      )
      .to(
        '#shape2',
        {
          morphSVG:
            'm2.00006,1l1279.99988,0l0,1767.99976l-1279.99988,0l0,-1767.99976z',
        },
        0
      )
      .to('#shape1', { morphSVG: '#shape1', x: 666.761, y: 1030.095 })
      .to(
        '#shape2',
        { morphSVG: '#shape2', x: 666.761, y: 1030.095 },
        '-=0.9'
      )
  }

 

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