Table of Contents
react-spring quick look
springs
Highly animated
Animation transition
Demo
Other details
Some notes on these sandboxes
Build practical applications
Animate our modals
Home Web Front-end CSS Tutorial Making Sense of react-spring

Making Sense of react-spring

Apr 03, 2025 am 10:58 AM

Making Sense of react-spring

React animation implementation has always been a difficult point in development. This article will introduce react-spring in an easy-to-understand manner and explore some practical application cases. While react-spring is not React's only animation library, it is one of the most popular and powerful libraries.

This article will use the latest 9.x version (release candidate version at the time of writing). If it has not been officially published at the time of reading this article, please use react-spring@next to install it. According to my experience and the main maintainer, the code is very stable. The only problem I've had is a small error when used with concurrency mode, which can be tracked in a GitHub repository.

react-spring quick look

Before delving into practical application cases, let's take a quick look at springs, high animations, and transition animations. A runnable demonstration will be provided at the end of this section, so there is no need to worry about the confusion you may encounter during the process.

springs

Let's consider the classic "Hello world" of animation: the fading in and out of content. Let's pause and think about how to switch to show and hide without any animation. It looks like this:

 export default function App() {
  const [showing, setShowing] = useState(false);
  Return (
    <div>
      <div style="{{" opacity: showing :>
        This content will fade in and fade out
      </div>
      <button onclick="{()"> setShowing(val => !val)}>Toggle</button>
      <hr>
    </div>
  );
}
Copy after login

Simple, but boring. How do we animate changes in opacity? Wouldn't it be nice if we could declaratively set the desired opacity based on the state, as above, but letting these values ​​be smoothly animated? This is what react-spring does. React-spring can be considered as our middleman, which handles our ever-changing style values, resulting in a smooth transition between the animation values ​​we want. Like this:

 const [showA, setShowA] = useState(false);

const fadeStyles = useSpring({
  config: { ...config.stiff },
  from: { opacity: 0 },
  to: {
    opacity: showA ? 1 : 0
  }
});
Copy after login

We use from to specify the initial style value and specify the current value in to section according to the current state. The return value fadeStyles contains the actual style value we apply to the content. We just need to do the last thing...

You might think you could do this:

<div style="{fadeStyles}">
  ...
</div>
Copy after login

But this doesn't work. We cannot use normal div , but we need to use react-spring div created from animated export. This may sound confusing, but it actually just means:

<animated.div style="{fadeStyles}">
  ...
</animated.div>
Copy after login

That's it.

Highly animated

Depending on what we are animating, we may want the content to slide up and down, from zero height to its full size so that the surrounding content adjusts and flows into place smoothly. You might wish we could simply copy the code above, the height changes from zero to auto, but unfortunately, you can't animate it to auto height. This works in both normal CSS and react-spring. Instead, we need to know the actual height of the content and specify it in to section of spring.

We need to dynamically get the height of any content in order to pass its value to react-spring. It turns out that the web platform has designed something specifically for this: ResizeObserver . And its support is actually pretty good! Since we are using React, we will of course wrap the usage in a hook. My hook looks like this:

 export function useHeight({ on = true /* no value means on */ } = {} as any) {
  const ref = useRef<any> ();
  const [height, set] = useState(0);
  const heightRef = useRef(height);
  const [ro] = useState(
    () =>
      new ResizeObserver(packet => {
        if (ref.current && heightRef.current !== ref.current.offsetHeight) {
          heightRef.current = ref.current.offsetHeight;
          set(ref.current.offsetHeight);
        }
      })
  );
  useLayoutEffect(() => {
    if (on && ref.current) {
      set(ref.current.offsetHeight);
      ro.observe(ref.current, {});
    }
    return () => ro.disconnect();
  }, [on, ref.current]);
  return [ref, height as any];
}</any>
Copy after login

We can choose to provide an on value to toggle the enablement and disabling of the measurement function (this will come in handy later). When on is true, we tell ResizeObserver to observe our content. We return a ref that needs to be applied to whatever we want to measure, as well as the current height.

Let's see how it actually works.

 const [heightRef, height] = useHeight();
const slideInStyles = useSpring({
  config: { ...config.stiff },
  from: { opacity: 0, height: 0 },
  to: {
    opacity: showB ? 1 : 0,
    height: showB ? height: 0
  }
});

<animated.div style="{{" ...slideinstyles overflow:>
  <div ref="{heightRef}">
    This content will fade in and fade out with sliding
  </div>
</animated.div>
Copy after login

useHeight gives us the height value of the ref to measure and the content we pass it to our spring. Then we apply ref and apply height style.

