Create animations like PowerPoint slides in React with framer-motion and react-intersection-observe
Published: 2021-05-26 09:11:09

## Well, I’m a little bit far from use of animation libraries and kind of, but at my last project we had such a request:
>  1. Clients want a webpage that will have similar effects as a presentation in PowerPoint. On mousewheel we change our slides (previous and next)
>  2. This PowerPoint effects should be available only on a desktop view (viewport 1200px and up). But for 1200px and down we must have a normal scrollable page.
>  3. Time resource — critical low(well as always because need in some days to be in a production)

Do not to waste your time, check out final codesandbox here:
[***https://codesandbox.io/s/motion-framer-sample-3-l2y9x?file=/src/App.js:0-1912](https://codesandbox.io/s/motion-framer-sample-3-l2y9x?file=/src/App.js:0-1912)***

Assuming all these criterias, the only option is to use a library with a good documentation( no need to reinvent the wheel) and a ready CRA init project. After CRA install, we add these libraries:
 npm i framer-motion react-intersection-observer lodash

Quick review on this libs:
1. **Framer-Motion** — actually the package that helps us to animate our react components. API here: [https://www.framer.com/api/motion/](https://www.framer.com/api/motion/)
2. **react-intersection-observer **— react implementation of the Intersection Observer API to tell you *when an element enters or leaves the viewport *(from official description). Docs here: [https://github.com/thebuilder/react-intersection-observer#readme](https://github.com/thebuilder/react-intersection-observer#readme)
3. famous **lodash **— well, we need only **debounce** function . Debounce description from official docs : *Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since the last time the debounced function was invoked.*

Now, we start to count steps:

 1. Create Sections — Slides. Giving them a width and height of 100%, also setting them id. id must be different for all sections.
our App.js look like:
{% gist  https://gist.github.com/fargusmd/9ce3817ad1fcd1c94db059fa0e3145e4.js %}
*styles.css* looks like that:
- we reset margin and borders for all elements (*margin:0; padding:0*) and create sections with a width and height of 100% of container(container is *100vh and 100vw from viewport*).

- we create we flex center text and add some font size to text .
- some color to each section individually
{% gist https://gist.github.com/fargusmd/98bc5504058e96ebf396d6ea4156d2e3.js %}
Each section looks like that :
- nothing special, just a section tag with an ID (later we will add actions using this id! so important to each section to add a different id.
*./sections/Section1.jsx (all sections contains same code)*
{% gist https://gist.github.com/fargusmd/ea32f6b33da0a73d5bda7cc9bbd025a6.js %}
Now, we have sections, we have our packages setted up, let’s do some coding!

**Every page has 4 effects:**

 1. Animation that is played when section is on viewport( ***visible*** )

 2. Animation that is played when user is leaving outport(***hidden***)

 3. If user goes next section, we play exit animation for next section (***next***)

 4. If user returns back to previous section, we play different exit animation (***previous***).

Well, let’s implement a custom hook that does all these. Also a file that constains all our constants ( delay! of course… we need a delay for our debounce function , that will optimize our ***wheel*** event listener)

*./constants.js*
{% gist https://gist.github.com/fargusmd/3173e6d3f65123a195a122457370290d.js %}
*./useObserverHook.jsx
 *So, what we did there:
- we set by default ***hidden*** state of our animation, and if it is on viewport, with help of ***useInView*** hook we set state to ***visible**.*

    **useEffect**(() => {        
    if (**inView**) {            
       *setVariant*("**visible**");        
      } else setVariant("**hidden**");    
    }, [**inView**]);

Also we create a debounced function to trigger state change when user scrolls up or down, also we add a flag *isFirst* and *isLast *(well you dont need to go on previous section if you are on last section and viceversa)

    const ***onMouseScroll*** = **debounce**((e) => {        
      if (e.deltaY > 0 && variant === "**visible**") 
         setVariant(isLast ? "**visible**" : "**next**")        
      if (e.deltaY < 0 && variant === "**visible**") 
         setVariant(isFirst ? "**visible**" : "**previous**")    
     }, MAIN_DELAY)
{% gist https://gist.github.com/fargusmd/ed23ddcfde59b7875a793697da07d4cb.js %}
A codesandbox with sample files:
{% codesandbox motion-framer-sample-1-39iji %}
Now, let’s implement that hook on *Sections1.jsx*!
{% gist https://gist.github.com/fargusmd/25b72c11604e5ca70997d0747edd97f9.js %}
— First, we’re gonna create a *./constants.js *file and export MAIN_DELAY constant we’re we declare it to 600ms (well, this actually is debounce value for our hook — we can infinite scroll, but event will be trigerred after last “wheel” effect )
 — Let’s console.log our sandbox, to be sure that we going on the right way:

![](https://cdn-images-1.medium.com/max/2000/1*azI7tQawjJVWelC6bxQkbg.gif)

— Now we have variants for triggering animation on mouse wheel , so next step we will do that. To do that we need to import motion from motion-framer and wrap our sections with *motion. *Well, we got something like that:
{% gist https://gist.github.com/fargusmd/cc58e8af0c3e90973279fafb0d2008e2.js %}
To create these PowerPoint*ish slide effects, we need to create a separate div that will be positionated absolute on top of a section, and with different variants of animations, so we update our *./styles.css *with this block of code:
{% gist https://gist.github.com/fargusmd/3be03c328fa7d5edcf18d03831f20e58.js %}
So, when user use wheel down event, we play for drawer — *drawerVar*, and for section — *sectionVar. *Code sandbox here:
{% codesandbox motion-framer-sample-2-hpsqs  %}
And last small step, is just to switch our pages/slides on same mouse wheel event.
To do that we need to update our *App.jsx* component (navigation — on scroll we switch out the slide and leave 0.4s to play animations for sections):
{% gist https://gist.github.com/fargusmd/e905a4dbc3258867178b49eecc8ea706.js %}
And some small updates on sections, and we get this effects :
a) for drawer — previous and next
b) for content — hidden and visible

![](https://cdn-images-1.medium.com/max/2000/1*GQuxUUPBqhH6J4rz2WCguA.gif)

Final results on codesandbox here:
[***https://codesandbox.io/s/motion-framer-sample-3-l2y9x?file=/src/App.js:0-1912](https://codesandbox.io/s/motion-framer-sample-3-l2y9x?file=/src/App.js:0-1912)***
{% codesandbox motion-framer-sample-2-hpsqs %}>  ***Happy Coding!***