Rodrigo last won the day on July 26 2016

Rodrigo had the most liked content!


  • Content count

  • Joined

  • Last visited

  • Days Won


Rodrigo last won the day on July 26 2016

Rodrigo had the most liked content!

Community Reputation

2,006 Superhero


About Rodrigo

  • Rank
    Advanced Member

Contact Methods

  • Skype

Profile Information

  • Gender
  • Location
    Santiago - Chile

Recent Profile Visitors

20,372 profile views
  1. Rodrigo

    Drag svg file along with path

    You're setting as the target of the Draggable instance the element with the id="box". It should be the patch element. var line = document.querySelector("#line"); var patch = document.getElementById("patch_218_454"); var x1 = line.x1.baseVal.value || 0; var y1 = line.y1.baseVal.value || 0; var x2 = line.x2.baseVal.value || 0; var y2 = line.y2.baseVal.value || 0; var C = x2 - x1; var D = y2 - y1; var distSq = C * C + D * D; /* TweenLite.set(patch, { xPercent: -50, yPercent: -50 }); */ var draggable = new Draggable(patch, { allowContextMenu: true, liveSnap: { points: getClosestPoint } }); /* TweenLite.set(patch, getClosestPoint({ x: draggable.x, y: draggable.y })); */ draggable.update(true) That allows the patch to be dragged, but it throws the live snapping far from the line. I'm not sure why is that, perhaps the fact that you have an <svg> tag inside another <svg> tag. The code is making the calculations in the context of the outer SVG tag and then those are being applied in the inner SVG tag, which causes that issue. I haven't seen something like that before; any particular reason for that? Also you don't need throwProps for this, so you can remove it from the Draggable instance. Happy Tweening!! PS: Well... basically what Blake said , He beat me to it
  2. Rodrigo

    Drag svg file along with path

    @OSUblake How geometrically elegant, a true heir of Pythagoras
  3. Rodrigo

    Drag svg file along with path

    Hi, I had to deal with something like this about a month ago. First you forgot to include jQuery in the pen's resources. Then in order to enforce the right bounds, you need to find the dimensions of the path you want to use as track using getBBox() and use those in the Draggable instance, but for that you have to line up the patch and the track correctly, otherwise it looks a bit funny: var bounds = document.getElementById("line2").getBBox(); var R = Draggable.create( "#patch_218_454", { type: "x,y", bounds:{ minX: 0, maxX:bounds.width, minY:-5, maxY:bounds.height - 5 }, onDrag: function (e) { console.log(e.offsetX); console.log(R); } }); Then in order to limit the patch movement to the path you want to use, don't use the Draggable instance on the patch but in a dummy object, then simply trigger the instance using the patch and update an independent GSAP instance using the onDrag callback. This sample from one of our heroes @Diaco shows how to do that: Here in the docs, more specifically in the config object properties you'll find the trigger property. Hope this helps. Happy Tweening!!!
  4. Ever heard of conditioning?? this guy has all the answers EDIT: I didn't saw the Bugs Bunny shape there!!!! You deserve like 500 likes for that!!!
  5. Hi Gilbert and welcome to the GreenSock forums. First try to avoid string refs, they are deprecated since React 15.x. Use the ref callback instead: In the constructor create an empty array to add the elements as you call the render title method: // this is inside the component's class constructor(){ super(); // just an empty array, we'll use the ref callback to add the DOM elements this.titles = []; // the rest of your constructor code here } Then, IMHO I don't see the need for two methods to render each title, you can run all your logic in just one and use the ref callback to add the element to the array, like that you'll have a predictable way to add your elements to the timeline. renderTitle(e) { const ATitle = titles[e.component]; return( <div className="box" key={e.component} ref={e => this.titles.push(e)} > <ATitle size={e.size} text1={e.title} text2={e.subTitle} open="false" style={{ position: "absolute", x: " +e.x+" , y: " +e.x+", "fill": "orange" }} onComplete={this.handleComplete} /> </div> ) } The key is the ref callback which has the DOM node as the argument, then the DOM node is added to the titles array. Then you can access the titles array and create your timeline: componentDidMount(){ const { tl, titles } = this; titles.forEach( (e,i) => { this.refs[e.component], 1, {x: e.size, y: e.size/3 , scale: 0.5, opacity: 0.5}, .5 * i ); }); tl.reverse(); } This is a better and more actual approach to write your app. Now to the sequencing part of your app. I'm a bit puzzled, because react-titles does creates either a GSAP instance or uses react-motion for the animations. If you want to control those animations, you'll have to fork the repo and create an API that exposes some way to do what you need, because react-titles starts as soon as the component is mounted. For the initial position, what I saw in the repo is that the code uses fromTo instances, which create an immediate render, which sets the initial position of the elements being animated, so no matter what you do react-title will enforce those initial values passed in the props of the component's instance. Such is the nature of re-usable React components, you have to work with whatever the creator of the component is giving you. Also keep in mind that the open prop passed to the component is a boolean and you're passing a string "false". Try using a boolean instead: <ATitle size={e.size} text1={e.title} text2={e.subTitle} open={false} style={{ position: "absolute", x: " +e.x+" , y: " +e.x+", "fill": "orange" }} onComplete={this.handleComplete} /> Another option is hack into the code of react-titles and create a way to control the animations. Honestly if I was you, I'd take the animations being created by react-titles and bake my own solution for the particular needs of the project. The GSAP code in react-titles is good and there are no issues there and unfortunately we can't spend a lot of time figuring out how GSAP based tools work and how they're used. I saw that you started an issue in the repo, hopefully the creator(s) of the tool can help you more than I can and they can add a way to keep the tweens paused when mounting and give more control over them, as well as creating more complex sequences. Happy Tweening!!!
  6. Rodrigo

    React Native Support

    Hi and welcome to the GreenSock forums. Unfortunately, as far as I know, GSAP can't be used in React Native, due to the way RN actually works. Keep in mind that RN works in a different way than Cordova, PhoneGap and other tools. What I know is that RN uses the Animated API, which jumps directly on the native way for creating animatios. GSAP uses the browser's requestAnimationFrame event to create the animations. Now take this with a grain of salt, because my knowledge lays on the web side of React and not in RN. Although I made a google search for RN animations with JS libraries and there weren't a lot of results. Perhaps you could try going to Reactiflux and go into their discord channel and ask around for animations using an external framework: Sorry that I can't be more helpful, but it seems that for the foreseeable future just the existent APIs are the ones you'll have to work with. Other options involve using C, JAVA or Swift, like Flutter. Then there's Lottie that allows to export After Effects animations but that's a completely different scenario. Happy Tweening!!!
  7. Rodrigo

    GSAP does not work

    Ohh... how swiftly you rose... and now thou has fallen....
  8. Rodrigo

    GSAP does not work

    Well... aren't you a big box of surprises!!!
  9. Rodrigo

    TimelineMax not defined

    Hi, TimelineMax by itself is not going to work, because GSAP's core is not available. Add TweenLite before TimelineMax and it should work: <!DOCTYPE html> <html> <head> <title>TimelineMax test</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <script src=""></script> <script src=""></script> </head> <body> <script> var tl = new TimelineMax({});"body", .3, {backgroundColor:"blue"}); </script> </body> </html> Happy Tweening!!!
  10. Rodrigo

    React support

    Hi and welcome to the GreenSock forums. First I love the avatar!!!! Second, yep there's a lack of documentation for GSAP + React / React Native out there. Unfortunately tools like Pose and Transition Group help only with simple tasks, when the time comes to make complex animations things get a bit complicated. There is (actually was) a tool for that ( but is no longer maintained so not much to do there. Also in this scenarios we run into React's declarative nature, which is quite unique and the fact that the React team doesn't help too much in getting access to the DOM elements. In fact I've read some comments and Tweets from Dan Abramov (creator of Redux and part of the React and React Create App teams), where He declares that accessing the DOM nodes should be a no-no and that is anti-pattern. Obviously Dan hasn't done a single complex animation in His life on a React app ;). Back to the issue the most robust tool I use (and that's just one guy's opinion) is React Transition Group (RTG), because it takes care of the DOM operations and reconciliation when the components get mounted, unmounted, updated, etc., so with it we can take care of only creating our GSAP code. The caveat (there's always one of those, right?) is to create the transition component to use GSAP in it, but once you get the hang of it you'll see is not too complicated because RTG's API is quite simple. Here are some samples using GASP with RTG: Simple list of elements (this has some commented out code, that's the code to use... [sigh] CSS Transitions, but the sample actually uses GSAP) Mount / UnMount a component Draggable Rotation Now the first two need a major update because they're using React 15.x, when you could still use string refs for the dom elements. Now a days you need a different approach for it, like this: But updating it shouldn't be too complex. If you still find some issues on the road, please don't hesitate to create a codepen or sandbox to get a look at it. The idea is to encourage people to keep using GSAP and spread it as the standard for web animations. Happy Tweening!!!
  11. Rodrigo

    Draggable Loop with Vanilla JS and Greensock

    Mhh.. I see you're rehearsing your stand up comedy routine
  12. Rodrigo

    Draggable Loop with Vanilla JS and Greensock

    Sorry I didn't went through all the code. What's the idea here, have a horizontal stripe of images to drag/throw and snap I presume?. My knowledge of flex is quite limited (working on backend and PIXI for too long does that to you ) so I'd suggest to look how that can be done. Also consider that the element have an absolute position. Also how the progress is being calculated via the drag/throw update and being applied. I managed to get something barely useful with this css: .box { flex-grow:0; } .boxes { display: flex; position: relative; flex-direction: row; } This creates a horizontal stripe with the images, but there are some issues with the progress of the tween. Unfortunately I don't have a lot of time to go through the and get what the issue is, but my suggestion is to first get the animation to work, like this: And when that's working like that go to the draggable part of it to calculate and apply the progress. Use this pen from the GreenSock collection to understand how the modifiers plugin works: Happy Tweening!!
  13. Rodrigo

    Draggable Loop with Vanilla JS and Greensock

    Hi, This code makes it work: var $overflow = document.getElementById("overflow"); var $viewport = document.getElementsByClassName("viewport"); var $wrapper = document.getElementsByClassName("wrapper"); var $boxes = document.getElementsByClassName("boxes"); var $proxy = document.getElementById("box1"); var numBoxes = 4; var boxWidth = 350; var boxHeight = 250; var imgWidth = boxWidth - 6; var imgHeight = boxHeight - 14; var viewWidth = $viewport[0].offsetWidth; var wrapWidth = numBoxes * boxWidth; var progress = 0; var xMin = 0; var xMax = 0; TweenLite.set([$wrapper, $viewport], { height: boxHeight, xPercent: -50 }); TweenLite.set($boxes, { left: -boxWidth }); var animation =".box", 1, { x: "+=" + wrapWidth, ease: Linear.easeNone, paused: true, repeat: -1, modifiers: { x: function(x, target) { x = x % wrapWidth; = x - boxWidth > viewWidth ? "hidden" : "visible"; return x; } } }); Draggable.create($proxy, { type: "x", trigger: ".wrapper", throwProps: true, onDrag: updateProgress, onThrowUpdate: updateProgress, snap: { x: snapX } }); $overflow.onchange = applyOverflow; $(window).resize(resize); function snapX(x) { return Math.round(x / boxWidth) * boxWidth; } function updateProgress() { animation.progress(this.x / wrapWidth); } function resize() { console.log($viewport[0].offsetWidth); viewWidth = $viewport[0].offsetWidth; animation.render(animation.time(), false, true); } function applyOverflow() { if($overflow.checked){ TweenLite.set(".wrapper", {overflow:"visible"}); }else { TweenLite.set(".wrapper", {overflow:"hidden"}); } } Just needed to remove some jquery methods and use onchange instead of on in the checkbox. Happy Tweening!!
  14. Rodrigo

    Countdown with rolling numbers

    Hi and welcome to the GreenSock forums!! A looooooooong time ago I made this for another question in the forums. It uses the draggable tool to move the numbers but you could easily use it to get started with what you need: I made a fork of it and changed a bit to simulate what you need: Hopefully this helps. Happy Tweening!!!
  15. Hi, This seems to do what you're trying to achieve or what I understood at least. I'll go in as much detail as possible. First I got rid of almost all your code, nothing against your code, is just my unwillingness to go through a lot of code I didn't write, normally is faster to just rewrite things. I went as much as possible in the react way of doing things. So I removed the global scope timeline for the menu and added it to the component, like this: class App extends React.Component { constructor(props) { super(props); this.state = { menuExpanded: false, targetPanel: null }; // menu items this.menuItems = []; // menu tween this.menuTl = new TimelineLite({paused:true}); } componentDidMount() { this.createMenuTween(); } // bring the project to view// createMenuTween = () => { this.menuTl .staggerTo(this.menuItems, 1, { cycle: { x: ["56%","64%", "72%", "80%", "88%"] }, ease: Power2.easeInOut }) .reverse(); }; render() { return ( <div className="main"> <button onClick={this.toggleMenu} id='toggleMenu'> menu </button> <div className="projects" ref="projects"> {, index) => { return ( <div className="project" ref={ project => this.menuItems.push(project)} key={index} id={`project-${index + 1}`} onClick={this.togglePreviewCase.bind(null, index)} > <span> {} </span> </div> ); })} </div> </div> ); } } This is the basic setup to render the DOM elements. As you can see I attached two properties to the component, the menu timeline and a menu items array: // menu items this.menuItems = []; // menu tween this.menuTl = new TimelineLite({paused:true}); Also I added the menu items to the array using the ref method on each menu item: <div className="project" ref={ project => this.menuItems.push(project)} key={index} id={`project-${index + 1}`} onClick={this.togglePreviewCase.bind(null, index)} > <span> {} </span> </div> The idea behind using an array is that GSAP can use them without any problems, weather is to create a stagger animation or a common animation for all the elements. Since you're using the cycle property for each element there's no need to use a stagger method, just a regular to() instance that animates all the elements at the same time. The rest is pretty much self explanatory and we've been through that in a different topic. The main idea, as mentioned in my previous post, was adding a property in the state to track if the menu is expanded and if there's a menu panel expanded as well (menuExpanded, targetPanel). Then is the toggle method, we'll go into the basic stuff of this and then get back to it: toggleMenu = e => { const { menuExpanded, targetPanel } = this.state; // if a panel is expanded, collapse any panel and then reverse // the menu timeline if ( targetPanel !== null ) { return this.menuItems.slice(0, targetPanel + 1), 1, { x: '+=50%', ease: Power2.easeInOut, onComplete: () =>{ // toggle the state property this.setState({ menuExpanded: !menuExpanded, targetPanel: null }); this.menuTl.reversed( menuExpanded ); }// on complete });// tweenlite instance } // toggle the state property this.setState({ menuExpanded: !menuExpanded }); this.menuTl.reversed( menuExpanded ); }; The basic stuff are the final two lines, basically the state property is being updated and we're using that to trigger the animation in order to keep consitency between react and the timeline reversed state. I'm using the reversed property because the timeline is paused in the constructor and when is created instead of playing it is reversed, with that we can toggle the reversed property of the timeline to toggle it's direction. Then the part of each panel animation: togglePreviewCase = (index, e) => { const { menuItems } = this; const { targetPanel } = this.state; // create two arrays with the elements that should be animated // one for the elements that will be expanded and the ones that // will be collapsed let expandArray, collapseArray; // if the current target is null means no element is expanded if ( targetPanel === null ) { // only create an array for the elements that will be expanded expandArray = menuItems.slice(0, index + 1); expandArray, 1, { x: '-=50%', ease: Power2.easeInOut }) } else if ( index < targetPanel ) { // the new target is already expanded, we have to collapse // the panels before that, there's no expand animation collapseArray = menuItems.slice(index + 1, targetPanel + 1); collapseArray, 1, { x: '+=50%', ease: Power2.easeInOut }); } else if ( index > targetPanel ) { // the new target is not expanded, we have to expand all the // elements between the previous target and the new one expandArray = menuItems.slice(targetPanel + 1, index + 1); expandArray, 1, { x: '-=50%', ease: Power2.easeInOut }); } else if ( index === targetPanel ) { // the current target element is being clicked, reverse that menuItems[index], 1, { x: '+=50%', ease: Power2.easeInOut }); // the new target index should be the previous element if the // index is bigger than 0 return this.setState({ targetPanel: targetPanel > 0 ? targetPanel - 1 : null }); } // set the current index as the target panel this.setState({ targetPanel: index }); }; The comments and the explanation for the use of arrays pretty much cover what's happening here. The idea is to keep track of the index of the element being clicked to create an array of elements that should be animated. Then depending on the index position of the target panel (where the user clicks) if those will be collapsed or expanded. Finally the other part of the toggle menu code. If the user clicks on the toggle menu button and there's a menu panel expanded, we create a tween for all the expanded panels and when that's complete, reverse the menu tween and set the target panel to null using an onComplete callback. Happy Tweening!!