Oh, don't forget to add overflow: hidden in the container. This allows us to correctly include the height values ​​we adjusted.

Animation transition

Finally, let's see how to add animated items to the DOM and remove animated items from the DOM. We already know how to animate an existing project and keep the value changes of the items in the DOM, but to animate adding or removing items, we need a new hook: useTransition .

If you've used react-spring before, this is one of the few places where the 9.x version has significant changes in its API. Let's take a look.

To animation the project list, like this:

 const [list, setList] = useState([]);
Copy after login

We will declare our transformation function as follows:

 const listTransitions = useTransition(list, {
  config: config.gentle,
  from: { opacity: 0, transform: "translate3d(-25%, 0px, 0px)" },
  enter: { opacity: 1, transform: "translate3d(0%, 0px, 0px)" },
  leave: { opacity: 0, height: 0, transform: "translate3d(25%, 0px, 0px)" },
  keys: list.map((item, index) => index)
});
Copy after login

As I mentioned earlier, the return value listTransitions is a function. react-spring is tracking the list array, tracking items that have been added and deleted. We call the listTransitions function, providing a callback function that accepts a single style object and a single item, react-spring will call it for each item in the list based on whether the item is newly added, newly deleted, or just located in the list, and use the correct style.

Note the keys section: This allows us to tell react-spring how to recognize objects in the list. In this case, I decided to tell react-spring the index of the item in the array to define the item uniquely. Normally, this is a bad idea, but now, it allows us to see how the feature actually works. In the following demonstration, the Add Project button adds the item to the end of the list when clicked, and the Delete Last Project button removes the most recently added item from the list. So if you type in the input box and quickly click the Add button and then click the Delete button, you will see the same item starts smoothly entering and then immediately leave from any stage of the animation. Instead, if you add an item and quickly hit the Delete button and Add button, the same item will start sliding, then suddenly stops in place and slide back to where it is.

Demo

Wow, I said so much! This is a runnable demo that shows everything we just covered.

[Demo link]

Other details

Have you noticed that when you slide content down in the demo, it bounces into place like…spring? This is where the name comes from: react-spring uses spring physics to interpolate our ever-changing values. It does not simply divide the value change into N equal increments and then apply these increments within N equal delays. Instead, it uses more complex algorithms to produce this spring-like effect, which will appear more natural.

The spring algorithm is fully configurable, with some presets you can use directly - the above demonstration uses stiff and gentle presets. Please refer to the documentation for more information.

Also note how I animate values ​​within translate3d values. As you can see, the syntax is not the most concise, so react-spring provides some shortcuts. There is documentation on this, but for the rest of this article, I will continue to use the full non-shortcut syntax for the sake of clarity.

