PakoVince Posted September 3, 2024 Posted September 3, 2024 Hello Everyone. I am new to GSAP in general and this is my first time using the GSAP MorphSVGPlugin and was hoping for some guidance. I have a simple eye made with two separate svg's. The one I am trying to animate is the eye-white svg. I want to create a simple blinking animation using the GSAP MorphSVGPlugin. As you can see from my plugin the initial opening animation and the random blinking animation don't seem to be working. I also originally did this with Vanilla JS and couldn't get the animation quite right, that's why I am switching to GSAP's plugin. I have a feeling I am just new to this and don't quite understand the fundamentals but if someone could help me out I would appreciate it because after several hours of this I am out of ideas. I am using HTML, SCSS, and JS. I also added the raw SVG files in my html commented out as I am not sure if that is needed or not. Thanks See the Pen dyBQbGW by calebluster (@calebluster) on CodePen.
Cassie Posted September 3, 2024 Posted September 3, 2024 Hi there. This is fun! It's also *a lot* of code for quite a simple effect. Did you use Chat GPT by any chance? Just out of curiosity, trying to get better at spotting it - It tends to overcomplicate stuff like this and loves class based setups for GSAP effects. Either way though! Would you be up for some feedback on how to approach this a little differently or would you just like input on the morphing? --- Morphing for now --- The main issue here currently is that the eyes aren't SVG, you're creating lots of divs in the JS, looks like you started off with some SVG's in the HTML though. You could do a 'blink' by just scaling on the y axis,? That's nice and simple - or you could go the route of morphing an SVG, but you'll need SVG for that! See the Pen RwzqwQz?editors=0010 by GreenSock (@GreenSock) on CodePen.
PakoVince Posted September 3, 2024 Author Posted September 3, 2024 @Cassie Thank you for the response, I have used ChatGPT to help me through this. Some of it was mine... I am not sure if that's good or bad thing to be honest 😅. I have tried the scaleY axis before and as you can see the pupil gets stretched and squished as well. It should instead keep its size and shape and the eye lid should close over the pupil. I have tried numerous attempts and different angles but can't seem to get it to feel right. I believe morphing is the way to go but if you have another approach that you think will be better I am totally down to see what you thought of! As far as the svg's go here is the svg I am currently trying to morph (eye-white svg) <svg width="180" height="103" viewBox="0 0 180 103" fill="none" xmlns="http://www.w3.org/2000/svg"> <g id="eye-white"> <g id="white"> <path d="M180 51.5C180 51.5 148.541 103 90 103C31.459 103 0 51.5 0 51.5C0 51.5 31.459 0 90 0C148.541 0 180 51.5 180 51.5Z" fill="white"/> <path d="M180 51.5C180 51.5 148.541 103 90 103C31.459 103 0 51.5 0 51.5C0 51.5 31.459 0 90 0C148.541 0 180 51.5 180 51.5Z" fill="url(#paint0_linear_1100_1900)"/> </g> </g> <defs> <linearGradient id="paint0_linear_1100_1900" x1="88.125" y1="61.3053" x2="94.6368" y2="129.013" gradientUnits="userSpaceOnUse"> <stop stop-color="white"/> <stop offset="0.814649" stop-color="#BCBCBC"/> </linearGradient> </defs> </svg> if creating divs using JavaScript isn't the way to go, can I create multiple svg's instead? I thought that's what I was already doing and the div was just the container? Here is a video of what the blinking animation should look like. I am trying to recreate it. eye-blink.mp4
Cassie Posted September 3, 2024 Posted September 3, 2024 Ah ok, yeah, that's using a mask. So something like this? See the Pen jOjQOoL?editors=0011 by GreenSock (@GreenSock) on CodePen. 1
Cassie Posted September 3, 2024 Posted September 3, 2024 If I were you I'd create multiple eyes in one SVG, that way you get the benefit of SVG's responsive stuff with zero effort. Also (in my opinion) If you can create the SVG manually and have the elements in the DOM without JavaScript that's better, less work client side and the elements will be there immediately on page load. Then just use JS to animate the bits! Easy. Bear in mind each mask will have to be unique and with a unique ID Hope this helps!
PakoVince Posted September 3, 2024 Author Posted September 3, 2024 @Cassie I think I am down the right path but I am struggling to implement this into my current code. These are the three functions controlling the eye blinking animations and the rendering of eyes. Should I change it so it renders a new svg instead of a new div? I have been messing around with this for about an hour and I guess I am not following your advice. Maybe we are moving outside of GSAP help... but any further assitence is appreicated. renderEyes() { const heroSection = document.querySelector('.hero'); for (let i = 0; i < this.eyeCount; i++) { const eyeContainer = document.createElement('div'); eyeContainer.classList.add('eye-container'); eyeContainer.id = `eye-${this.positions[i].id}`; const offsetX = this.positions[i].offsetX; const offsetY = this.positions[i].offsetY; eyeContainer.style.top = `${this.centerY + offsetY}px`; eyeContainer.style.left = `${this.centerX + offsetX}px`; const sizeMultiplier = this.isMobile ? 0.2 + Math.random() * 0.6 : 0.5 + Math.random() * 0.6; eyeContainer.style.width = `${180 * sizeMultiplier}px`; eyeContainer.style.height = `${103 * sizeMultiplier}px`; const eye = document.createElement('div'); eye.classList.add('eye-white', 'closed'); eye.style.width = `${180 * sizeMultiplier}px`; eye.style.height = `${103 * sizeMultiplier}px`; const pupil = document.createElement('div'); pupil.classList.add('eye-white__pupil'); pupil.style.width = `${70 * sizeMultiplier}px`; pupil.style.height = `${70 * sizeMultiplier}px`; eye.appendChild(pupil); eyeContainer.appendChild(eye); heroSection.appendChild(eyeContainer); this.applyOpeningAnimation(eye); } } applyOpeningAnimation(eye) { const openDelay = Math.random() * 2 + 0.5; gsap.to(eye, { duration: 0.5, delay: openDelay, clipPath: "ellipse(100% 50% at 50% 50%)", onComplete: () => { eye.classList.remove('closed'); eye.classList.add('open'); this.applyRandomBlink(eye); } }); } applyRandomBlink(eye) { const blinkInterval = Math.random() * 5 + 2; const blinkAnimation = () => { gsap.timeline({ repeat: -1, repeatDelay: blinkInterval }) .to(eye, { duration: 0.2, clipPath: "ellipse(100% 0% at 50% 50%)", ease: "power1.inOut", }) .to(eye, { duration: 0.3, clipPath: "ellipse(100% 50% at 50% 50%)", ease: "power1.inOut", }); }; setTimeout(blinkAnimation, Math.random() * 10); } I guess I am not sure how to target the dynamically created eyes and then apply this animiation to them. And this animation still is not quite working like i would like it to.
PakoVince Posted September 3, 2024 Author Posted September 3, 2024 1 hour ago, Cassie said: If I were you I'd create multiple eyes in one SVG, that way you get the benefit of SVG's responsive stuff with zero effort. Also (in my opinion) If you can create the SVG manually and have the elements in the DOM without JavaScript that's better, less work client side and the elements will be there immediately on page load. Then just use JS to animate the bits! Easy. Bear in mind each mask will have to be unique and with a unique ID Hope this helps! I re read this and I think I understand now what you mean. Instead of rendering a bunch of eyes dynamically, since there position is fixed I should just render each one in my HTML file and then animate each one using an id or class?
Rodrigo Posted September 3, 2024 Posted September 3, 2024 I think what Cassie meant is that each eye will have a different size and position. Masks in SVG are applied with very distinctive coordinates, meaning that if you have a square that is a mask and this square is at x: 0, y: 0 and has a side of 50 it will only mask that particular area of the SVG canvas, so if you apply that mask to any element that is not in that area, those elements won't get masked. https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Clipping_and_masking#masking You can simply get the path of each eye and use code to create the mask for it and then use Cassie's suggestion for scaling the mask on the Y axis.
Solution Cassie Posted September 4, 2024 Solution Posted September 4, 2024 Quote I re read this and I think I understand now what you mean. Instead of rendering a bunch of eyes dynamically, since there position is fixed I should just render each one in my HTML file and then animate each one using an id or class? Yep. You could even create the SVG in a graphics editor if you wanted so it looks exactly as you want it. You could do this with dynamically rendering things and using DOM elements and clip path if you want. Or you could dynamically add SVG elements and masks to the DOM. I'd probably just keep it simple and edit the HTML/SVG directly and just keep the animation in JS. Here's how to approach this in the way I showed you, with SVG and multiple elements. See the Pen LYKXbzy?editors=1010 by GreenSock (@GreenSock) on CodePen. note that each eye has it's own mask. Masks use ID's so they need to be unique. I've also set up all of your SVG paths to be relative, this means you can change the first two numerical coordinates (after the M) to move where that SVG path is positioned Quote From MDN - M: Move to The command "Move To" or M: It takes two parameters, a coordinate ' x ' and coordinate ' y ' to move to. The "Move To" command appears at the beginning of paths to specify where the drawing should start Hope this helps. Frankly, even if you decide to use DOM elements and clip paths, I would consider just starting over and simplifying, there's a lot of code that's very unnecessary in your solution. 1
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now