Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Decay (a.k.a intertia) with immediate #1444

Merged
merged 6 commits into from Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .codesandbox/ci.json
Expand Up @@ -20,6 +20,7 @@
"/demo/src/sandboxes/css-keyframes",
"/demo/src/sandboxes/tree",
"/demo/src/sandboxes/notification-hub",
"/demo/src/sandboxes/rocket-decay",
"/demo/src/sandboxes/parallax",
"/demo/src/sandboxes/parallax-vert"
],
Expand Down
1 change: 1 addition & 0 deletions demo/custom.d.ts
@@ -0,0 +1 @@
declare module 'vec-la'
1 change: 1 addition & 0 deletions demo/package.json
Expand Up @@ -21,6 +21,7 @@
"react-use-gesture": "^9.1.3",
"react-use-measure": "^2.0.4",
"styled-components": "^5.2.3",
"vec-la": "^1.5.0",
"wouter": "^2.7.4"
},
"devDependencies": {
Expand Down
Binary file added demo/public/r.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions demo/src/App.jsx
Expand Up @@ -21,6 +21,7 @@ import SvgFilter from './sandboxes/svg-filter/src/App'
import CssKeyframes from './sandboxes/css-keyframes/src/App'
import NotificationHub from './sandboxes/notification-hub/src/App'
import Tree from './sandboxes/tree/src/App'
import DecayRocket from './sandboxes/rocket-decay/src/App'
import Parallax from './sandboxes/parallax/src/App'
import ParallaxVert from './sandboxes/parallax-vert/src/App'

Expand All @@ -44,6 +45,7 @@ const links = {
'css-keyframes': CssKeyframes,
'notification-hub': NotificationHub,
tree: Tree,
'decay-rocket': DecayRocket,
parallax: Parallax,
'parallax-vert': ParallaxVert,
}
Expand Down
28 changes: 28 additions & 0 deletions demo/src/sandboxes/rocket-decay/package.json
@@ -0,0 +1,28 @@
{
"name": "initial-rocket",
"version": "1.0.0",
"description": "",
"keywords": [],
"homepage": "https://codesandbox.io/s/new",
"main": "src/index.js",
"bic": false,
"dependencies": {
"@react-spring/web": "*",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-scripts": "4.0.3",
"react-use-gesture": "9.1.3",
"vec-la": "1.5.0"
},
"devDependencies": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"typescript": "^4.2.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
43 changes: 43 additions & 0 deletions demo/src/sandboxes/rocket-decay/public/index.html
@@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React Use Gesture - Memo Rocket</title>
</head>

<body>
<noscript> You need to enable JavaScript to run this app. </noscript>

<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Binary file added demo/src/sandboxes/rocket-decay/public/r.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions demo/src/sandboxes/rocket-decay/src/App.tsx
@@ -0,0 +1,57 @@
import React from 'react'
import { useSpring, to, animated, config } from 'react-spring'
import { scale, dist } from 'vec-la'
import { useDrag } from 'react-use-gesture'

import styles from './styles.module.css'

export default function App() {
const [{ pos }, api] = useSpring(() => ({ pos: [0, 0] }))
const [{ angle }, angleApi] = useSpring(() => ({
angle: 0,
config: config.wobbly,
}))
// direction calculates pointer direction
// memo is like a cache, it contains the values that you return inside "set"
// this way we can inject the springs current coordinates on the initial event and
// add movement to it for convenience

const bind = useDrag(
({ xy, previous, down, movement: pos, velocity, direction }) => {
// console.log('DOWN', down)
// if (!down) {
// api.start({
// pos,
// config: { velocity: scale(direction, velocity), decay: true },
// })
// } else {
// api.start({
// pos,
// immediate: true,
// })
// }
// console.log('POS', pos)
api.start({
pos,
immediate: down,
config: { velocity: scale(direction, velocity), decay: true },
})

// if (dist(xy, previous) > 10 || !down)
// angleApi.start({ angle: Math.atan2(direction[0], -direction[1]) })
},
{ initial: () => pos.get() }
)
return (
<animated.div
className={styles.rocket}
{...bind()}
style={{
transform: to(
[pos, angle],
([x, y], a) => `translate3d(${x}px,${y}px,0) rotate(${a}rad)`
),
}}
/>
)
}
34 changes: 34 additions & 0 deletions demo/src/sandboxes/rocket-decay/src/index.css
@@ -0,0 +1,34 @@
html,
body,
#root {
height: 100%;
width: 100%;
}

body {
font-family: system-ui;
margin: 0;
}

#root {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
overflow: hidden;
user-select: none;
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir,
helvetica neue, helvetica, ubuntu, roboto, noto, segoe ui, arial, sans-serif;
font-size: 12px;
font-weight: 600;
display: flex;
justify-content: center;
align-items: center;
background: #3398db;
}

*,
*:after,
*:before {
box-sizing: border-box;
}
12 changes: 12 additions & 0 deletions demo/src/sandboxes/rocket-decay/src/index.tsx
@@ -0,0 +1,12 @@
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'

const rootElement = document.getElementById('root')
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
)
13 changes: 13 additions & 0 deletions demo/src/sandboxes/rocket-decay/src/styles.module.css
@@ -0,0 +1,13 @@
.rocket {
background: url(/r.png) no-repeat;
background-size: contain;
width: 140px;
height: 140px;
cursor: -webkit-grab;
will-change: transform;
user-select: none;
}

.rocket:active {
cursor: -webkit-grabbing;
}
2 changes: 1 addition & 1 deletion demo/tsconfig.json
Expand Up @@ -16,5 +16,5 @@
"noEmit": true,
"jsx": "react"
},
"include": ["./src"]
"include": ["./src", "custom.d.ts"]
}
32 changes: 28 additions & 4 deletions packages/core/src/SpringValue.ts
Expand Up @@ -335,14 +335,37 @@ export class SpringValue<T = any> extends FrameValue<T> {
})

const node = getAnimated(this)!
/**
* Get the node's current value, this will be different
* to anim.to when config.decay is true
*/
const currVal = node.getValue()
if (idle) {
const value = getFluidValue(anim.to)
if (node.setValue(value) || changed) {
this._onChange(value)
// get our final fluid val from the anim.to
const finalVal = getFluidValue(anim.to)
/**
* check if they're not equal, or if they're
* change and if there's no config.decay set
*/
if ((currVal !== finalVal || changed) && !config.decay) {
// set the value to anim.to
node.setValue(finalVal)
this._onChange(finalVal)
} else if (changed && config.decay) {
/**
* if it's changed but there is a config.decay,
* just call _onChange with currrent value
*/
this._onChange(currVal)
}
// call stop because the spring has stopped.
this._stop()
} else if (changed) {
this._onChange(node.getValue())
/**
* if the spring has changed, but is not idle,
* just call the _onChange handler
*/
this._onChange(currVal)
}
}

Expand Down Expand Up @@ -728,6 +751,7 @@ export class SpringValue<T = any> extends FrameValue<T> {

// Changing "decay" or "velocity" starts the animation.
if (
(!isEqual(anim.immediate, immediate) && !immediate) ||
!isEqual(config.decay, decay) ||
!isEqual(config.velocity, velocity)
) {
Expand Down