Finally, I want to note that when you slide up the content in the above demo, you may see the content beneath it pounding a little at the end. This is due to the same bounce effect. It looks clear when the content bounces into place, but not when we slide the content upwards. Stay tuned for how we will turn it off. (spoiler, it's clamp attribute).

Some notes on these sandboxes

Code Sandbox uses hot reload. When you change the code, the changes are usually reflected immediately. This is cool, but it can be destructive to the animation. If you start tinkering and then see strange, ostensibly incorrect behavior, try refreshing the sandbox.

Other sandboxes in this article will use modality. For reasons I can't figure out completely, you won't be able to modify any code when the modal is turned on - the modal refuses to give up focus. So be sure to turn off the modal before trying any changes.

Build practical applications

These are the basic building blocks of react-spring. Let's use them to build something more interesting. Given all of the above, you might think react-spring is very easy to use. Unfortunately, in practice, figuring out some of the subtleties that need to be handled correctly can be tricky. Many of these details will be explored in depth in the rest of this article.

I’ve written before is related to my booklist project in some way. This post is no exception – it's not an obsession, it's just that the project happens to have a publicly available GraphQL endpoint and a lot of existing code that can be exploited, making it an obvious goal.

Let's build a UI that allows you to open modals and search for books. When the results appear, you can add them to the run list of selected books displayed below the modal. Once done, you can turn off the modal and click a button to find books similar to the selected book.

We will start with a fully functional UI and then gradually add animations to the sections, including interactive demos.

If you really want to know what the end result looks like, or if you are already familiar with react-spring and want to see if I cover something you don't know yet, here is it (it won't win any design awards, I'm pretty clear). The rest of this article will introduce the process of achieving the final state step by step.

Animate our modals

Let's start with our modality. Before we start adding any data, let's animate our modals very well. This is what a basic, unanimated modal looks like. I'm using Ryan Florence's Reach UI (particularly modal components), but the idea is the same no matter what you use to build the modal. We want to let our background fade in and convert our modal content.

Since the modal is conditionally rendered according to some "open" attribute, we will use useTransition hook. I've wrapped the Reach UI modal with my own modal component and rendered empty content or actual modal based on isOpen attribute. We just need to animate it by converting hooks.

Here is what the conversion hook looks like:

 const modalTransition = useTransition(!!isOpen, {
  config: isOpen ? { ...config.stiff } : { duration: 150 },
  from: { opacity: 0, transform: `translate3d(0px, -10px, 0px)` },
  enter: { opacity: 1, transform: `translate3d(0px, 0px, 0px)` },
  leave: { opacity: 0, transform: `translate3d(0px, 10px, 0px)` }
});
Copy after login

There are no big surprises here. We want to fade in and provide a slight vertical transition based on whether the modal is active or not. The weird part is this:

 config: isOpen ? { ...config.stiff } : { duration: 150 },
Copy after login

I just want to use spring physics when the modal is turned on. The reason for this - at least in my experience - is that when you turn off the modal, the background disappears for too long, which causes the underlying UI to be interactive for too long. So when the modal is on, it will bounce in place nicely by spring physics, and when closed, it will quickly disappear in 150 milliseconds.

Of course, we will present our content via the conversion function returned by the hook. Note that I'm extracting opacity styles from the style object to apply to the background and then applying all animation styles to the actual modal content.

 return modalTransition(
  (styles, isOpen) =>
    isOpen && (
      <animateddialogoverlay allowpinchzoom="{true}" initialfocusref="{focusRef}" isopen="{isOpen}" ondismiss="{onHide}" style="{{" opacity: styles.opacity>
        <animateddialogcontent style="{{" border: solid hsla borderradius: maxwidth:>
          <div>
            <div>
              <standardmodalheader caption="{headerCaption}" onhide="{onHide}"></standardmodalheader>
              {children}
            </div>
          </div>
        </animateddialogcontent>
      </animateddialogoverlay>
    )
);
Copy after login

[Demo link]

The remaining part is omitted because the length is too long and is repeated with the previous content. The core idea is to use react-spring's useSpring and useTransition hooks, combined with ResizeObserver to achieve various animation effects, including fading in and out, height changes, and entry and exit animations of list items. The article explains in detail how to deal with animation details, such as initial state, animation duration, and how to avoid animation conflicts. The final result is an interactive UI with smooth and user experience.

The above is the detailed content of Making Sense of react-spring. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. How to Fix Audio if You Can't Hear Anyone
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: How To Unlock Everything In MyRise
3 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Working With GraphQL Caching Working With GraphQL Caching Mar 19, 2025 am 09:36 AM

If you’ve recently started working with GraphQL, or reviewed its pros and cons, you’ve no doubt heard things like “GraphQL doesn’t support caching” or

Making Your First Custom Svelte Transition Making Your First Custom Svelte Transition Mar 15, 2025 am 11:08 AM

The Svelte transition API provides a way to animate components when they enter or leave the document, including custom Svelte transitions.

Show, Don't Tell Show, Don't Tell Mar 16, 2025 am 11:49 AM

How much time do you spend designing the content presentation for your websites? When you write a new blog post or create a new page, are you thinking about

Building an Ethereum app using Redwood.js and Fauna Building an Ethereum app using Redwood.js and Fauna Mar 28, 2025 am 09:18 AM

With the recent climb of Bitcoin’s price over 20k $USD, and to it recently breaking 30k, I thought it’s worth taking a deep dive back into creating Ethereum

What the Heck Are npm Commands? What the Heck Are npm Commands? Mar 15, 2025 am 11:36 AM

npm commands run various tasks for you, either as a one-off or a continuously running process for things like starting a server or compiling code.

How do you use CSS to create text effects, such as text shadows and gradients? How do you use CSS to create text effects, such as text shadows and gradients? Mar 14, 2025 am 11:10 AM

The article discusses using CSS for text effects like shadows and gradients, optimizing them for performance, and enhancing user experience. It also lists resources for beginners.(159 characters)

Creating Your Own Bragdoc With Eleventy Creating Your Own Bragdoc With Eleventy Mar 18, 2025 am 11:23 AM

No matter what stage you’re at as a developer, the tasks we complete—whether big or small—make a huge impact in our personal and professional growth.

Let's use (X, X, X, X) for talking about specificity Let's use (X, X, X, X) for talking about specificity Mar 24, 2025 am 10:37 AM

I was just chatting with Eric Meyer the other day and I remembered an Eric Meyer story from my formative years. I wrote a blog post about CSS specificity, and

See all articles