GSAP animation in STUDIO

Question:
Do Studio has the option to instal NPM GSAP animation ? Can you provide instruction please? And an example how to use it?

Product:
Wix Studio

What are you trying to achieve:
I would like to have more animation opportunity on my website using GSAP library.

What have you already tried:
Already I installed NPN package, but when I use it in the code- it doesn’t work.

Additional information:
Here is a website Installation | GSAP | Docs & Learning

1 Like

You can install any package via NPM in Wix Studio. However some packages, such as this one, might not work if they require DOM access.

The way to do use this on Wix would be to put all of your code inside a custom element including the CDN link for GSAP. You can read more about custom elements here: Wix Editor: Adding a Custom Element to Your Site | Help Center | Wix.com

1 Like

Thank you so much will chek now)

1 Like

For someone who also wants to use GSAP here is a solution using Iframe.

I add animation to the text and “Original text” replaced on “Your New Poems” maybe I was wrong somewhere in the code so please correct me.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://unpkg.com/gsap@3.12/dist/gsap.min.js"></script>
  <script src="https://unpkg.com/gsap@3.12/dist/TextPlugin.min.js"></script>
</head>
<body>
  <div id="myTextElement">Original Text</div>

  <script>
    const textElement = '#myTextElement';

    gsap.registerPlugin(TextPlugin);

    const animation = gsap.to(textElement, {
      duration: 2,
      text: 'Your New Poems',
      ease: 'power2.inOut',
      onComplete: () => {
        console.log('Animation was finished!');
      }
    });

    gsap.to(textElement, {
      duration: 2,
      opacity: 1,
      scrollTrigger: {
        trigger: textElement,
        start: 'top center',
        end: 'bottom center',
        scrub: true
      }
    });

    document.onmousemove = function (e) {
      const x = e.pageX / window.innerWidth - 0.5;
      const y = e.pageY / window.innerHeight - 0.5;
      gsap.to(textElement, {
        duration: 1,
        x: x * 400,
        y: y * 400,
        ease: 'sine.out'
      });
    };
  </script>
</body>
</html>
1 Like

Awesome! Thanks for providing a solution!

All this, just to create a type-writer-Effect? (kind of Type-writer-effect). :thinking:

Yes, as you can see in my example i used TextPlugin.min.js library to animate text. Instead this you can export any other libraries to do animation you need

great thanks for that :muscle:

Hi Anthony, how would Vitaliyk’s iFrame code look as a custom element; have gone through several options - nothing works. Should the entire html file be packed into the custom element - in the linked descriptions it seems to be a pure javascript document. What do the attributes mean - are they the CSS styles?

Yes the entire HTML file should be placed in the custom element. Can you clarify what’s meant by “what do the attributes mean”?

Hi Anthony,
I packed everything in a html-file; but when I want to import this html-file as a custom element, there is only the option to import an url or a velo file; the url-example in this section is also an js-file; the velo-file is als js; but where should I place the html.file - Is there anywhere another place to import an html-file?
Kind Regards

1 Like

You can do so here with the “Embed Code” element:

Hey guys, I will write a few more examples successfully examples of GSAP embedded code. You can copy and paste into your site and look how it works.
1)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  
  <script src="https://unpkg.com/gsap@3.12.5/dist/gsap.min.js"></script>
  <script src="https://unpkg.com/gsap/dist/ScrollTrigger.min.js"></script>
</head>
<body>
  <canvas id="image-sequence" width="1158" height="770"></canvas>
  
  <style>
   body {
  height: 300vh;
  background: #000;
}

canvas {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  max-width: 100vw;
  max-height: 100vh;
}
  </style>
  
  <script>
    gsap.registerPlugin(ScrollTrigger);
 

let frameCount = 147,
    urls = new Array(frameCount).fill().map((o, i) => `https://www.apple.com/105/media/us/airpods-pro/2019/1299e2f5_9206_4470_b28e_08307a42f19b/anim/sequence/large/01-hero-lightpass/${(i+1).toString().padStart(4, '0')}.jpg`);

imageSequence({
  urls, // Array of image URLs
  canvas: "#image-sequence", // <canvas> object to draw images to
  //clear: true, // only necessary if your images contain transparency
  //onUpdate: (index, image) => console.log("drew image index", index, ", image:", image),
  scrollTrigger: {
    start: 0,   // start at the very top
    end: "max", // entire page
    scrub: true, // important!
  }
});


