Popular Content

Showing most liked content since 03/10/2018 in all areas

  1. 7 points
    Hey fellow GreenSockers, I’ve seen some demos and questions lately with SVGs containing nested groups that are 10 deep and generic class names that aren’t helpful. This makes your job tougher, so I thought I’d share a few tips for better SVG exports from Adobe Illustrator. I’ve created a simple SVG with a background rectangle, some ungrouped squares, a group of circles, a group of lines and one open wavy path. Here’s the artwork with the layer panel in AI. Tip 1: IDs If you have elements that you know you’ll be targeting individually, give them an ID in AI. In this case I’ve given each of the colored squares a name. I’ve also named the wavy open path. Tip 2: Grouping If you have a group of elements that you’ll want to stagger or somehow target as a group, create a group for them. Simply select all of them and pressing Ctrl + G will make a group for you. You can also create a sub-layer and add them to it or create an entirely separate layer. Do whatever works for you. Just get them grouped before export. You can see in my layers panels I have a masterGroup around everything and then nested groups around the straight lines and circles. The elements in those groups do not need an ID as I’ll have no need to target them individually. You can also use nested groups within nested groups. Maybe a character has a group called ‘#face’, but the eyes need to be their own group within the face group. If that’s what you need for control, go for it. Tip 3: Export Settings Choose File –-> Export –-> Export As --> then choose ‘Save as type: SVG’. The directory is unimportant as you won’t actually be saving it. Choose Export at the bottom of that panel and then we’ll get to the important settings. The next screen you’ll see will be for the SVG Options. At this point you could choose OK and the file would be saved, but I find it best to click to ‘Show Code’ instead. That will launch a text editor which will allow you to copy and paste the code into your editor. Make certain the Object IDs is set to Layer Names. If not, the group names and path IDs will not come through to the SVG. The most important setting here is the Styling. If you choose: Internal CSS, you’ll get a bunch of generic class names. The IDs will still come through, but I don’t find generic class names helpful at all. Here’s what you get with that export. <svg xmlns="http://www.w3.org/2000/svg" width="1000" height="500" viewBox="0 0 1000 500"> <defs> <style> .cls-1 { fill: #333; } .cls-2 { fill: #ff0; } .cls-3 { fill: #7ac943; } .cls-4 { fill: #3fa9f5; } .cls-5 { fill: #ff931e; } .cls-6 { fill: none; stroke: #e6e6e6; stroke-miterlimit: 10; stroke-width: 4px; } </style> </defs> <g id="backgroundGroup"> <rect id="backgroundGray" class="cls-1" width="1000" height="500"/> </g> <g id="masterGroup"> <g id="nestedCircles"> <circle class="cls-2" cx="650" cy="150" r="50"/> <circle class="cls-3" cx="650" cy="350" r="50"/> <circle class="cls-4" cx="850" cy="150" r="50"/> <circle class="cls-5" cx="850" cy="350" r="50"/> </g> <rect id="greenBox" class="cls-3" x="100" y="100" width="100" height="100"/> <rect id="blueBox" class="cls-4" x="100" y="300" width="100" height="100"/> <rect id="orangeBox" class="cls-5" x="300" y="100" width="100" height="100"/> <rect id="yellowBox" class="cls-2" x="300" y="300" width="100" height="100"/> <path id="wavyPath" class="cls-6" d="M68,457c45.67-15.25,115.6-33,201-31,84.49,2,104.92,21.37,193,25,108.61,4.48,136.93-22.58,236-28,61.7-3.37,150.91,1.64,262,43"/> <g id="straightLines"> <line class="cls-6" x1="450" y1="100" x2="450" y2="400"/> <line class="cls-6" x1="500" y1="100" x2="500" y2="400"/> <line class="cls-6" x1="550" y1="100" x2="550" y2="400"/> </g> </g> </svg> For styling I prefer to set it to Presentation Attributes. Here’s what you get with that setting. <svg xmlns="http://www.w3.org/2000/svg" width="1000" height="500" viewBox="0 0 1000 500"> <g id="backgroundGroup"> <rect id="backgroundGray" width="1000" height="500" fill="#333"/> </g> <g id="masterGroup"> <g id="nestedCircles"> <circle cx="650" cy="150" r="50" fill="#ff0"/> <circle cx="650" cy="350" r="50" fill="#7ac943"/> <circle cx="850" cy="150" r="50" fill="#3fa9f5"/> <circle cx="850" cy="350" r="50" fill="#ff931e"/> </g> <rect id="greenBox" x="100" y="100" width="100" height="100" fill="#7ac943"/> <rect id="blueBox" x="100" y="300" width="100" height="100" fill="#3fa9f5"/> <rect id="orangeBox" x="300" y="100" width="100" height="100" fill="#ff931e"/> <rect id="yellowBox" x="300" y="300" width="100" height="100" fill="#ff0"/> <path id="wavyPath" d="M68,457c45.67-15.25,115.6-33,201-31,84.49,2,104.92,21.37,193,25,108.61,4.48,136.93-22.58,236-28,61.7-3.37,150.91,1.64,262,43" fill="none" stroke="#e6e6e6" stroke-miterlimit="10" stroke-width="4"/> <g id="straightLines"> <line x1="450" y1="100" x2="450" y2="400" fill="none" stroke="#e6e6e6" stroke-miterlimit="10" stroke-width="4"/> <line x1="500" y1="100" x2="500" y2="400" fill="none" stroke="#e6e6e6" stroke-miterlimit="10" stroke-width="4"/> <line x1="550" y1="100" x2="550" y2="400" fill="none" stroke="#e6e6e6" stroke-miterlimit="10" stroke-width="4"/> </g> </g> </svg> The output is much cleaner and any of those attributes can be easily controlled with CSS or GSAP. This code straight out of AI is ready to animate with no cleanup necessary. You can quickly target those group child elements for whatever you need. It’s the best of both worlds as you can get to each element for a stagger without the need for unique IDs and you can also control them as a collective. The nested circles can be targeted like this: tl.staggerFrom("#nestedCircles circle", 0.5, {attr:{r:0}}, 0.15); Or easily targeted as a group: tl.to("#nestedCircles", 1, {svgOrigin:"750 250", rotation:360}); Bottom line: Better artwork prep will make your GreenSock life easier. Proper names and grouping before you export will make your animation work go faster as you won’t have to fumble with meaningless class names and trying to group things in your code editor. That’s not to say that you can’t tweak a few names or groups – I do that all the time. But the more things you can have exported from AI correctly, the easier your coding and animation work will be. Of course, all this is just my two-cent opinion. Take from it what you will. Hopefully some of it will be helpful. Happy tweening.
  2. 6 points
    So, last night I was listening to Jack's awesome interview with egghead.io (https://itunes.apple.com/us/podcast/egghead-io-instructor-chats/id1308497805#) and he started talking about all the unexpected uses of GSAP, and it got me thinking; what have I used GSAP for? And I realized the range is from pretty typical to not so typical! My line of work is for a few large clients. So I've used GSAP on many projects; some externally facing (marketing sites) ... some for "exclusive" groups (targeted marketing), and many internally facing (for very specific groups ... members and employees). So, for the typical stuff, it's Immersive content UI indicators and helpers UX sauce For the not so typical, it get's pretty varied pretty quick! But the one project that sticks out the most is GSAP as the heart of a frontend delivery system (backed by Drupal) that drives a community cable channel for a gated resort community in GA. The Drupal side allows content managers to create and place 3 types of media; image slideshows, video, and embedded (iframed) HTML. They have control over timings and transitions which map out to GSAP, background audio playlists and ducking (the ducking is tweened with GSAP ), and asset publish/unpublish dates. For the embedded HTML ... they are calling in external sites which are slideshows, but managed by another group for another purpose; a real estate listings slideshow of active properties within the community that is displayed on a large screen in their sales office which is ... you guessed it ... GSAP! It cycles through 280 properties (+/- a few) daily; transitioning in a property image, then a delayed detail and pricing overlay slides over a portion of the image. But back to the cable channel ... every asset and configuration managed on the Drupal side is fed through custom template files that generate all the GSAP Timelines and Tweens. It has been running for years and is rock solid! So, my not so typical uses are cable channel programming (in the sense of delivering programmed content), digital kiosks, and digital signage. So, I'm curious ... what have others used GSAP for that might be a little outside the norm?! Edit: I changed the title from "What have you done with GreenSock?" to "How have you used GreenSock?" Upon reading it back some time later ... it occured to me that the title could be implying that something is wrong with GreenSock Of course there isn't!
  3. 6 points
    You can simply put repeat: -1 on the single tween within the timeline. If you want them to do something slightly different you could add a new tween to the end with new property values and repeat that one.
  4. 6 points
    The best thing to do here for any kind of help would be to distill the problem down in a CodePen. We can then fork and recommend things to fix any issues you might encounter. But, at first glance ... the get rid of the initial flash of the SVG, you can simply use some inline CSS to not display it, add "opacity: 0;" (or visibility: hidden;") to its selector in a style sheet, or load it in with JS within $('document').ready. (I'm sure there are other options I'm forgetting right now). Edit: Dang it ... did it again.
  5. 6 points
    You can use modifiers plugin to calculate values before applying them so your tween will continue playing on resize as if nothing happened. Though if your current tween inside the timeline is not active then you will have to manually set it's position on resize.
  6. 5 points
    Hi Tomsah and welcome to the GreenSock forums. The issue here lies in the fact that you're creating a new instance of the timeline each time you click on the toggle button. The real kicker is that, since the timeline is on a global scope, everytime the slideMenu method is called, GSAP says: "ok, so your calling a method that adds this instances to the timeline and since you're not specifying where you want them, I'll add them to the end". So everytime you click on the toggle button the timeline gets longer and longer and longer and longer and... you see where this is going right?? React has components lifecycle methods to help with this. You can create the timeline just once in the componentDidMount method and the timeline will be created and you can be sure that your elements are in the DOM when it happens. Just remove the call to slide menu in the toggle button click handler and add the component did mount call in your component: // add component did mount to the class componentDidMount(){ this.slideMenu(); } // remove the slide menu call in the toggle click handler toggleMenu = (e) => { e.preventDefault(); menuTl.reversed() ? menuTl.play() : menuTl.reverse(); this.setState({ menuActive: !this.state.menuActive }) } On a related note, the more purist React developers will consider using selectors like this a big NO-NO: var projects = document.getElementsByClassName('project'); React is all about doing things in a declarative way, so for getting dom nodes the so called "React way" is to use refs which in some cases can be really painful, specially if you're working with components that you didn't wrote, which makes generating and accessing the refs a bit tricky. I've been using React with GSAP for some time now and this is just my opinion, but in the case of elements that are permanently rendered on the DOM and are always there, there's no issue in doing it like you did. If that's no the case, the one thing you have to keep in mind is if a state or props change could un-mount the component being animated or elements contained in that component that could be animated, because the component could be unmounted and the GSAP instance could be still be active and you'll get an error. In that case you can use componentWillUnmount to kill the animation and prevent an error. Happy tweening!!
  7. 5 points
    Hi again @determin1st .. #1 We are helping you... #2 GSAP is already popular and does rule forever This isn't a GSAP algorithm thing . GSAP can only use what CSS property values are reported and computed by the browser, based on how you have your HTML and CSS setup. Like i said above this has to do with the CSS Spec on how height is calculated with box-sizing. As well as how the CSS box-model works. That is the nature of CSS, animating padding will affect height of its parent regardless if box-sizing is used or not Animating anything but transforms and opacity will cause jank (lost frames). Transforms and opacity do not cause a repaint and layout to be recalculated on every render tick frame. Transforms are also better for interface object, since they can animate on a sub-pixel level for silky smooth motion. Whereas padding and other non transform and opacity properties can only animate on a pixel level. And non transform or opacity CSS properties cant take advantage of hardware acceleration (the GPU) for smoother animation. You could take advantage of using transform scale instead of padding. We can only offer advise and solutions, but in the end its up to you. Happy Tweening!
  8. 5 points
    It's not really a GSAP problem, when you animate elements using className, GSAP animates whatever the difference those class create. In your case, you have box-sizing as border-box, which when you change padding causes the height to change. So in order to animate class GSAP MUST animate height, which gives your parent element inline height. So when your child element animates, parent element has it's own height instead of auto height, so it won't animate along with the child element. When animation completes, GSAP like usual will remove inline styles set while animating class which causes the jump. If you put together many such different classes that affect each other or parent and child in MANY different ways then you will see such weird behavior and GSAP can't put logic in the place to address such out of control situations. What you can do is, 1. force height auto by using !important 2. First change padding of parent element and only then animate height of child element. 3. Manually animate height of parent and when animation completes, set height back to auto. 4. Use content box as @Jonathan suggested and as you are using flexbox you don't need to set width 100%, element will set itself to 100% inside the parent.
  9. 5 points
  10. 5 points
    😭 naming my first kid after you. thank you!
  11. 5 points
    The problem is you're recreating the SplitText each time the waypoint is triggered. Create it once but trigger the Tween with the waypoint.
  12. 5 points
    For the situation where things are going wrong, we'd really need to see a simplified CodePen showing it going wrong. There are a lot of factors that could lead to this type of situation. Not much can be gleaned from a GIF. :/
  13. 5 points
    Hi @emilychews You could do something like this: var tl1 = new TimelineMax({onComplete:function(){tl1.play("start")}}); You could also make the fade tween a stand-alone tween and then onComplete play the main timeline. There are lots of possibilities. Hopefully that helps. Happy tweening.
  14. 5 points
    HI BigTreat, The point of these forums is not to have members build projects on demand, but to show them how to use various features of the GSAP API. The demo Shaun provided should be more than adequate to show how to break up an image and animate different parts. here is another demo from @Rodrigo that uses a full grid. Hit the "remove image" and "restore image" buttons a few times: To build something like revolution slider from scratch with your special requirements just takes too much custom html, css and js. Unfortunately it would be way too time consuming for people to be able to build that properly and try to teach you at the same time. Again, if you have specific questions related to GSAP and a simple demo we can work off, we are more than happy to help. Thanks for understanding.
  15. 5 points
    Nice work on the site. Looking good. We really can't investigate a standalone javascript file with 100+ lines of code. However, if you are seeing elements appear on page load before you want to animate them, most often what you can and should do is set their visibility to hidden in the css path { visibility:hidden; } and then in the js when it comes time animate them use TweenLite.set("path", {visibility:"visible"}); It sounds like your page is rendering before the js runs which is typical. So by setting the visibility to hidden via css, they will not be shown on the initial render of the page.
  16. 5 points
    Hello @determin1st and Welcome to the GreenSock forum! I would have to agree with @Acccent regarding height, padding and box-sizing. The box-sizing property takes into account the CSS Box Model. You can see in the CSS Spec for height that when you use box-sizing: border-box that the height calculation of the browser will include the height of the borders. CSS height spec: https://drafts.csswg.org/css-box-3/#the-width-and-height-properties You also have to take into consideration CSS inheritance, and the fact that anytime you change padding it will affect the height of the parent containing element even without using box-sizing. But you can get around this by: using box-sizing: content-box for div instead of border-box. No need to remove or stop using box-sizing. remove or don't use the !important declaration on the height: auto property for .box selector. !important should only be used as a last resort. Just keep in mind that anytime you animate margin, padding, width, height, and border the animation wont be as smooth as animating transforms and opacity. Since anything animated that is not transforms or opacity will cause layout to re-triggered causing jank (lost frames) due to how CSS is rendered. See Jack's @GreenSock article on this: https://css-tricks.com/myth-busting-css-animations-vs-javascript/ See CSS Triggers: https://csstriggers.com/ Also you should set the duration to a lower number. 10 seconds will be a really slow animation, and look like its either broke or nothing is happening. Setting i the duration to something like 1 second or 0.5 second like @Sahil did in his codepen will make it animate better. For full control sometimes its better to just use a fromTo() tween instead of animating classes using className. GSAP fromTo(): https://greensock.com/docs/TimelineMax/fromTo() Happy Tweening!
  17. 5 points
    Trick for something like the Slide Boxes example is to break up the image (or container with background-image applied) into subdivisions each with the original image applied as a background with the background positioned to present a seamless (an indiscernible at the start) mosaic. Here is an example I made to show this in a post quite a while back ... the idea could be applied to a greater number of sections both vertically and horizontally (or both). Edit: One thing I forgot to mention ... I made this configurable with html data-attributes. So there is a bit of preTweening logic to get those attribute values and calculate the necessary property values for the tween.
  18. 5 points
  19. 5 points
    Ya @Acccent is right about padding being animated and causing the problem. But I was wondering why that should happen? It is happening because you are using box-sizing as border-box so changing padding changes height and width of your box element. You can stop using border-box. Or you can set the parent element's height to auto !important.
  20. 5 points
    The thread you linked has all demos that only run on page load, most probably OP wanted it that way. If you want your clock to run like normal clock then it should track time every few milliseconds. So in your code you just need to update time inside your showTime function, all four variables datetime, h, m, s need to be updated so they will have new time. The clock hands are being positioned by using flexbox and yPercent as -50, that will center them at bottom end. Transform origin is set to '50% 100%' which is at the bottom of the div so it will rotate clockwise from bottom. The rotation value is post-fixed by '_cw' which makes sure that clock will only run clockwise.
  21. 5 points
    You have typo, it should be TimelineMax. Also, are you just including TimelineMax? You will need TweenLite and CSSPlugin to make it work. You can include TweenMax instead that includes both CSSPlugin and TimelineMax and few other common plugins.
  22. 4 points
    Hey Beau, There's absolutely nothing wrong with the React part of your code, that's working as expected. You're generating the refs for each DOM node and using them in the componentDidMount method. The issue has to do with the GSAP part. Right now you're creating an instance in the timeline for each SVG element but you're not passing a position parameter, so GSAP is putting each instance at the end of the timeline, therefore each element is animated in a sequential way. In order to achieve what's happening in the second codepen you can use two approaches: Option One Add every single instance at the start of the timeline using the position parameter at 0 and add random delays to each one in order to create the overlapping. const tl = new TimelineMax(); waves.forEach( item => { tl.to( item, duration, {opacity: random(), delay: randomDelay()}, 0 ); }); That zero at the end tells GSAP to add the instance at the very start of the timeline, ie, time zero. The random functions are for creating random opacity and delay numbers. Option Two Create a random position parameter for each instance based on the duration of the timeline when that particular instance is being added to the timeline or by forcing the timeline to have a specific duration using the (you guessed... the duration method). Now this might be a little more complicated because you'll have to access the duration of the timeline or set a fix duration for it. Check this from the API: https://greensock.com/docs/TimelineMax/duration() So I'd recommend using option one if you can. Finally check this from professor @Carl in order to get a great grasp of the position parameter. https://greensock.com/position-parameter Happy Tweening!!
  23. 4 points
    Hello Gsap'ers, Another post about the function so requested: Transition between pages using our beloved GSAP. But unlike the others, instead of questions, I am bringing some answers. I decided to make a Simple site template with SPA, which I believe will help everyone who is looking for solutions to these tools without having to use a framework. The activation of transitions is very simple: just click on the photos to go to the corresponding page. I hope it's useful. Ps¹: It is necessary to use the BarbaJS lib Ps²: Some settings are still missing and you can optimize the codes, best experience in Debbug Mode View Ps³: I'm using the translator hahaha https://codepen.io/Noturnoo/project/full/ZeYREp/
  24. 4 points
    Hey Tomsah, getting back to it now. Not at all, your GSAP instance will be available to every method in the global scope of your module or file. Some people like to attach the GSAP instance to the react component like this: import React, { Component } from "react"; import { TimelineLite } from "gsap"; class MyApp extends Component { constructor(){ super(); this.projectTl = new TimelineLite({paused: true, reversed: true}); } render(){ return(...); } } This allows to pass some of the components props or state properties to GSAP's callback system and stuff like that or if you need to reference a specific method from your component in a callback, or perhaps update the state using onComplete, onRevereseComplete or something like that. I don't see anything wrong with using stagger to do that. Keep in mind though that the stagger methods return a series of GSAP instances with different position parameter applied to them, based on the stagger time you provide. I recommend using React transition group only when the animation is related to the component being mounted/unmounted. For example if you want the element to be mounted before the animation starts and unmounted after the animation is complete, or if you're looking for some finer detail control over the lifecycle methods and associate them to the animation, you can use transition group. Other wise my advice is to stick with GSAP and use it in he usual way with react. Transition group might be a little too much if you're just starting with react and GSAP. If the animation only affects the elements in that component, then there's no reason to have it in a different component. If you want to target some specific callback from a parent component in the timeline, then you can pass it as a property. Finally if you need to access the GSAP instance in different components in your app, perhaps you should think about using redux, mobx or some other store solution in order to access the timeline in different components. Now I have never used redux to store a GSAP instance, but I can't think about a reason against though. Happy Tweening!!!
  25. 4 points
    Hi @Zaperela Welcome to the forum. The reason your demo wasn't (kinda sorta) working was the onComplete was in the wrong spot and being called three times. It is a bit confusing, but for stagger tweens the onCompleteAll goes after the stagger number. So what you were seeing was the infinite circle repeat timeline starting before it should have which caused an overwrite. More info in the stagger docs: https://greensock.com/docs/TweenMax/static.staggerFrom() In addition to @Shaun Gorneau's demo or moving that onComplete call on the stagger, you could also move the onComplete to the vars of the cs timeline. Lots of options. Hopefully that helps. Happy tweening and welcome aboard.
  26. 4 points
    Try this .. I'm animating the the x property (vs css: { transform: 'translateX'}) and the rotation property (again, vs css:{ transform: 'rotate'}) and it seems to have much better results.
  27. 4 points
    Hi @alfianridwan, no problem at all. I'll break down a few things that are happening here ... First and foremost ... I'm using jQuery to make some tasks much easier. If you're new to JS, you may or may not know the ins and outs of cross-browser compatibility. In short ... jQuery fights that battle for us. So your questions and my answers are addressing jQuery $('document').ready( function(){ Is just waiting for the DOM to be ready before we do anything. $('.blog-card img').each( function(){ So .each() is a jQuery method that allows us to iterate over the matched set ... in this case, all elements returned with the selector ".blog-card img". So what happens here is the DOM is scoured for all <img> within elements with the class ".blog-card". Now that that images are returned as a set, jQuery's .each() will iterate over each one to perform all the actions with the .each(){ }. In plain english, here is what is happening within that, Find the <img>'s nearest (up the DOM tree) enclosing element with the class "blog-card". This is the card that the image belongs to. Within that card, inject an element before all its children ... a new div using the current <img>'s (i.e. the <img> currently being referenced as we iterate with .each() ) src as the css background-image move the <h2> within the card to now be a child element of the new <div class="blog-card-background"> Add in a new div to represent the "close" X in the top of the expanded card. remove the <img> from this card (we no longer need it) Now, for the why to all the above We do this just to make a simple reference rather than asking jQuery to look up the tree many times. In this particular case, the hit is negligible. But it's good practice that if you are going to ask for something multiple times ... simply create a variable to hold it. For me, positioning and animating a div with a background image applied is much easier than positioning and animating the <img> itself. For example ... applying an image as a background with background-size: cover and background-position: 50% 50% tackles so many issues on its own when dealing with responsive images! Because the new <div class="blog-card-background"> is positioned relatively, it allows us to stick the (now) absolutely positioned <h2> to the bottom of the image (with padding) by using the CSS bottom property without any concern for how many lines the <h2> may be. We have to be able to close it! No use in keeping it in the DOM ... we are now using that image as a background for the <div class="blog-card-background"> You'll notice we did a lot of manipulation at this point with jQuery. But why? Why not just do all of this in the markup? Well, for several reasons. If javascript isn't available (or some other script causes it to fail), we want the user to have something that a. looks approriate and functions as expected (i.e. click a blog card and navigate go through to that blog post as a new web page) b. doesn't present interface elements that perform no action My basic rule ... if Javascript is required to interact, then Javascript is responsible for creating the element. So make sure it's usable without JS. $('#some-blog').on( 'click', '.blog-card.inactive', fucntion(){ ... This one is a bit nuanced, but I'll explain here. We want things to happen when something is clicked. Someting "A" when clicked in a "card" state and something "B" to happen when clicked in an open state. Now, I could have simply used ... $('.blog-card.inactive').click( function(){ ... Buuuuut, that does something a bit unexpected. A unique click handler get's bound to each matching element from that point forward .. regardless of losing the "inactive" class down the road. You might say, "that's OK, we're using a different element to close the open state.". And that's true ... but, clicking anywhere else anywhere within, in this example, the open blog post would still cause this handler to fire. We could do some detection to remove this handler while in an open state and rebind after. But it's so much easier to do $('someSharedParentElement').on( 'click' , 'target', function(){ ... because it creates a single handler for all matching elements AND allows the handler to be bound to dynamically created elements down the road! Think of a blog with infinite scroll, for example. And, when the "blog-card" has its class changed from "inactive" to "active", this event handler is no longer bound. When it goes back to "inactive" it is bound Noice!
  28. 4 points
    Hey Beau, I understand the frustration. The reality of things is that React works in a declarative way and the discourage to use string refs and find dom node has mostly to do with some specific issues and some excessively paternal approach by the React team. The paternal issue comes from the fact that the deprecation of string refs has to do with some edge cases and because of a few things that could go wrong the entire API is changed for everyone. Take GSAP for example, Jack does His best to ensure that GSAP serves as many scenarios as possible but never compromises performance, file size nor legacy support, and when those changes do come, believe me there is a lot of questions and samples going around before the decision is made. But that's how GSAP works and perhaps we've been a little spoiled by that, but that's how serious Jack and Carl are about how this works. IMHO there's nothing wrong with using string refs and fond dome node if you know what you're doing. At some point developers have to take responsibility for the code they write and not expect that the frameworks make all kind of loops and hoops to ensure that every blunder is supported. As I mentioned in another post yesterday this is some very specific issue, because you have to take in consideration the lifecycle of your app and it's components in order to make the best decision. There are some cases that you can safely reference the elements using the classic get element by id if, when you create the variable you're sure that the DOM node will be rendered in the app. In other cases if you're not sure is better to use refs. This simple codepen goes into the detail of how refs are accepted today: In the console you'll find an array with all the rendered elements that can be used with GSAP. This is the so called "React way" for referencing DOM nodes. Now the pain comes when you're using a third party component that doesn't expose the DOM node, like this case: import ThirdParty from "third-party"; const elements = ["label-1", "label-2", "label-3", "label-4"]; class MyApp extends Component{ constructor(){ super(); this.elements = []; } render(){ return <div> { elements.map( e => { <ThirdParty key={e} ref={ node => this.elements.push(node) } /> }) } </div>; } } In this case the array will have a collection of the four React components being rendered and not the nodes being added to the DOM. In this case find dom node comes in handy because it allows to reference the actual element in the screen. If the plan is indeed remove find dome node from ReactDOM then things will get more complicated, because developers will have to bake their own version of this component in order to expose the ref to the DOM node or the developers of third party components will have to come up with some way to find those references. One of the simple ways to do this could be for components to accept an id property in order to later reference that and get access to the DOM node. Finally don't get discouraged from using GSAP with React, there are ways to use it and the more is being used, the React team will become aware that to create complex animations and rich environments GSAP is the way to go and they'll have to come with some way to actually gain access to the dom elements. Right now we have to keep using this type of approaches with the hope that things will change and become simpler in the future. Also keep in mind that the article you mention was written when React was in version 15 and string refs and find dom node were not deprecated and that wasn't a long time ago, unfortunately this world moves way to fast and doesn't wait for anyone... Happy Tweening!!!
  29. 4 points
    Had a bit of time this afternoon ... here is a quick crack at it. A few notes ... the HTML, CSS, and JS are setup to support progressive enhancement. If JS isn't available, you'd simply get a tile with a background image and an overlaid title. Clicking would push through the the target URL. jQuery does some restructuring to get better control from both a CSS and a Tweening perspective. I'm also using jQuery to handle the simple click events. I'm sure adjusting some timings (or even tween orders) would produce a better result ... I haven't gotten there yet Big note ... I wouldn't look it here in the embedded CodePen ... I would go out and view it in a narrower viewport.
  30. 4 points
    Yep. It's a LOT more complex than you might imagine due to performance optimizations and the flow of code. But I'll spend some more time looking for a way to make it happen (no promises). You can definitely work around it, even in your second example. Here I use an "indexify()" function to which you feed in the elements and the modifiers object and it'll do all that work for you automagically... var elements = [ "#element1", ".stage h1", ".slider .slide span" ]; TweenMax.to(elements, 1, { x: 50, modifiers: indexify(elements, { x: function(value, target, index) { console.log(index, target); } }) }); function indexify(elements, vars) { var a = [].slice.call(document.querySelectorAll(typeof(elements) === "string" ? elements : elements.join(", "))), wrap = function(func) { return function(value, target) { func(value, target, a.indexOf(target)); } }, p; for (p in vars) { vars[p] = wrap(vars[p]); } if (vars.scale) { //scale is an alias for scaleX and scaleY vars.scaleX = vars.scaleY = vars.scale; delete vars.scale; } return vars; } Obviously that indexify() function could be reused as much as you want. Does that help?
  31. 4 points
    This isn't nearly as easy as it may seem due to a lot of performance optimizations and the flow of code. I'll look into it more though and see if there's a way I can do it efficiently, but no promises. I can do that copying of "scale" to "scaleX"/"scaleY" in the next release pretty easily, though, so that should resolve your issue @kreativzirkel. In the mean time, though, here's a way to use a simple function to reduce that code sample you provided: function scaleVars(vars) { vars.scaleX = vars.scaleY = vars.scale; delete vars.scale; return vars; } var tl = new TimelineMax() .from(".elem1", 1, { scale: 0, xPercent: 100, modifiers: scaleVars({ scale: function(value, target) { // some logic return newValue; }, xPercent: function(value, target) { // some logic return newValue; }) } }) .from(".elem2", 1, { scale: 0, xPercent: 100, modifiers: scaleVars({ scale: function(value, target) { // some logic return newValue; }, xPercent: function(value, target) { // some logic return newValue; }) } }) .from(".elem3", 1, { scale: 0, xPercent: 100, modifiers: scaleVars({ scale: function(value, target) { // some logic return newValue; }, xPercent: function(value, target) { // some logic return newValue; }) } }) .from(".elem4", 1, { scale: 0, xPercent: 100, modifiers: scaleVars({ scale: function(value, target) { // some logic return newValue; }, xPercent: function(value, target) { // some logic return newValue; }) } }) .from(".elem5", 1, { scale: 0, xPercent: 100, modifiers: scaleVars({ scale: function(value, target) { // some logic return newValue; }, xPercent: function(value, target) { // some logic return newValue; }) } }); Does that help?
  32. 4 points
    I think PointC's version is fantastic and meets all the goals. However, this is a fun challenge so I thought I'd just share an alternate approach that adds a touch more randomness and TWICE as many boxes The idea is that there is a function that animates a thing with random duration, delay, and repeatDelay. When the animation finishes it calls that function again and passes in the same thing and animates it again with totally new and random values.
  33. 4 points
    Hi @beau_dev If I understand your question correctly, I think a quick loop through the elements would be an easy way to make it happen without jQuery. Here's a quick example that should be easy to understand. It randomizes the duration and a delay and yoyos the opacity. You could also add each tween to a timeline if you need that kind of control.Hopefully that helps. Happy tweening.
  34. 4 points
    Look at @Shaun Gorneau work with that shiny new Moderator Badge. Seven minutes from question post to answer. Nice work. Congratulations on the promotion. You've earned it. Now you'll have to test your quick-draw question answering speed skills against a super fast guy like @Sahil.
  35. 4 points
    In addition to the usual suspects of ScrollMagic and Waypoints, you may want to check out The Intersection Observer. It's come up in a few threads: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API Hopefully those links give you some ideas. Happy tweening.
  36. 4 points
    Maybe try animating top: 90% to top: 50% instead? The top CSS rule takes priority over bottom, so maybe there's some rule inheritance somewhere that messes things up... It's really hard to tell though. It looks like the div is animating to the position where it would be if it had position: relative, maybe that's worth investigating as well. Hard to provide any more help without a look at the code, like Shaun said.
  37. 4 points
    A quick and dirty way would be to put simple click targets in those positions and allow a click event to push the knob to those positions. A little CSS to position and hide ... and a little JS to update the draggable object. I'm sure a better method would be to use the Draggable's onClick method to get mouse coordinates and determine the click position relative to the knob and logic to determine the closest snap point. But for a few simple snap-to points, a couple of clickable elements might be what you're looking for. EDIT: Based on your positioning things ... you may want to look at this in CodePen itself ... seems some relative positions are causing the clickable elements to not align. EDIT 2: Reduced your pen down to simplify it for positioning.
  38. 4 points
    Thanks Sahil! It was a completely unexpected surprise that turned around a ho-hum morning!
  39. 4 points
    You can also add a global handler something like this that will prevent the page from being visible until all assets are ready. In head: <style type="text/css"> .js {visibility: hidden;} </style> <script type="text/javascript"> document.getElementsByTagName('html')[0].className = 'js'; </script> Before closing body tag: <script> // assuming jQuery was loaded with assets window.onload = function() { $('html').removeClass('js');}; </script> Discussion on SO of various page load event handlers: https://stackoverflow.com/questions/807878/javascript-that-executes-after-page-load
  40. 4 points
    You figured that out by looking at an Android forum? There's not a lot of people that would be able to make that connection 👏👏👏 2D canvas/graphics libraries are used everywhere, and I do mean everywhere. In fact, everything you're looking at is being generated by one of those libraries. To render the page, all the HTML, CSS, and SVG in the DOM gets reduced down to a bunch of canvas draw calls. What's really interesting, and what you just discovered, is that the HTML5 canvas uses very similar API. For fun, I made a Skia Fiddle to compare code with. Skia is the graphics engine used in Chrome, Firefox, and a bunch of other stuff like Sublime Text. Skia https://fiddle.skia.org/c/edce0208cfe8e7b4d0bf1ba0ac4d6af9 void draw(SkCanvas *canvas) { canvas->clear(SK_ColorBLACK); const int centerX = 400; const int centerY = 400; SkPath rocket; SkParsePath::FromSVGString("M2.35,24.5...", &rocket); SkPaint fillPaint, strokePaint; fillPaint.setAntiAlias(true); strokePaint.setAntiAlias(true); strokePaint.setStrokeWidth(2); strokePaint.setStyle(SkPaint::kStroke_Style); SkColor colors[][2] = { {0x8888ff00, 0xff88ff00}, {0x880088bb, 0xff0088bb} }; SkScalar rotation = 0; for (auto color: colors) { fillPaint.setColor(color[0]); strokePaint.setColor(color[1]); canvas->save(); canvas->translate(centerX, centerY); canvas->rotate(rotation); canvas->scale(3, 3); canvas->drawPath(rocket, fillPaint); canvas->drawPath(rocket, strokePaint); canvas->restore(); rotation -= 90; } } HTML5 Canvas https://codepen.io/osublake/pen/gebqVK function draw(context) { const centerX = 400; const centerY = 400; const rocket = new Path2D("M2.35,24.5..."); // 32-bit RRGGBBAA color const colors = [ ["#88ff0088", "#88ff00ff"], ["#0088bb88", "#0088bbff"] ]; let rotation = 0; context.lineWidth = 2; for (let color of colors) { context.fillStyle = color[0]; context.strokeStyle = color[1]; context.save(); context.translate(centerX, centerY); context.rotate(rotation); context.scale(3, 3); context.fill(rocket); context.stroke(rocket); context.restore(); rotation -= Math.PI / 2; } } Skia uses C++, so there's obviously going to be some syntax differences. Outside of that, the only difference is that Skia uses paint objects to hold style related stuff, like color. That's really nice because you don't have to keep changing the context. I've seen talks about adding paint objects to the HTML5 canvas, but I don't know if it's still being considered. I'll be back later, but check out Path2D. It will let you save a path. See how I'm using it in that demo for the rocket path. https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
  41. 4 points
    After a while, I finally know what was frustrating me and why the stuff I wrote in functions wasn't responding, and all the stuff with removing, adding, controlling wasn't working as it should - if you will ever have a problem with controlling timelines held in functions be sure to first declare a variable and then assign it to the function that returns your timeline...^_^'' @Dipscom wrote something like var returned_tl = tlReturned(); a tiny bit of code somewhere in the forums and huge thanks for that - now GSAP magic works the same regardless it's in function or not and my code is much much cleaner Thanks again for the help @Sahil
  42. 4 points
    Hi @Cristo Welcome to the forum. You can click the magnifying glass in the upper right part of the top menu and enter your search terms. If that doesn't get you what you need, you can also Google the forum pages. To limit your search to GreenSock site results, you can do something like this: site:greensock.com your search terms If you want to search for an exact phrase, (say something like timeline objects), you'd put your search terms in quotes: site:greensock.com "timeline objects" Hopefully that helps. Happy tweening.
  43. 4 points
    Hi @Bigtreat We'd ask that you please not request help from members through the private message system. It's best to keep all the questions and answers in the public thread so others can follow along and learn too. If you want to contact a member about hiring them, then certainly reach out via PM. I know @Sahil gave you some solid advice about getting started here: Have you gone through the learning page here? https://greensock.com/learning The blog also has wonderful articles to help you get a better understanding of GSAP. https://greensock.com/blog/ You can also use the forum search feature (upper right) to find threads talking about the parallax effect. (We've had several.) We don't have the resources to take you through an entire project from start to finish so we ask that you get something started and post a specific GSAP related question when you get stuck. Simplified demos will get you the best answers. We're always happy to help with GSAP related questions and problems. Thanks.
  44. 4 points
    No problem at all. Glad it's resolved. And I prefer "your majesty".
  45. 4 points
    Holy Cow. Defining xmlns for the xlink totally fixed this. Thank you oh mighty Overlord. I will never forget you.
  46. 4 points
    I'm not an expert on this, but I believe browsers sandbox SVG files for security reasons, so you can't expect them to be able to reach out into the parent document and find other objects (like GSAP). I believe you've got to load the JS into your SVG file, like: <script type="text/javascript" xlink:href="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.4/TweenMax.min.js" /> (inside the SVG) (and you'll need to load DrawSVGPlugin too of course) Or you could inline GSAP, of course, but that seems kinda wasteful to me. Also, you need to define an xmlns for the xlink in your <svg> tag, like: <svg id="AnimationsContainer" width="200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-250 -250 500 500"> Does that help?
  47. 4 points
    Hi Voo, .delay() can be a bit misleading – when you call blue.play().delay(0.5), you set the initial delay for blue to 0.5s. When you call it again later on, you set that initial delay to 0.5s again, which doesn't do anything because it was already set to that value. The reason it doesn't wait again is because .delay() is only for the initial delay, that is the time before the animation plays for the first time. It is not part of the animation itself (which is why it isn't affected by the animation's timeScale). If you reverse the animation and then play it again, it will go to its starting position, but that initial delay is already 'gone'. If you want a tween that always starts after 0.5s, an easy way would be to use the position parameter like this: blue.to('.blue', 1, {x: '500%', ease: Power4.easeIn}, 0.5); If you want to add a waiting period at the end of an animation, you can add an empty tween like this: red.to({}, 0.5, {}); (or add a label) That way, when you reverse the animation, it will first "tween" nothing for 0.5s, then proceed with the tween that comes before that. (PS: this is a very minor thing, but maybe not everyone is a guy here )
  48. 4 points
    This happens because you are changing the padding, so GSAP animates the element to its new padding based on the size of the inner box before it started animating. If you remove the padding rules from the CSS, you'll see that the container box animates adequately. If you'd still like to keep the padding, I guess you could specify the height either with CSS or JS, maybe by calculating it on the fly?
  49. 4 points
    This is my workflow as well. Although I do the same thing with Affinity Designer to avoid the Adobe tax. It works perfectly. Grouped layers in Designer get ID'd correctly in the SVG output. Some things don't output well in either Illustrator or Designer though when it comes to SVG output. Layer effects are dicey. Some fill types are quirky. Transform Matrix does odd things. Etc. The trick is to use just simple lines and fills without using more advanced vector features/effects.
  50. 4 points
    You need to use feDisplacementMap and pass two sources to it using in and in2, which would be result of turbulence and the source graphic.