Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...

Search the Community

Showing results for tags 'react'.



More search options

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • GreenSock Forums
    • GSAP
    • Banner Animation
    • Jobs & Freelance
  • Flash / ActionScript Archive
    • GSAP (Flash)
    • Loading (Flash)
    • TransformManager (Flash)

Product Groups

  • Club GreenSock
  • TransformManager

Categories

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


AIM


MSN


Website URL


ICQ


Yahoo


Jabber


Skype


Location


Interests

Found 54 results

  1. Hi all, I started working on an idea and want to stop before I go further and ask a few questions and get some criticism on best practices. I'll preface with saying that I'm only concerned with modern browsers. First, GSAP performance. Is there a better approach I could take to accomplish the same thing and would perform better? Second, React with GSAP. This should probably be a separate question... I've been building react apps via `create-react-app` for a while and in the past, I had some issues using GSAP in React. Mostly, with using plugins that `require TweenLite`, requiring me to eject the `create-react-app` and customize the webpack config to resolve the alias. (As an aside, I now get around ejecting for simple things like this by using react-app-rewired). There are some edge case issues in particular I'm trying to solve. When you move the mouse quickly from left to right, sometime the cube will spin too much. I've played around with some boolean checks to see if I'm overlapping tweens or something but nothing seems to help. I suspect it's based on the way I'm "snapping" the cube's most forward face to the center when the mouse moves back to the center. EDIT 1... is it possible that this is related to React state? I wonder if react-gsap-enhancer would help. EDIT 2... looks like the codepen may even have other issues that aren't present in my local setup. If you move the mouse too far past the cube it stops, which should only occur when the mouse is over the cube. And it's more difficult to see the real issue I'm trying to solve in the codepen.
  2. YetiWords

    React GSAP

    Hello! React + GSAP tutorials are pretty rare (imho), but I just stumbled on this channel. The videos are concise and the creator explains everything well. Hope this is helpful https://www.youtube.com/channel/UCqrxiLP9RHz2GxDJaZuTRBw/videos Blessings, Yeti
  3. Hi everyone! I'm trying to ease a progress bar filling animation but when it gets triggered it jumps straight to 100%. I want the animation to be triggered only once. I'm using react-scrollmagic and react-gsap. Progress bar is from Bulma framework. This is my code: <Controller> <Scene pin={false} reverse={false} indicators={true} triggerElement= "#trigger1" duration={0} > <Tween staggerFrom={{ value: 0, ease: 'Expo.easeOut' }} staggerTo={{ value: 100, ease: 'Expo.easeOut' }} stagger={0.15}> <div id="trigger1"></div> <progress className="progress is-primary" value="0" max="100"></progress> </Tween> </Scene> </Controller> The same tween without the scrollmagic scene instead works perfectly. Any ideas how to fix this?
  4. Hello Community - my second post as I'm very stuck trying to integrate a range-slider in React that will be friends with the progress() method to move through the timeline! I can pass around the value I need to my slider, but getting undefined errors when I try to pass this over to this.tl.progress(this.state.value) etc? I've read through several posts, but none look very current...in fact most are going back 2-3 years and the react-way has changed! Looking through the 'getting started with react post from Rodrigo' there are some useful hints, but it seems overly complex, compared to how easy it was to plugin the play, pause, reverse and restart methods into a button with an onClick event handler. Do we really need a separate state management file to pass down the state as props to a child component just to get the slider to move through the timeline without breaking? I also got some very good support already (This gives you more of an idea what I am working on...sorry for the lack of a reduced codepen example as this is a large full-stack application! coming soon I promise!) Here's some code of how I got the play, pause, reverse, and restart methods working...these plugged right in! (FYI ButtonGroup and Button components are from React-Bootstrap): <Row> <Col md={12}> <ButtonGroup className="animationControls"> <Button bsStyle="primary" onClick={() => this.tl.play()}> <Glyphicon glyph="play" /> {"Play"} </Button> <Button bsStyle="primary" onClick={() => this.tl.pause()}> <Glyphicon glyph="pause" /> {"Pause"} </Button> <Button bsStyle="primary" onClick={() => this.tl.reverse()}> <Glyphicon glyph="backward" /> {" Reverse"} </Button> <Button bsStyle="primary" onClick={() => this.tl.restart()}> <Glyphicon glyph="step-backward" /> {"Restart"} </Button> </ButtonGroup> </Col> </Row> Until bootstrap-4 is up and running with react, you do not have range slider in your form components, so I had to look elsewhere. After trying a few different versions, the npm package rc-slider seems to be the most lightweight (little to no boilerplate required!) create your styles for the slider before your class function: const railStyle = { position: "relative", width: "90%", margin: "0% 0% 0% 3%", height: 10, borderRadius: 7, cursor: "pointer", backgroundColor: "#afafaf" }; const handleStyle = { height: 15, width: 15, backgroundColor: "white", borderTopLeftRadius: 10, borderTopRightRadius: 10, border: "3px solid #E5F1FC", top: -2, position: "absolute" }; AND be sure to set your starting value in the constructor....probably 0 since that would be the start of your timeline.... constructor(props) { super(props); this.tl = new TimelineMax(); this.state = { center: [46.8, 8.3], zoom: 1, value: 0 }; this.handleZoomIn = this.handleZoomIn.bind(this); this.handleZoomOut = this.handleZoomOut.bind(this); this.handleReset = this.handleReset.bind(this); this.handleSliderChange = this.handleSliderChange.bind(this); } Next, I have two functions...please note that onSliderChange and onAfterChange are pre-built methods for the react rc-slider component. these successfuly track and log the value as you drag along the timeline, but kill the animation! onSliderChange = value => { this.setState({ value }); console.log("Value is: ", value); }; onAfterChange = value => { console.log(value); this.tl.progress(value / 100); }; .....And lastly, the slider component itself, inside render() <Slider className="slider" style={railStyle} handleStyle={handleStyle} min={0} max={bookingData.length} value={this.state.value} onChange={this.onSliderChange} //onInput={this.handleSliderChange} onAfterChange={this.onAfterChange} /> I know this may be hard to digest without a working example. I'll try to make a reduced case, but here's the issue...inside the Slider component, if you drag the slider around, it will successfully log the value I need. Where can I pass the value to this.tl.progress(this.state.value / 100) etc to get the timeline to respond? I've tried a dozen different ways, and I either get that value is undefined, or when I try to pass this in to the onSliderChange I get my fav error about expected a function, but instead saw an expression, no unused expressions. dragging the slider around kills the timeline, or depending where I do it, will make the animated elements disappear from the screen. Grrrrr! React is very powerful, but the need to constantly update the state of components during their lifecycle make these kinds of things very frustrating! If anyone has solved this or can send a link to an example of how to do this it would be greatly appreciated! If I figure it out on my own I will update the post - I know I'm close! Thanks community!
  5. Hi guys. Just wondering if it would be possible to use GSAP for animating simple page transitions in React? I have my index.js file rendering my app: // React Common Modules import React from 'react'; import ReactDOM from 'react-dom'; import { TransitionGroup, CSSTransition } from 'react-transition-group' import { BrowserRouter, Switch, Route, Link, withRouter } from 'react-router-dom' import thunkMiddleware from 'redux-thunk'; import createLogger from 'redux-logger'; import { createStore, applyMiddleware } from 'redux'; import { Provider } from 'react-redux'; import Reducer from './Reducers/Reducer'; const loggerMiddleware = createLogger(); const createStoreWithMiddleware = applyMiddleware( thunkMiddleware, // Middleware for dispatch() loggerMiddleware // Middleware for loging )(createStore); let store = createStoreWithMiddleware(Reducer); // Main SCSS import './index.scss'; // Root React Component import App from './App'; ReactDOM.render( <BrowserRouter> <App/> </BrowserRouter>, document.getElementById('root') ); Then I have my app.js // React Common Modules import React, { Component } from "react"; // React Router import { BrowserRouter, Switch, Route, Link, withRouter } from 'react-router-dom' import { TransitionGroup, CSSTransition } from 'react-transition-group' import 'gsap/TweenLite' import 'gsap/CSSPlugin' import 'gsap/EasePack' import 'gsap/TimelineLite' // Own Modules import { DefaultPage } from "./Pages/"; import { AboutPage } from "./Pages/"; class App extends Component { constructor(props) { super(props); } render() { return ( <TransitionGroup> <CSSTransition key={location.key} classNames='fade' timeout={1000}> <Switch location={this.props.location}> <Route exact path='/' component={DefaultPage} /> <Route exact path='/about' component={AboutPage} /> </Switch> </CSSTransition> </TransitionGroup> ); } } export default App; And then I have a SideMenu component with all my <Link> 's. Everything is working fine, I just can't get it to create a transition in between the routes, which is where I'd like to use GSAP. The timeout simply doesn't work. Here's the scenario I'm trying to accomplish: User is on the Landing Page Clicks /about link Color slides in from the left and covers the entire screen (using GSAP) while the landing page is still there beneath the color. The landing page leaves behind the color. The color slides out. About page is revealed. Does anyone know how to do this? Thanks!
  6. Hi there, First post here! I'm working on a React/Gatsby app which references this tutorial. I've solved most of the issues except for the following error: TypeError: Cannot read property 'easeOut' of undefined I've tried a bunch of variations of imports destructuring etc but Quad is never defined. My code is as follows: import React, { Component } from "react" import Layout from "../components/layout" import SEO from "../components/seo" import { TweenMax, Quad, Sine, Back } from "gsap/TweenMax" const prettyLetter = require( 'pretty-letters' ) export default class IndexPage extends Component { componentDidMount() { var options = { groupClass: 'char-group-', groupTag: 'span' } prettyLetter('a', options) const lineEq = (y2, y1, x2, x1, currentVal) => { // y = mx + b var m = (y2 - y1) / (x2 - x1), b = y1 - m * x1 return m * currentVal + b } const lerp = (a,b,n) => (1 - n) * a + n * b const distance = (x1,x2,y1,y2) => { var a = x1 - x2 var b = y1 - y2 return Math.hypot(a,b) } const getMousePos = (e) => { let posx = 0 let posy = 0 if (!e) e = window.event if (e.pageX || e.pageY) { posx = e.pageX posy = e.pageY } else if (e.clientX || e.clientY) { posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop } return { x : posx, y : posy } } let winsize; const calcWinsize = () => winsize = {width: window.innerWidth, height: window.innerHeight} calcWinsize() window.addEventListener('resize', calcWinsize) // The feDisplacementMap element const feDisplacementMapEl = document.querySelector('feDisplacementMap') class Menu { constructor() { this.DOM = { svg: document.querySelector('svg.distort'), menu: document.querySelector('nav.menu') } // The images (one per menu link) this.DOM.imgs = Array.from(Object.assign.apply(Object, [this.DOM.svg.querySelectorAll('g > image')])) // The menu links this.DOM.menuLinks = Array.from(Object.assign.apply(Object, [this.DOM.menu.querySelectorAll('.menu__link')])) // Mouse position this.mousePos = {x: winsize.width/2, y: winsize.height/2} // Last mouse positions (one to consider for the image translation movement, another for the scale value of the feDisplacementMap element) this.lastMousePos = { translation: {x: winsize.width/2, y: winsize.height/2}, displacement: {x: 0, y: 0} } // feDisplacementMap scale value this.dmScale = 0 // Current menu link position this.current = -1 this.initEvents() requestAnimationFrame(() => this.render()) } initEvents() { // Update mouse position window.addEventListener('mousemove', ev => this.mousePos = getMousePos(ev)) this.DOM.menuLinks.forEach((item, pos) => { const letters = Array.from(Object.assign.apply(Object, [item.querySelectorAll('span')])) const mouseenterFn = () => { // Hide the previous menu image if (this.current !== -1) { TweenMax.set(this.DOM.imgs[this.current], { opacity: 0 }); } // Update current this.current = pos // Now fade in the new image if we are entering the menu or just set the new image's opacity to 1 if switching between menu items if (this.fade) { TweenMax.to(this.DOM.imgs[this.current], 0.5, { ease: Quad.easeOut, opacity: 1 }) this.fade = false } else { TweenMax.set(this.DOM.imgs[this.current], { opacity: 1 }) } // Letters effect TweenMax.staggerTo(letters, 0.2, { ease: Sine.easeInOut, y: this.lastMousePos.translation.y < this.mousePos.y ? 30 : -30, startAt: {opacity: 1, y: 0}, opacity: 0, yoyo: true, yoyoEase: Back.easeOut, repeat: 1, stagger: { grid: [1,letters.length-1], from: 'center', amount: 0.12 } }) } item.addEventListener('mouseenter', mouseenterFn) }) const mousemenuenterFn = () => this.fade = true const mousemenuleaveFn = () => TweenMax.to(this.DOM.imgs[this.current], .2, { ease: Quad.easeOut, opacity: 0 }) this.DOM.menu.addEventListener('mouseenter', mousemenuenterFn) this.DOM.menu.addEventListener('mouseleave', mousemenuleaveFn) } render() { // Translate the image on mousemove this.lastMousePos.translation.x = lerp(this.lastMousePos.translation.x, this.mousePos.x, 0.2) this.lastMousePos.translation.y = lerp(this.lastMousePos.translation.y, this.mousePos.y, 0.2) this.DOM.svg.style.transform = `translateX(${(this.lastMousePos.translation.x-winsize.width/2)}px) translateY(${this.lastMousePos.translation.y-winsize.height/2}px)` // Scale goes from 0 to 50 for mouseDistance values between 0 to 140 this.lastMousePos.displacement.x = lerp(this.lastMousePos.displacement.x, this.mousePos.x, 0.1) this.lastMousePos.displacement.y = lerp(this.lastMousePos.displacement.y, this.mousePos.y, 0.1) const mouseDistance = distance(this.lastMousePos.displacement.x, this.mousePos.x, this.lastMousePos.displacement.y, this.mousePos.y) this.dmScale = Math.min(lineEq(50, 0, 140, 0, mouseDistance), 50) feDisplacementMapEl.scale.baseVal = this.dmScale requestAnimationFrame(() => this.render()) } } new Menu() } render() { return ( <Layout> <SEO title="Home" keywords={[`Artist`, `Brisbane-based`, `drawing`, `painting`, `watercolour`, `sculpture`, `installation`, `video`, `embroidery`]} /> <div style={{ paddingBottom: 100 }}> <svg className="distort" width="350" height="450" viewBox="0 0 350 450"> <filter id="distortionFilter"> <feTurbulence type="turbulence" baseFrequency="0.07 0.01" numOctaves="5" seed="2" stitchTiles="stitch" x="0%" y="0%" width="100%" height="100%" result="noise"/> <feDisplacementMap in="SourceGraphic" in2="noise" scale="0" xChannelSelector="R" yChannelSelector="B" x="0%" y="0%" width="100%" height="100%" filterUnits="userSpaceOnUse"/> </filter> <g filter="url(#distortionFilter)"> <image className="distort__img" x="50" y="50" xlinkHref={require('../images/1.jpg')} height="350" width="250"/> <image className="distort__img" x="50" y="50" xlinkHref={require('../images/2.jpg')} height="350" width="250"/> </g> </svg> <nav className="menu"> <a href="#" className="menu__link">Shanghai</a> <a href="#" className="menu__link">Taipei</a> <a href="#" className="menu__link">Bangkok</a> <a href="#" className="menu__link">Kyoto</a> </nav> </div> </Layout> ) } }
  7. A guest post from Rodrigo Hernando, a talented animator/developer who has years of experience solving animation challenges. Rodrigo is a seasoned React developer and he was one of our first moderators, serving the GreenSock community since 2011 with his expert help and friendly charm. Preface This guide assumes a basic knowledge of both the GreenSock Animation Platform (GSAP) and React, as well as some common tools used to develop a React app. As GSAP becomes the de-facto standard for creating rich animations and UI's on the web, developers must learn how to integrate it with other tools like React which has become popular because it allows developers to write their apps in a modular, declarative and re-usable fashion. As a moderator in the GreenSock forums, I've noticed that there are a few common hurdles to getting the two working together seamlessly, like referencing the DOM element appropriately, doing things The React Way, etc. which is why I'm writing this article. We won't delve into how a React app should be structured since our focus is on using GSAP, but the techniques used throughout this guide follow the official guidelines and have been reviewed by maintainers of the React Transition Group tool. We'll start simple and get more complex toward the end. How GSAP Works GSAP basically updates numeric properties of an object many times per second which creates the illusion of animation. For DOM elements, GSAP updates the the inline style properties. const myElement = document.getElementById("my-element"); TweenLite.to(myElement, 1, {width: 100, backgroundColor: "red"}); As you can see this means that we need access to the actual DOM node rendered in the document in order to pass it to the TweenLite.to() method. How React Works Explaining how React works is beyond the scope of this article, but let's focus on how React gets the JSX code we write and puts that in the DOM. <div className="my-class"> Some content here </div> With React, we normally don't pass an id attribute to the element because we use a declarative way to access methods, instances, props and state. It's through the component's (or the application's) state that we can change how things are represented in the DOM. There's no direct DOM manipulation, so typically there's no need to actually access the DOM. The React team has given developers ways to access the DOM nodes when needed, and the API changed a bit over the years as React matured. At this time (September, 2018) the latest version of React (16.4.2) allows developers to use Refs to access the DOM nodes. In this guide we'll mainly use the Callback Refs to create a reference to the DOM node and then feed it into GSAP animations because it's much faster for GSAP to directly manipulate properties rather than funneling them through React's state machine. Creating Our First Animation We'll use the ref to access the DOM node and the componentDidMount() lifecycle method of the component to create our first animation, because this will guarantee that the node has been added to the DOM tree and is ready to be passed into a GSAP animation. class MyComponent extends Component { constructor(props){ super(props); // reference to the DOM node this.myElement = null; // reference to the animation this.myTween = null; } componentDidMount(){ // use the node ref to create the animation this.myTween = TweenLite.to(this.myElement, 1, {x: 100, y: 100}); } render(){ return <div ref={div => this.myElement = div} />; } } Not that difficult, right? Let's go through the code so we can understand what is happening. First when we create an instance of this class, two properties are added to it: myElement and myTween, but both are equal to null. Why? Because at this point the node has not been added to the DOM tree and if we try to pass this node to a GSAP animation, we'll get an error indicating that GSAP cannot tween a null target. After the new instance has been initialized, the render() method runs. In the render method we're using the ref attribute that is basically a function that has a single parameter – the DOM node being added to the DOM tree. At this point we update the reference to the DOM node created in the class constructor. After that, this reference is no longer null and can be used anywhere we need it in our component. Finally, the componentDidMount() method runs and updates the reference to myTween with a TweenLite tween whose target is the internal reference to the DOM node that should animate. Simple, elegant and very React-way of us! It is worth mentioning that we could have created a one-run-animation by not creating a reference to the TweenLite tween in the constructor method. We could have just created a tween in the componentDidMount method and it would run immediately, like this: componentDidMount(){ TweenLite.to(this.myElement, 1, {x: 100, y: 100}); } The main benefit of storing a TweenLite tween as a reference in the component, is that this pattern allows us to use any of the methods GSAP has to offer like: play(), pause(), reverse(), restart(), seek(), change the speed (timeScale), etc., to get full control of the animations. Also this approach allows us to create any GSAP animation (TweenLite, TweenMax, TimelineLite, etc.) in the constructor. For example, we could use a timeline in order to create a complex animation: constructor(props){ super(props); this.myElement = null; this.myTween = TimelineLite({paused: true}); } componentDidMount(){ this.myTween .to(this.myElement, 0.5, {x: 100}) .to(this.myElement, 0.5, {y: 100, rotation: 180}) .play(); } With this approach we create a paused Timeline in the constructor and add the individual tweens using the shorthand methods. Since the Timeline was paused initially, we play it after adding all the tweens to it. We could also leave it paused and control it somewhere else in our app. The following example shows this technique: Simple Tween Demo Animating a Group of Elements One of the perks of using React is that allows us to add a group of elements using the array.map() method, which reduces the amount of HTML we have to write. This also can help us when creating an animation for all those elements. Let's say that you want to animate a group of elements onto the screen in a staggered fashion. It's simple: constructor(props){ super(props); this.myTween = new TimelineLite({paused: true}); this.myElements = []; } componentDidMount(){ this.myTween.staggerTo(this.myElements, 0.5, {y: 0, autoAlpha: 1}, 0.1); } render(){ return <div> <ul> {elementsArray.map((element, index) => <li key={element.id} ref={li => this.myElements[index] = li} > {element.name} </li>)} </ul> </div>; } This looks a bit more complex but we're using the same pattern to access each DOM node. The only difference is that instead of using a single reference for each element, we add each element to an array. In the componentDidMount() method we use TimelineLite.staggerTo() and GSAP does its magic to create a staggered animation! Multiple Elements Demo Creating a Complex Sequence We won't always get all the elements in an array so sometimes we might need to create a complex animation using different elements. Just like in the first example we store a reference in the constructor for each element and create our timeline in the componentDidMount() method: Timeline Sequence Demo Note how in this example we use a combination of methods. Most of the elements are stored as a an instance property using this.element = null, but also we're adding a group of elements using an array.map(). Instead of using the map() callback to create tweens in the timeline (which is completely possible), we're adding them to an array that is passed in the staggerFrom() method to create the stagger effect. Animating Via State The most commonly used pattern to update a React app is through changing the state of its components. So it's easy to control when and how elements are animated based on the app state. It's not very difficult to listen to state changes and control a GSAP animation depending on state, using the componentDidUpdate() lifecycle method. Basically we compare the value of a state property before the update and after the update, and control the animation accordingly. componentDidUpdate(prevProps, prevState) { if (prevState.play !== this.state.play) { this.myTween.play(); } } Control Through State Demo In this example we compare the value of different state properties (one for each control method implemented in the component) to control the animation as those values are updated. It's important to notice that this example is a bit convoluted for doing something that can be achieved by calling a method directly in an event handler (such as onClick). The main idea is to show the proper way of controlling things through the state. A cleaner and simpler way to control an animation is by passing a prop from a parent component or through an app state store such as Redux or MobX. This modal samples does exactly that: // parent component <ModalComponent visible={this.state.modalVisible} close={this.setModalVisible.bind(null, false)} /> // ModalComponent constructor(props){ super(props); this.modalTween = new TimelineLite({ paused: true }); } componentDidMount() { this.modalTween .to(this.modalWrap, 0.01, { autoAlpha: 1 }) .to(this.modalDialog, 0.25, { y: 50, autoAlpha: 1 }, 0) .reversed(true) .paused(false); } componentDidUpdate(){ this.modalTween.reversed(!this.props.visible); } As you can see the modal animation is controlled by updating the visible prop passed by its parent, as well as a close method passed as a prop. This code is far simpler and reduces the chance of error. State Modal Demo Using React Transition Group React Transition Group(RTG) is a great tool that allows another level of control when animating an element in a React app. This is referred to as the capacity to mount and unmount either the element being animated or an entire component. This might not seem like much when animating a single image or a div, but this could mean a significant performance enhancement in our app in some cases. SIMPLE TRANSITION DEMO In this example the <Transition> component wraps the element we want to animate. This element remains unmounted while it's show prop is false. When the value changes to true, it is mounted and then the animation starts. Then when the prop is set to false again, another animation starts and when this is completed it can also use the <Transition> component to wrap the entire component. RTG also provides the <TransitionGroup> component, which allows us to control a group of <Transition> components, in the same way a single <Transition> component allows to control the mounting and unmounting of a component. This is a good alternative for animating dynamic lists that could have elements added and/or removed, or lists based on data filtering. Transition Group Demo <Transition timeout={1000} mountOnEnter unmountOnExit in={show} addEndListener={(node, done) => { TweenLite.to(node, 0.35, { y: 0, autoAlpha: show ? 1 : 0, onComplete: done, delay: !show ? 0 : card.init ? props.index * 0.15 : 0 }); }} > In this example we use the addEndListener() callback from the <Transition> component. This gives us two parameters, the node element being added in the DOM tree and the done callback, which allows to control the inner state of the <Transition> component as the element is mounted and unmounted. The entire animation is controlled by the in prop, which triggers the addEndListener() and ultimately the animation. You may notice that we're not creating two different animations for the enter/exit state of the component. We create a single animation that uses the same DOM node and the same properties. By doing this, GSAP's overwrite manager kills any existing animation affecting the same element and properties, giving us a seamless transition between the enter and exit animations. Finally, using RTG allows us for a more fine-grained code, because we can use all the event callbacks provided by GSAP (onStart, onUpdate, onComplete, onReverse, onReverseComplete) to run all the code we want, before calling the done callback (is extremely important to notify that the animation has completed). Animating Route Changes Routing is one of the most common scenarios in a React app. Route changes in a React app means that an entirely different view is rendered depending on the path in the browser's address bar which is the most common pattern to render a completely different component in a route change. Obviously animating those changes gives a very professional look and feel to our React apps. Rendering a new component based on a route change means that the component of the previous route is unmounted and the one for the next route is mounted. We already covered animating components animations tied to mount/unmount using the <Transition> component from RTG, so this is a very good option to animate route changes. <BrowserRouter> <div> <Route path="/" exact> { ({ match }) => <Home show={match !== null} /> } </Route> <Route path="/services"> { ({ match }) => <Services show={match !== null} /> } </Route> <Route path="/contact"> { ({ match }) => <Contact show={match !== null} /> } </Route> </div> </BrowserRouter> This main component uses React Router's <BrowserRouter> and <Route> and checks the match object passed as a prop to every <Route> component, while returning the component that should be rendered for each URL. Also we pass the show property to each component, in the same way we did in the transition example. <Transition unmountOnExit in={props.show} timeout={1000} onEnter={node => TweenLite.set(node, startState)} addEndListener={ (node, done) => { TweenLite.to(node, 0.5, { autoAlpha: props.show ? 1 : 0, y: props.show ? 0 : 50, onComplete: done }); }} > As you can see, the code is basically the same used to animate a single component; the only difference is that now we have two animations happening in different components at the same time. Route Animation Demo It's worth noting that the animations used in this example are quite simple but you can use any type of animation even complex, nested animations. As you can see by now, using GSAP and React can play together nicely. With all the tools and plugins GSAP has to offer the sky is the limit for creating compelling and amazing React applications! FAQ What is this "Virtual DOM" thing, that is referred so much when it comes to React Apps?. Can GSAP work with this virtual dom? The Virtual DOM is what React uses to update the DOM in a fast and efficient way. In order to learn more about it check this article and the React Docs. GSAP can't work with the virtual DOM because the elements in the Virtual DOM are not exactly DOM nodes per-se. I often read about the declarative nature of React. Does that affect how we use GSAP in a React APP? Yes. React works by updating the rendered DOM through changes in the App state, so when creating an animation using GSAP, instead of reaching out directly to the DOM, like in most other cases, we need to wait for those changes in the app state and the DOM to be rendered, in order to use the current representation of the app state and create the animation. To learn more about how declarative and imperative code work read this article. In the second sample I see this code in the ref callback ref={div => this.cards = div}. Why is the index being used instead of just pushing the element in the array? The reason for that is that every time a React component is re-rendered, the render method is executed, but the original instance remains unchanged. The array used to create the animation is created in the component's constructor. The GSAP animation (a TimelineLite) is created in the componentDidMount hook. These two elements are created just once in the App's lifecycle, while the render method is executed on every re-render. Therefore if we push the elements to the array on every re-render, even though the Timeline instance won't change, the array will get bigger and bigger every time the component is re-rendered. This could cause a memory issue, especially for large collections. In the guide one of the samples triggers animations via the state of a component or the app. Is it possible to update the state of the component/app using GSAP? Absolutely! All you have to do is use one of the many callback events GSAP has to offer. The only precaution is to be aware of infinite loops. That is if an animation is started on the render method of a component and a callback from that animation updates the state of the component then that will trigger a re-render, which will start the animation again. You can check this simple example of how that can be done. Is it possible to trigger a route change using GSAP? It is possible using React Router's API. Although is not recommended because using React Router's API directly will prevent triggering the route change animations when using the browser's back and forward buttons. However, using React Transition Group with GSAP does trigger the route change animations with the native navigation methods. Can I use other GSAP plugins and tools in a React App? This guide shows only TweenMax, Timeline and the CSS Plugin? Yes, any GSAP tool or plugin you want can be used in a React app. Just be sure to follow the same patterns and guidelines from this article and you'll be fine. I tried the code in the guide and samples, but it doesn't work. What can i do? Head to the GreenSock forums where all your questions will be answered as fast as possible. I want to contribute or post an issue to this guide. Where can I do that? Even though this guide was reviewed by GreenSock and React experts, perhaps something might have slipped away, or with time and new versions, some things should or could be done differently. For those cases please head to this GitHub Repo and inform any issues or create a Pull Request with the changes you think should be added. New to GSAP? Check out the Getting Started Guide. Got questions? Head over to the GreenSock forums where there's a fantastic community of animators. Acknowledgments I'd like to thank the three developers that took time from their work and lives to review this guide as well as the samples in it. I couldn't have done this without their help and valuable input. Please be sure to follow them: Xiaoyan Wang: A very talented React developer. While Xiaoyan doesn't have a very active social life (twitter, facebook, etc), you can follow what he does in GitHub. Jason Quense: One of the maintainers of React Transition Group and part of the React Bootstrap Team. Jason also collaborates in many other React-related projects. Check Jason's GitHub profile for more info. Matija Marohnić: The most active contributor and maintainer of React Transition Group and Part of the Yeoman Team. Matija also contributes in a lot of React-related projects as well as many other open source software. Be sure to follow Matija in GitHub and Twitter.
  8. Hey everyone! New Club Greensock member here I'm loving GSAP so far! Hats off to the Greensock team for creating this wonderful tool. Basically, I'm just trying to get React to compile after importing 'animation.gsap'. I haven't included a Codepen, as the app and ScrollMagic work -- but I can't get the right behaviour due to this animation.gsap issue. That said, do let me know if you want a Codepen up regardless. I installed ScrollMagic via NPM. I'm aware this issue was brought up before, but none of the solutions I could find have worked out for me :(. At the moment, I'm testing out scaling down my h2 header to half its size upon scroll to the section. Thanks in advance! UPDATE: I've loaded GSAP, ScrollMagic, and animation.gsap.js via CDN. While the former two are communicating, animation.gsap.js isn't. Apparently, it might be due to "setTween( )"? What would be the alternative to "setTween()"? From ScrollMagic Troubleshooting Docs 2.2 Make sure it's a ScrollMagic issue and not related to your animation framework. Many animation-related issues are caused by an animation framework (GSAP/Velocity) or a misuse thereof. A very common mistake for example, is that the selector for TweenMax turns up empty. For GSAP the recommended best practice is to create your tweens, but refrain from adding them to the ScrollMagic scene object using setTween. This ensures that ScrollMagic doesn't manipulate the animations in any way. If you have a look at your site now, you can check if the animations plays out the way you wanted to. If they don't, the problem is obviously not rooted in ScrollMagic. ///////////// ERROR Failed to compile ./node_modules/scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap.js Module not found: Can't resolve 'TimelineMax' in '/Users/mabbs/Desktop/WebDevelopment/port-site2/node_modules/scrollmagic/scrollmagic/uncompressed/plugins' CODE import React, {Component} from 'react' import 'gsap'; import ScrollMagic from 'scrollmagic'; import '../../../node_modules/scrollmagic/scrollmagic/uncompressed/plugins/animation.gsap.js'; export default class Projects extends Component { constructor(props){ super(props) this.controller = new ScrollMagic.Controller(); } componentDidMount(){ new ScrollMagic.Scene({ triggerElement: "#scrollStarts", duration: 400, offset: 200 }) .setTween('#main-content', { scale: 0.5 }) .setPin("#latest") .addTo(this.controller) } render(){ return( <section className="projects-section" id="projects"> <div id="scrollStarts"> ------ Test ------- </div> <div className="inner-wrapper" id="main-content"> <h2 id="latest">My Latest Work.</h2> </div> </section> ) } }
  9. I'm getting an error when trying to use TweenMax in React component. I create the component like this below. import React, {Component} from 'react'; import {connect} from 'react-redux'; import Thumbnail from './thumbnail'; import TransitionGroup from 'react-transition-group/TransitionGroup' import {fetchHomeCollection} from '../../actions/client'; import styles from '../../../styles/client/home.scss'; class Home extends Component { constructor(props){ super(props); } componentWillMount(){ this.props.fetchHomeCollection(); } firstChild(props) { const childrenArray = React.Children.toArray(props.children); return childrenArray[0] || null; } renderThumbnails(){ const {collection} = this.props; if(collection.length <= 0) return <div>Loading...</div> return collection.map((photo, index)=>{ return( <TransitionGroup component={this.firstChild} key={photo._id}> <Thumbnail {...photo} delay={0.5 * index} key={photo._id}/> </TransitionGroup> ) }) } render(){ return ( <div id="home" className="flexbox-container"> {this.renderThumbnails()} </div> ) } } function mapStateToProps({photos}){ return{ collection: photos.collection } } export default connect(mapStateToProps, {fetchHomeCollection})(Home); then in my Thumbnail component I use the componentWillAppear to call my TweenMax fromTo method. import React, {Component} from 'react'; import { Link } from 'react-router-dom'; import {TweenMax} from "gsap"; export default class Thumbnail extends Component{ componentWillAppear(callback){ const el = this.container; TweenMax.fromTo(el, 0.3, {opacity: 0}, {opacity: 1, onComplete: callback}); } } Anyone know why I might be getting this error ?
  10. https://codesandbox.io/s/sad-maxwell-2b7vv?fontsize=14 Edit: For clarification, this needs to use React hooks. I have it transforming from = to x and vice versa, however, I had to hard code the reverse animation because I couldn't seem to get it to work by using the reverse or reveresed methods like shown below. if (isMenuOpen) timeline.play() else (timeline.reversed(true).play()
  11. Hi! I'm fairly new to React and firsttimer with Greensock aminations. I'm developing a timeline that is animated on the z-axis when the user scrolls through the page. However I have some issues with getting React Transition Group to play well with nested timelines, and animating out of the current progress in the timeline and then animate back on track. I have the following issues: How would I use React Transition Group in components to add/remove elements when scrolled by the element in the timeline. As you can see in Box1 I tried to wrap the component in the Transition component, but then I cannot add a nested timeline to the main timeline in index.js, that is displayed as box2 with animate on scroll. When clicking a box it opens with an animation. how would I make it animate back to small again, when clicking/scrolling. Right now it just jumps back to where it were on the timeline, before it was clicked (try to click box2). I made a simple project. The real project is much larger, and I'm therefore really concerned about optimizing performance and reuse components etc. https://stackblitz.com/edit/react-jap66w?embed=1&file=index.js For reference, this site is doing some of what I'm trying to achieve visually: https://2018.craftedbygc.com/ Thanks in advance
  12. Hi guys, I just want to contribute with my findings in case others ran into this. I had been struggling getting GSAP's Draggable to work with Next.js and have finally found a solution. Next.js does Server Side Rendering (SSR) so pages rendered on the server had issues loading the GSAP NPM modules. Please note that pages worked just fine client-side. ERROR: import TweenLite, { _gsScope, globals, EventDispatcher } from "./TweenLite.js"; ^^^^^^ SyntaxError: Unexpected token import To resolve the issue with GSAP not being transpiled, I installed next-plugin-transpile-modules npm install --save next-plugin-transpile-modules Then I modified/created my next.config.js file according to their instructions on their NPM page. https://www.npmjs.com/package/next-plugin-transpile-modules Draggable finally worked after that (only if throwProps was set to false and you did not import ThrowPropsPlugin). However, if using a plugin like ThrowPropsPlugin.js it would display an error message like: TypeError: Cannot read property 'defaultView' of undefined Around line 515 of the ThrowPropsPlugin.js file, I changed it: //FROM THIS: _doc = _gsScope.document, //TO THIS LINE I FOUND IN DRAGGABLE: _doc = _gsScope.document || {createElement: function() {return _dummyElement;}}, After that, I just did "npm run dev" and the pages rendered on the server side were fully functional as expected. Hopefully this helps somebody out! Guys at GSAP, is there any harm in changing that line in the ThrowPropsPlugin? If not, would it be a good idea to update the plugin and other plugins for others who purchased the membership with GSAP so they don't run into this issue that I encountered?
  13. I'm fairly new to React and fresher with Greensock aminations. Want to achieve vertical scroll effect like https://plasticbcn.com but stuck in middle of transition. please guide or provide some code snippets that include react + gsap scroll animation.
  14. I'm working on a project using ReactJS with Pixi & GSAP and the js library I'm using is called "react-pixi-fiber". I'm trying to reference the React Pixi Sprite object and handling the animation it using GSAP. However, it changes position without easing, and there's no error message showing. Code as below The "moveEle" function will be called from the parent component. class MainObject extends Component { constructor(props) { super(props); this.state = { ... }; this._refEle = React.createRef(); moveEle = options => { TweenLite.to(this._refEle, options.duration, { x: options.x, y: options.y, ease:Power2.easeOut }).play(); }; render() { return ( <Fragment key="MainElesFragment"> <Sprite ref={div => (this._refEle = div)} anchor={centerAnchor} texture={textureUrl} width={width} height={height} x={x} y={y} key="thisEle" /> </Fragment> ) } }
  15. Hi everyone, I've successfully built with GSAP and Gatsby before, but not with the DrawSVGPlugin. It's throwing this error: This seems to be a Gatsby build problem as it works fine with React. Any ideas? Any suggestions would be hugely appreciated. I've referenced the plugins like so: const gsapPlugins = [DrawSVG, CSSPlugin, TweenLite] Thanks, Matt
  16. Hi Just discovered GSAP and what a great library it is! I am having a problem with bounding my draggable components in my React testing. I have followed the great instructions found here (https://greensock.com/react) to get a reference to the React generated DOM element and haven't had any problems making them draggable. The draggables sit inside of another React component which should act as the bounding element. I am again getting a reference to this element as previously described and passing this into the draggable components as props - to be referenced by the draggable childrens draggable.create. This works (in that it doesn't throw any errors and my console logs are showing the correct DOM element being passed in) but the draggable bounds are behaving very oddly. A demo paints a million words so you can see what I mean here (I'm a bit more comfortable in codesandbox.io): The draggable elements appear to be bounded by something (not a DOM element I recognise) that is exactly 4 times their size and not their parent component? I have seen demo's, such as this one where they are successfully bounding a child react component in its parent. The only obvious difference to me appears to be the use of a class component over a functional component but, again, I'm not sure why that would make a difference? Note: I'm not adverse to using a class component but would love to understand why this is behaving the way it is. Any tips or suggestions welcome!
  17. Hello everyone, since recently I started using React with GSAP. I have little experience in both, so the struggle is real. I am trying to create something very simple, a full screen menu that you can show/hide. When i am trying to debug this, i can see the state changing, and if I try to print within the `toggleMenu()`, method, i can see the state is changing however `timeline.play()`, simply doesn't do anything. I tried also removing the if case for play and reverse but still no effect. Its just atht my timeline doesnt want to play. I also tried using `componentDidUpdate()` but still no success. in that case my code looked like this: toggleMenu() { this.setState({ visible: !this.state.visible }); } componentDidUpdate(prevProps, prevState){ const {visible: preVisible} = prevState; const { visible } = this.state; if (visible && visible !== preVisible) { this.timeline.play(); } } componentDidMount(){ this.timeline = new TimelineLite({ paused: true }) .to(this.menuRef.current, 0.5, {autoAlpha: 1}); // .staggerTo(this.listNodes, 0.3, { opacity: 1,y: 0}, 0.2, "-=0.3"); } This is my container, where the magic is supposed to happen: import React, { Component } from "react"; import Hamburger from "./hamburger.jsx"; import Menu from "./menu.jsx"; import Logo from "./misc/Logo.jsx"; import { TweenMax, TimelineLite, CSSPlugin } from "gsap/all"; import { Transition } from "react-transition-group"; class MenuContainer extends Component { constructor(props) { super(props); this.state = { visible: false }; this.toggleMenu = this.toggleMenu.bind(this); this.timeline = null; this.menuRef = React.createRef(); } toggleMenu() { console.log(this.state.visible) this.state.visible ? this.timeline.reverse() : this.timeline.play(); this.setState({ visible: !this.state.visible }); } componentDidMount(){ this.timeline = new TimelineLite({ paused: true }) .to(this.menuRef.current, 0.5, {autoAlpha: 1}); // .staggerTo(this.listNodes, 0.3, { opacity: 1,y: 0}, 0.2, "-=0.3"); } render() { const { visible } = this.state; let logoColor = "#3e444e"; //blue if (visible) logoColor = "#ffffff"; //White return ( <div className="nav-wrapper"> <div className="top-bar"> <div className="top-bar-left"> <Logo logocolor={logoColor} /> </div> <div className="top-bar-right"> <div> <Hamburger handleMouseDown={this.toggleMenu} menuVisibility={visible} /> </div> </div> </div> <div> <Menu visible={visible} menuRef={this.menuRef} /> </div> </div> ); } } export default MenuContainer; This is the menu element where I am setting the `ref`: import React, { Component } from "react"; import { apiUrl } from './../../methods'; class Menu extends Component { constructor(props) { super(props); this.state = { links: [], isLoading: false, error: null }; } componentDidMount() { const targetUrl = apiUrl("/sapi/main_menu_items.json"); this.setState({ isLoading: true }); fetch(targetUrl) .then(response => { if (response.ok) { return response.json(); } else { throw new Error("Something went wrong...."); } }) .then(data => this.setState({ links: data.links, isLoading: false })) .catch(error => this.setState({ error, isLoading: false })); //Passing the data to state.links and changing isLoading to false } render() { const { links, isLoading, error } = this.state; const listItems = links.map((link, index) => ( <li className="menu-link v-link" key={link.slug} // ref={li => (this.listNodes[index] = li)} > <a href={link.slug}>{link.title}</a> </li> )); if (error) return <p>{error.message}</p>; if (isLoading) return <p>Loading....</p>; return ( <div className="full-screen-menu" ref={this.props.menuRef}> <div className="grid-y grid-frame align-middle"> <div className="cell auto links-wrapper"> <div className="grid-container full-height"> <div className="grid-x align-middle full-height"> <div className="cell small-12"> <ul className="links">{listItems}</ul> </div> </div> </div> </div> <div className="cell shrink"> <div className="grid-container"> <div className="grid-x"> <div className="cell small-12 text-right text-white"> <ul> <li> <a className="text-white">+31 638 501 270</a> </li> <li> <a className="text-white">hello@whale-agency.com</a> </li> </ul> </div> </div> </div> </div> </div> </div> ); } } export default Menu; And here is the hamburger, that is being clicked: import React, { Component } from "react"; import MenuOpener from "./misc/menuOpener.jsx"; import MenuCloser from "./misc/menuCloser.jsx"; class Hamburger extends Component { render() { if (!this.props.menuVisibility) { return ( <MenuOpener className="menu-button" onMouseDown={this.props.handleMouseDown} /> ); } else { return ( <MenuCloser className="menu-button" onMouseDown={this.props.handleMouseDown} /> ); } } } export default Hamburger; Last but not least, the important css: .full-screen-menu { position: absolute; opacity: 0; visibility: hidden; width: 100vw; height: 100vh; bottom: 0; left: 0; background: url(/images/menu-background.jpg); background-size: cover; background-position: center; background-repeat: no-repeat; z-index: 999; } Hopefully someone can help me go in the right direction. Greetings, Nikolay
  18. I've been a long time user of GSAP and have used the SplitText plugin on many projects. I've been learning React and more specifically the Gatsby library and have run into an issue I cannot solve. I setup this SplitText in my Gatsby project and it worked fine - then all of a sudden today it broke giving me a Type Error. I've downloaded the most recent GSAP library and Gatsby/ React is up to date as well. TypeError: null is not an object (evaluating 'parent.insertBefore') (I know this code below is partial - I ripped out a lot of excess code) import React, { Component } from "react" import { TweenMax } from "gsap/TweenMax"; import SplitText from "../utils/gsap/SplitText" export default class IndexPage extends Component { componentDidMount() { const split = new SplitText( 'h1, p', { type: "lines", linesClass: "ts-line" } ) TweenMax.staggerFrom( '.ts-line', 1, { y: "125%", opacity: 0, }, 0.25, ); } render() { return ( <Layout> <h1>Heading Text</h1> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </p> </Layout> ) } } Here is the error I'm getting: × TypeError: null is not an object (evaluating 'parent.insertBefore') _splitRawText src/utils/gsap/SplitText.js:428 425 | container = _doc.createElement("div"), 426 | parent = element.parentNode; 427 | > 428 | parent.insertBefore(container, element); 429 | container.textContent = element.nodeValue; 430 | parent.removeChild(element); 431 | element = container; View compiled _split src/utils/gsap/SplitText.js:505 502 | element._isSplit = true; 503 | return; 504 | } > 505 | _splitRawText(element, vars, wordStart, charStart); 506 | 507 | }, 508 | p = SplitText.prototype; View compiled _split src/utils/gsap/SplitText.js:498 495 | child.style.position = "relative"; 496 | } 497 | child._isSplit = true; > 498 | _split(child, vars, wordStart, charStart); //don't split lines on child elements 499 | } 500 | } 501 | vars.absolute = absolute; View compiled split src/utils/gsap/SplitText.js:527 524 | this._originals[i] = e.innerHTML; 525 | origHeight = e.clientHeight; 526 | origWidth = e.clientWidth; > 527 | _split(e, vars, wordStart, charStart); 528 | _setPositionsAfterSplit(e, vars, this.chars, this.words, this.lines, origWidth, origHeight); 529 | } 530 | this.chars.reverse(); View compiled SplitText src/utils/gsap/SplitText.js:132 129 | this.lines = []; 130 | this._originals = []; 131 | this.vars = vars || {}; > 132 | this.split(vars); 133 | }, 134 | _swapText = function(element, oldText, newText) { 135 | var type = element.nodeType; View compiled componentDidMount src/pages/index.js:41 38 | export default class IndexPage extends Component { 39 | componentDidMount() { 40 | > 41 | const split = new SplitText( 42 | 'h1, p', 43 | { 44 | type: "lines", View compiled ▶ 18 stack frames were collapsed. (anonymous function) /Users/leecampbell/Projects/Portfolio/Development/.cache/app.js:56 53 | const preferDefault = m => (m && m.default) || m 54 | let Root = preferDefault(require(`./root`)) 55 | domReady(() => { > 56 | renderer(<Root />, rootElement, () => { 57 | postInitialRenderWork() 58 | apiRunner(`onInitialClientRender`) 59 | }) View compiled This screen is visible only in development. It will not appear if the app crashes in production. Open your browser’s developer console to further inspect this error. Does anyone have an idea why this would be happening?
  19. I am building a GatsbyJS site which is essentially a React site that builds out to static files. In my build process, I am getting an error in my ThrowPropsPlugin.js The specific error is: WebpackError: TypeError: Cannot read property 'defaultView' of undefined Here's a screenshot of the part of code... In the actual file... this code is under the VelocityTracker section. I'm guessing this error is happening because on the server side, the document doesn't exist and _doc is undefined. Is that thought correct? Does anybody have experience with building with GatsbyJS? or how I can skip out of my Greensock plugin files during the build process? Thanks!
  20. Hi team! I've been struggling for a while to get some conditional logic in place with my animations. Some quick backstory to give my codepen some context...Anyone who's curious can also see my two other threads, all related to the same project, but I thought I would make a new one with a greatly reduced example. There are LOTS of other questions about animating list items, but not many with react? And none that I've seen that involve potentially large amounts of data, or conditional logic mixed into the timeline? Backstory: I've built an interactive USA map using D3/react-simple-maps, it shows all of my companies bookings on a 5 minute delay...usually close to 50 at a time...100-150 an hour, about 8,000 in a day! That's a lot dots to show on a map! Thanks GSAP for your awesome timeline and stagger methods to make it more digestible. So...enter the 'ticker'... I decided in addition to the map I would add a sidebar that has a card with data on each booking, like who it is, the dollar amount etc. These cards appear in the ticker at the same time dots appear on the map. Now that we're live, everyone wants new features. "Wouldn't it be cool if there was a booking over a certain dollar amount, we could show a whale fly across the screen?" How do I that???? A couple of things, then some code! - Because these ticker cards contain lots of data, I do want them to pause in place for long enough that someone could read them if they wanted...after digging through forums and lots of codepen examples, the best I could come up with was multiple staggerFromTos chained together! It's pretty choppy but it works...ish. The one that is live has better timing than the the codepen! Also - no map in this example - just list items...and a cheeseburger callback! - I've been playing around with .add and .call, or adding a label somewhere to my timeline - not quite there yet! - Nothing I've done actually considers the data! In my codepen, I've created an array with id, and values, and I'm hoping if a card reaches the top of the box (or anywhere in the box) and has a value greater than 10, it will call the cheesburger animation to do its thing? Any ideas? what am I doing wrong? Where should this conditional logic live - within the function, or outside of the timeline somewhere...possible in componentDidMount or DidUpdate? - I'm still getting the hang of react and its ways...but our best practice is to use keys when going through an array with the .map() method, and use createRefs to set targets for the animation. Is there a way I can access a particular card by it's key or ref etc? https://stackblitz.com/edit/react-iiesig?embed=1&amp;file=index.js Thanks!
  21. Hi all, Not sure if i'm missing something or if I'm doing something incorrect. But I'm trying to run simple unit tests with Jest and Enzyme on my component that uses gsap for a search input box. The component works perfectly fine, animation is great too. But whenever i run my tests, and specifically this line component.find('#close').'click' i get ERROR CANNOT TWEEN A NULL TARGET the close button calls this function below: animation.hideSearch(this.searchInput) animation.hideSearch is: hideSearch(target){ return TweenMax .to(target, duration, { opacity: 0, display: 'none' }) }, And finally here is my search input: <input id='search-input' className={styles.input} value={this.state.searchValue} onChange={this.handleSearch} ref={div => this.searchInput = div}/> Is there anything i need to configure in order to get jest to ignore the gsap animation? Any help is appreciated. thanks!
  22. Hello community - my first post! I discovered GSAP on Friday, got it working a few days later...mind=blown! I'd be happy to supply a codepen but have more a general/best practices question. My stack: React, D3...or rather, react-simple-maps npm package to use D3's map projections...and of course GSAP! My project: I'm a new front-end developer, hired as a data visualization analyst, and my first project is to build an interactive map to show bookings. with hundreds of bookings to render, it would be 'data overload' and lousy UI with hundreds of markers across the US all at the same time... I started racking my brain to try and find a way to map these out over a timeline to stagger the bookings...enter GSAP! Question: The map shows (first) the location of the customer who made the booking, our client's location, and a line connecting the two. I have a data object with nested client and customer data, looks something like this below. In order to generate map markers for both the client and the customer, I have already used .map to go through each array and create the marker. I have staggered the animations, but at runtime, all customers are rendered one at a time...then all clients are rendered one at a time, followed by all lines. Surely there's a way to to use the key to go by each bookingId and render the customer unique to that ID, then the client, and then animate a line connecting the two before applying that same animation to the next booking? My janky code for the animations (first-timer over here, go easy on me! lol) - I know the best practice in React is to use createRef instead of classeNames - will migrate soon! Likely to move this to a separate animation.js file and call the functions later in componentDidMount(). Happy to send more code examples and thanks! componentDidMount() { bookingInterval(); //Animations with GSAP const tl = new TimelineLite(); tl.staggerFrom( ".custMarkers", 1, { y: 500, cycle: { x: [-500, 500] }, ease: Power1.easeOut, opacity: 0.1, scale: 10 }, 0.5 ) .staggerFrom( ".clientMarkers", 1, { cycle: { x: [-500, 500] }, y: 500, ease: Power1.easeOut, opacity: 0.1, scale: 7 }, 0.5 ) .staggerFrom( ".lineAmation", 2, { y: 500, x: -500, ease: Power1.easeOut, opacity: 0.1, scale: 7, rotation: 360 }, 0.5 ); } Data: { BookingId: 123456, client: { ZipCode: 55555, coordinates: [-80.6998, 28.3716], clientName: "name of the client", }, customer: { ZipCode: 11111, coordinates: [-122.3122, 47.5838], location: "city, state" }, CreatedAt: "timestamp string" },
  23. I've been working on porting a shattering/explosion effect from vanilla js (https://codepen.io/elitmsw/pen/XomeQZ) to react. I think I have most everything working, but unfortunately the z property, which provides the explosion, doesn't take effect anymore. Both are using v2.0.2, and fiddle as I might, I can't seem to figure out why it won't work. I found I can simulate the z effect with a negative custom easing and moving the shatterFragmentRefs to the top and left, but it isn't really what I want. Any idea what might be blocking the z from kicking in? Notes for navigation: the timelines are located in the Explosion Component in componentDidUpdate Let me know if I'm leaving out any important info. Thank You, espeigeltmsw
  24. Hi guys 😄 I tried to animate the ':: before' pseudo-element of a button in a React application with Styled-Components ... I know, it's a bit crazy 🤠 But, unfortunately, I did not have much success, here is my code .... import React, { Component } from 'react'; import styled from 'styled-components'; import { TweenLite, CSSPlugin, CSSRulePlugin } from 'gsap/all'; const ButtonElement = styled.button` width: 200px; height: 60px; border-radius: 30px; line-height: 60px; background: #F9AE32; color: #0D0F1B; font-size: 1.6rem; text-align: center; font-weight: 500; position: relative; ::before { content: ''; width: 100%; height: 100%; border-radius: 30px; background: #C18522; position: absolute; bottom: -38px; right: -178px; } `; export default class Button extends Component { animateMenuOpen = () => { let buttonElementBefore = CSSRulePlugin.getRule("button::before"); console.log('buttonElementBefore', buttonElementBefore); // return undefined // it doesn't work... TweenLite.to(buttonElementBefore, .400, { x: 500 }) } animateMenuClose = () => {} render() { return ( <ButtonElement ref={ref => this.buttonElement = ref} onMouseEnter={this.animateMenuOpen} onMouseLeave={this.animateMenuClose} >Button Text</ButtonElement> ) } } What am I missing here? 😢 I know that to animate with GSAP and React we need the ref, so I tried that way ... let buttonElementBefore = CSSRulePlugin.getRule(`${this.buttonElement}::before`); console.log('this.buttonElementBefore', this.buttonElementBefore); But, still did not work 😭 Thank you guys 🤓 Alex.
  25. Hi everyone, So I'm looking at making an interactive timeline of famous people in history and there will be hundreds of people with associated data. I'm using react (gatsby specifically). I want to be able to map through the data object and render about 10 people at a time on this pathway, and it looks like they are at the back of the path. Then, when the user scrolls down say, the people move forward towards the user, on the timeline. Then when they get to the front of the timeline they fade away. In the meantime more people have appeared behind them as they move forward. What I think is a size animation, so they get bigger based on trigger points on the screen. But what the heck do I know?? Theres so much info out there I'm bamboozled. So what do you think - scrollmagic? gsap? Where do I even start yo?! Advice please!!!
×