/*
Helper function that handles scrubbing through a sequence of images, drawing the appropriate one to the provided canvas. 
Config object properties: 
- urls [Array]: an Array of image URLs
- canvas [Canvas]: the <canvas> object to draw to
- scrollTrigger [Object]: an optional ScrollTrigger configuration object like {trigger: "#trigger", start: "top top", end: "+=1000", scrub: true, pin: true}
- clear [Boolean]: if true, it'll clear out the canvas before drawing each frame (useful if your images contain transparency)
- paused [Boolean]: true if you'd like the returned animation to be paused initially (this isn't necessary if you're passing in a ScrollTrigger that's scrubbed, but it is helpful if you just want a normal playback animation)
- fps [Number]: optional frames per second - this determines the duration of the returned animation. This doesn't matter if you're using a scrubbed ScrollTrigger. Defaults to 30fps.
- onUpdate [Function]: optional callback for when the Tween updates (probably not used very often). It'll pass two parameters: 1) the index of the image (zero-based), and 2) the Image that was drawn to the canvas

Returns a Tween instance
*/
function imageSequence(config) {
  let playhead = {frame: 0},
      canvas = gsap.utils.toArray(config.canvas)[0] || console.warn("canvas not defined"),
      ctx = canvas.getContext("2d"),
      curFrame = -1,
      onUpdate = config.onUpdate,
      images,
      updateImage = function() {
        let frame = Math.round(playhead.frame);
        if (frame !== curFrame) { // only draw if necessary
          config.clear && ctx.clearRect(0, 0, canvas.width, canvas.height);
          ctx.drawImage(images[Math.round(playhead.frame)], 0, 0);
          curFrame = frame;
          onUpdate && onUpdate.call(this, frame, images[frame]);
        }
      };
  images = config.urls.map((url, i) => {
    let img = new Image();
    img.src = url;
    i || (img.onload = updateImage);
    return img;
  });
  return gsap.to(playhead, {
    frame: images.length - 1,
    ease: "none",
    onUpdate: updateImage,
    duration: images.length / (config.fps || 30),
    paused: !!config.paused,
    scrollTrigger: config.scrollTrigger
  });
}
  </script>
</body>
</html>
type or paste code here
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://unpkg.com/gsap@3.12/dist/gsap.min.js"></script>
  <script src="https://unpkg.com/gsap@3.12/dist/Flip.min.js"></script>
</head>
<body>
 <div class="container final">
	<div class="letter F">F</div>
	<div class="letter l">L</div>
	<div class="letter i">I</div>
	<div class="letter p">P</div>
</div>
  
  <style>
    :root {
  --color-shockingly-green: #00ff00; /* Replace with your actual color code */
  --color-lt-green: #a6f780; /* Replace with your actual color code */
  --color-blue: #00bae2; /* Replace with your actual color code */
  --color-lilac: #9d95ff; /* Replace with your actual color code */
}

.F {
  background: var(--color-shockingly-green);
}

.l {
  background: var(--color-lt-green);
}

.i {
  background: var(--color-blue);
}

.p {
  background: var(--color-lilac);
}

    
    * {
  box-sizing: border-box;
}

body {
  padding: 0;
  margin: 0;
  font-family: "Mori";
  font-weight: 300;
  height: 100vh;
  overflow: hidden;
}
.container {
  display: flex;
  height: 100%;
  width: 100%;
  justify-content: center;
  align-items: center;
  overflow: hidden;
}
.container.grid, .container.columns {
  align-content: stretch;
  align-items: stretch;
  flex-wrap: wrap;
}

.letter {
  text-align: center;
  color: black;
  font-size: 10vmax;
  font-weight: 400;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2px 6px;
}
.container.grid .letter {
  flex-basis: 50%;
}
.container.columns .letter {
  flex-basis: 25%;
}
.for, .gsap {
  font-size: 5vmax;
  color: var(--color-surface-white);
}
.for {
  padding: 2px 1.6vmax;
  font-weight: 300;
  display: none;
}
.gsap {
  padding: 2px 0;
  font-weight: 600;
  display: none;
}
.container.final .for, .container.final .gsap {
  display: block;
}
.F {
  background: var(--color-shockingly-green);
}
.l {
  background: var(--color-lt-green);
}
.i {
   background: var(--color-blue);
}
.p {
  background: var(--color-lilac);
}
.container.plain .letter {
  background: transparent;
  color: var(--color-surface-white);
  padding: 0;
}
  </style>


  <script>
   gsap.registerPlugin(Flip);

let layouts = ["final", "plain", "columns", "grid"],
  container = document.querySelector(".container"),
  curLayout = 0; // index of the current layout

function nextState() {
  const state = Flip.getState(".letter", { props: "color,backgroundColor", simple: true }); // capture current state

  container.classList.remove(layouts[curLayout]); // remove old class
  curLayout = (curLayout + 1) % layouts.length; // increment (loop back to the start if at the end)
  container.classList.add(layouts[curLayout]); // add the new class

  Flip.from(state, {
    // animate from the previous state
    absolute: true,
    stagger: 0.07,
    duration: 0.7,
    ease: "power2.inOut",
    spin: curLayout === 0, // only spin when going to the "final" layout
    simple: true,
    onEnter: (elements, animation) => gsap.fromTo(elements, { opacity: 0 }, { opacity: 1, delay: animation.duration() - 0.1 }),
    onLeave: (elements) => gsap.to(elements, { opacity: 0 }),
  });

  gsap.delayedCall(curLayout === 0 ? 3.5 : 1.5, nextState);
}

gsap.delayedCall(1, nextState);

  </script>
</body>
</html>
1 Like

Hi vitaliyk,
works perfectly in iFrame; How get you rid of the scroll bar - there are two …;

Hi Vitaly,
thank you very much for your info; I’ve been constantly trying to place the file as a custom element somehow; as an iFrame things work perfectly; unfortunately I have the problem that there is then a second scrollbar - can we get rid of that somehow?

Hi anthony , sorry for the wrong name.
thank you very much for your info; I’ve been constantly trying to place the file as a custom element somehow; as an iFrame things work perfectly; unfortunately I have the problem that there is then a second scrollbar - can we get rid of that somehow?