Skip to content

Commit 133da8d

Browse files
authored
feat: add exitBeforeEnter prop to useTransition (#1773)
* feat: add experimental exit-before-enter flag to useTransition * fix: useTransition tests * chore: alpha release * refactor: use different queues for exiting animations when prop is present * chore: add exitBeforeEnter demo * feat: add enterBeforeExit prop * chore: add exitbeforeenter demo to sandbox ci * chore: remove rogue logs
1 parent ca30818 commit 133da8d

File tree

19 files changed

+564
-87
lines changed

19 files changed

+564
-87
lines changed

.changeset/cool-avocados-eat.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@react-spring/core': minor
3+
'@react-spring/animated': minor
4+
'@react-spring/parallax': minor
5+
'@react-spring/rafz': minor
6+
'react-spring': minor
7+
'@react-spring/shared': minor
8+
'@react-spring/types': minor
9+
'@react-spring/konva': minor
10+
'@react-spring/native': minor
11+
'@react-spring/three': minor
12+
'@react-spring/web': minor
13+
'@react-spring/zdog': minor
14+
---
15+
16+
add experimental exit before enter

.codesandbox/ci.json

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
{
22
"packages": ["packages/*", "targets/*"],
33
"sandboxes": [
4+
"/demo/src/sandboxes/animating-auto",
45
"/demo/src/sandboxes/card",
5-
"/demo/src/sandboxes/goo-blobs",
6-
"/demo/src/sandboxes/flip-card",
7-
"/demo/src/sandboxes/slide",
8-
"/demo/src/sandboxes/draggable-list",
96
"/demo/src/sandboxes/cards-stack",
10-
"/demo/src/sandboxes/viewpager",
11-
"/demo/src/sandboxes/simple-transition",
7+
"/demo/src/sandboxes/chain",
8+
"/demo/src/sandboxes/css-keyframes",
9+
"/demo/src/sandboxes/draggable-list",
10+
"/demo/src/sandboxes/exit-before-enter",
11+
"/demo/src/sandboxes/flip-card",
12+
"/demo/src/sandboxes/goo-blobs",
1213
"/demo/src/sandboxes/image-fade",
1314
"/demo/src/sandboxes/list-reordering",
1415
"/demo/src/sandboxes/masonry",
15-
"/demo/src/sandboxes/animating-auto",
1616
"/demo/src/sandboxes/multistage-transition",
17-
"/demo/src/sandboxes/chain",
18-
"/demo/src/sandboxes/train",
19-
"/demo/src/sandboxes/svg-filter",
20-
"/demo/src/sandboxes/css-keyframes",
21-
"/demo/src/sandboxes/tree",
2217
"/demo/src/sandboxes/notification-hub",
23-
"/demo/src/sandboxes/rocket-decay",
2418
"/demo/src/sandboxes/parallax",
25-
"/demo/src/sandboxes/parallax-vert"
19+
"/demo/src/sandboxes/parallax-vert",
20+
"/demo/src/sandboxes/rocket-decay",
21+
"/demo/src/sandboxes/simple-transition",
22+
"/demo/src/sandboxes/svg-filter",
23+
"/demo/src/sandboxes/slide",
24+
"/demo/src/sandboxes/train",
25+
"/demo/src/sandboxes/tree",
26+
"/demo/src/sandboxes/viewpager"
2627
],
2728
"node": "14"
2829
}

demo/src/App.jsx

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,54 +2,69 @@ import React from 'react'
22
import { Link, Route } from 'wouter'
33
import styles from './styles.module.css'
44

5-
import GooBlobs from './sandboxes/goo-blobs/src/App'
5+
import AnimatingAuto from './sandboxes/animating-auto/src/App'
6+
67
import Card from './sandboxes/card/src/App'
7-
import FlipCard from './sandboxes/flip-card/src/App'
8-
import Slide from './sandboxes/slide/src/App'
9-
import DraggableList from './sandboxes/draggable-list/src/App'
108
import CardsStack from './sandboxes/cards-stack/src/App'
11-
import Viewpager from './sandboxes/viewpager/src/App'
12-
import SimpleTransition from './sandboxes/simple-transition/src/App'
9+
import Chain from './sandboxes/chain/src/App'
10+
import CssKeyframes from './sandboxes/css-keyframes/src/App'
11+
12+
import DecayRocket from './sandboxes/rocket-decay/src/App'
13+
import DraggableList from './sandboxes/draggable-list/src/App'
14+
15+
import ExitBeforeEnter from './sandboxes/exit-before-enter/src/App'
16+
17+
import FlipCard from './sandboxes/flip-card/src/App'
18+
19+
import GooBlobs from './sandboxes/goo-blobs/src/App'
20+
1321
import ImageFade from './sandboxes/image-fade/src/App'
22+
1423
import ListReordering from './sandboxes/list-reordering/src/App'
15-
import Chain from './sandboxes/chain/src/App'
24+
1625
import Masonry from './sandboxes/masonry/src/App'
17-
import AnimatingAuto from './sandboxes/animating-auto/src/App'
1826
import MultiStageTransition from './sandboxes/multistage-transition/src/App'
19-
import Trail from './sandboxes/trail/src/App'
20-
import SvgFilter from './sandboxes/svg-filter/src/App'
21-
import CssKeyframes from './sandboxes/css-keyframes/src/App'
27+
2228
import NotificationHub from './sandboxes/notification-hub/src/App'
23-
import Tree from './sandboxes/tree/src/App'
24-
import DecayRocket from './sandboxes/rocket-decay/src/App'
29+
2530
import Parallax from './sandboxes/parallax/src/App'
2631
import ParallaxVert from './sandboxes/parallax-vert/src/App'
2732
import ParallaxSticky from './sandboxes/parallax-sticky/src/App'
2833

34+
import SimpleTransition from './sandboxes/simple-transition/src/App'
35+
import Slide from './sandboxes/slide/src/App'
36+
import SvgFilter from './sandboxes/svg-filter/src/App'
37+
38+
import Trail from './sandboxes/trail/src/App'
39+
import Tree from './sandboxes/tree/src/App'
40+
41+
import Viewpager from './sandboxes/viewpager/src/App'
42+
2943
const links = {
44+
'animating-auto': AnimatingAuto,
3045
card: Card,
46+
'cards-stack': CardsStack,
3147
chain: Chain,
48+
'css-keyframes': CssKeyframes,
49+
'decay-rocket': DecayRocket,
50+
'draggable-list': DraggableList,
51+
'exit-before-enter': ExitBeforeEnter,
3252
'flip-card': FlipCard,
3353
'goo-blobs': GooBlobs,
34-
slide: Slide,
35-
'draggable-list': DraggableList,
36-
'cards-stack': CardsStack,
37-
viewpager: Viewpager,
38-
'simple-transition': SimpleTransition,
3954
'image-fade': ImageFade,
4055
'list-reordering': ListReordering,
4156
masonry: Masonry,
42-
'animating-auto': AnimatingAuto,
4357
'multistage-transition': MultiStageTransition,
44-
trail: Trail,
45-
'svg-filter': SvgFilter,
46-
'css-keyframes': CssKeyframes,
4758
'notification-hub': NotificationHub,
48-
tree: Tree,
49-
'decay-rocket': DecayRocket,
5059
parallax: Parallax,
5160
'parallax-vert': ParallaxVert,
5261
'parallax-sticky': ParallaxSticky,
62+
'simple-transition': SimpleTransition,
63+
slide: Slide,
64+
'svg-filter': SvgFilter,
65+
trail: Trail,
66+
tree: Tree,
67+
viewpager: Viewpager,
5368
}
5469

5570
const Example = ({ link }) => {
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "spring-exit-before-enter",
3+
"version": "1.0.0",
4+
"main": "src/index.tsx",
5+
"dependencies": {
6+
"@react-spring/web": "*",
7+
"react": "^17.0.1",
8+
"react-dom": "^17.0.1",
9+
"react-scripts": "4.0.3"
10+
},
11+
"scripts": {
12+
"start": "react-scripts start",
13+
"build": "react-scripts build",
14+
"test": "react-scripts test --env=jsdom",
15+
"eject": "react-scripts eject"
16+
},
17+
"browserslist": [
18+
">0.2%",
19+
"not dead",
20+
"not ie <= 11",
21+
"not op_mini all"
22+
],
23+
"bic": false,
24+
"devDependencies": {
25+
"@types/react": "^17.0.2",
26+
"@types/react-dom": "^17.0.1",
27+
"typescript": "^4.2.3"
28+
}
29+
}
Loading
Loading
Loading
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta
6+
name="viewport"
7+
content="width=device-width, initial-scale=1, shrink-to-fit=no"
8+
/>
9+
<meta name="theme-color" content="#000000" />
10+
<!--
11+
manifest.json provides metadata used when your web app is added to the
12+
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
13+
-->
14+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
15+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
16+
<!--
17+
Notice the use of %PUBLIC_URL% in the tags above.
18+
It will be replaced with the URL of the `public` folder during the build.
19+
Only files inside the `public` folder can be referenced from the HTML.
20+
21+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
22+
work correctly both with client-side routing and a non-root public URL.
23+
Learn how to configure a non-root public URL by running `npm run build`.
24+
-->
25+
<title>React Spring Sandbox</title>
26+
</head>
27+
28+
<body>
29+
<noscript> You need to enable JavaScript to run this app. </noscript>
30+
<div id="root"></div>
31+
<!--
32+
This HTML file is a template.
33+
If you open it directly in the browser, you will see an empty page.
34+
35+
You can add webfonts, meta tags, or analytics to this file.
36+
The build step will place the bundled scripts into the <body> tag.
37+
38+
To begin the development, run `npm start` or `yarn start`.
39+
To create a production bundle, use `npm run build` or `yarn build`.
40+
-->
41+
</body>
42+
</html>
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React, { useEffect, useLayoutEffect, useState } from 'react'
2+
import {
3+
useSpringRef,
4+
animated,
5+
useSpring,
6+
useTransition,
7+
config,
8+
} from '@react-spring/web'
9+
10+
import styles from './styles.module.css'
11+
12+
import IMG_01 from '../public/img_01.jpg'
13+
import IMG_02 from '../public/img_02.png'
14+
import IMG_03 from '../public/img_03.jpeg'
15+
16+
const IMAGES = [IMG_01, IMG_02, IMG_03]
17+
18+
export default function App() {
19+
const [activeIndex, setActiveIndex] = useState(0)
20+
const springApi = useSpringRef()
21+
22+
const transitions = useTransition(activeIndex, {
23+
from: {
24+
clipPath: 'polygon(0% 0%, 0% 100%, 0% 100%, 0% 0%)',
25+
},
26+
enter: {
27+
clipPath: 'polygon(0% 0%, 0% 100%, 100% 100%, 100% 0%)',
28+
},
29+
leave: {
30+
clipPath: 'polygon(100% 0%, 100% 100%, 100% 100%, 100% 0%)',
31+
},
32+
onRest: (_springs, _ctrl, item) => {
33+
if (activeIndex === item) {
34+
setActiveIndex(activeIndex === IMAGES.length - 1 ? 0 : activeIndex + 1)
35+
}
36+
},
37+
exitBeforeEnter: true,
38+
config: {
39+
duration: 4000,
40+
},
41+
ref: springApi,
42+
})
43+
44+
const springs = useSpring({
45+
from: {
46+
strokeDashoffset: 120,
47+
},
48+
to: {
49+
strokeDashoffset: 0,
50+
},
51+
config: {
52+
duration: 8000,
53+
},
54+
loop: true,
55+
ref: springApi,
56+
})
57+
58+
useLayoutEffect(() => {
59+
springApi.start()
60+
}, [activeIndex])
61+
62+
return (
63+
<div className={styles.container}>
64+
{transitions((springs, item) => (
65+
<animated.div className={styles.img__container} style={springs}>
66+
<img src={IMAGES[item]} />
67+
</animated.div>
68+
))}
69+
<div className={styles.ticker}>
70+
<div />
71+
<animated.svg
72+
width="40"
73+
height="40"
74+
viewBox="0 0 40 40"
75+
style={springs}>
76+
<path d="M19.9999 38.5001C17.5704 38.5001 15.1648 38.0216 12.9203 37.0919C10.6758 36.1622 8.63633 34.7995 6.91845 33.0816C5.20058 31.3638 3.83788 29.3243 2.90817 27.0798C1.97846 24.8353 1.49995 22.4296 1.49995 20.0002C1.49995 17.5707 1.97846 15.1651 2.90817 12.9206C3.83788 10.6761 5.20058 8.63663 6.91846 6.91875C8.63634 5.20087 10.6758 3.83818 12.9203 2.90847C15.1648 1.97876 17.5705 1.50024 19.9999 1.50024C22.4293 1.50024 24.835 1.97876 27.0795 2.90847C29.324 3.83818 31.3635 5.20088 33.0813 6.91876C34.7992 8.63663 36.1619 10.6761 37.0916 12.9206C38.0213 15.1651 38.4998 17.5707 38.4998 20.0002C38.4998 22.4296 38.0213 24.8353 37.0916 27.0798C36.1619 29.3243 34.7992 31.3638 33.0813 33.0816C31.3635 34.7995 29.324 36.1622 27.0795 37.0919C24.835 38.0216 22.4293 38.5001 19.9999 38.5001L19.9999 38.5001Z" />
77+
</animated.svg>
78+
</div>
79+
</div>
80+
)
81+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
html,
2+
body,
3+
#root {
4+
height: 100%;
5+
width: 100%;
6+
}
7+
8+
body {
9+
font-family: system-ui;
10+
margin: 0;
11+
}
12+
13+
*,
14+
*:after,
15+
*:before {
16+
box-sizing: border-box;
17+
}
18+
19+
.flex {
20+
display: flex;
21+
align-items: center;
22+
}
23+
24+
.flex.fill {
25+
height: 100%;
26+
}
27+
28+
.flex.center {
29+
justify-content: center;
30+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom'
3+
import App from './App'
4+
import './index.css'
5+
6+
const rootElement = document.getElementById('root')
7+
ReactDOM.render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>,
11+
rootElement
12+
)

0 commit comments

Comments
 (0)