Skip to content

Commit 00b1699

Browse files
authored
chore(demo): add css-gradient demo (pmndrs#2027)
1 parent feeb58b commit 00b1699

File tree

9 files changed

+217
-0
lines changed

9 files changed

+217
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"arrowParens": "avoid",
3+
"jsxBracketSameLine": true,
4+
"printWidth": 120,
5+
"semi": false,
6+
"singleQuote": true,
7+
"tabWidth": 2,
8+
"trailingComma": "es5"
9+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "CSS Gradients",
3+
"description": "Use interpolations & easings to create the CSS gradients you've been seeing all over the web.",
4+
"keywords": [
5+
"useSpring",
6+
"interpolation",
7+
"leva",
8+
"css",
9+
"gradients"
10+
],
11+
"version": "1.0.0",
12+
"main": "src/index.tsx",
13+
"dependencies": {
14+
"@react-spring/web": "*",
15+
"easing-coordinates": "2.0.2",
16+
"leva": "0.9.34",
17+
"react": "^18.0.0",
18+
"react-dom": "^18.0.0",
19+
"react-scripts": "5.0.1"
20+
},
21+
"scripts": {
22+
"start": "react-scripts start",
23+
"build": "react-scripts build",
24+
"test": "react-scripts test --env=jsdom",
25+
"eject": "react-scripts eject"
26+
},
27+
"browserslist": [
28+
">0.2%",
29+
"not dead",
30+
"not ie <= 11",
31+
"not op_mini all"
32+
],
33+
"devDependencies": {
34+
"@types/react": "^18.0.8",
35+
"@types/react-dom": "^18.0.3",
36+
"typescript": "^4.8.3"
37+
}
38+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
6+
<meta name="theme-color" content="#000000" />
7+
<!--
8+
manifest.json provides metadata used when your web app is added to the
9+
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
10+
-->
11+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
12+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
13+
<!--
14+
Notice the use of %PUBLIC_URL% in the tags above.
15+
It will be replaced with the URL of the `public` folder during the build.
16+
Only files inside the `public` folder can be referenced from the HTML.
17+
18+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
19+
work correctly both with client-side routing and a non-root public URL.
20+
Learn how to configure a non-root public URL by running `npm run build`.
21+
-->
22+
<title>React Spring Sandbox</title>
23+
</head>
24+
25+
<body>
26+
<noscript> You need to enable JavaScript to run this app. </noscript>
27+
<div id="root"></div>
28+
<!--
29+
This HTML file is a template.
30+
If you open it directly in the browser, you will see an empty page.
31+
32+
You can add webfonts, meta tags, or analytics to this file.
33+
The build step will place the bundled scripts into the <body> tag.
34+
35+
To begin the development, run `npm start` or `yarn start`.
36+
To create a production bundle, use `npm run build` or `yarn build`.
37+
-->
38+
</body>
39+
</html>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import * as React from 'react'
2+
import { cubicCoordinates, stepsCoordinates } from 'easing-coordinates'
3+
import { useSpring, animated, to as interpolate, createInterpolator } from '@react-spring/web'
4+
import { useControls } from 'leva'
5+
6+
import styles from './styles.module.css'
7+
8+
const easeMap = {
9+
'ease-in-out': { x1: 0.42, y1: 0, x2: 0.58, y2: 1 },
10+
'ease-out': { x1: 0, y1: 0, x2: 0.58, y2: 1 },
11+
'ease-in': { x1: 0.42, y1: 0, x2: 1, y2: 1 },
12+
ease: { x1: 0.25, y1: 0.1, x2: 0.25, y2: 1 },
13+
linear: { x1: 0.25, y1: 0.25, x2: 0.75, y2: 0.75 },
14+
}
15+
16+
export default function App() {
17+
const { from, mid, to, angle, stops, easeCustom, easing } = useControls({
18+
from: '#0bd1ff',
19+
mid: '#ffa3ff',
20+
to: '#ffd34e',
21+
angle: {
22+
value: 32,
23+
min: 0,
24+
max: 360,
25+
},
26+
stops: {
27+
value: 5,
28+
max: 100,
29+
min: 2,
30+
},
31+
easing: {
32+
value: 'ease-in-out',
33+
options: ['linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'steps'],
34+
},
35+
easeCustom: '',
36+
})
37+
38+
const { colorFrom, colorMid, colorTo } = useSpring({
39+
colorFrom: from,
40+
colorMid: mid,
41+
colorTo: to,
42+
})
43+
44+
const coordinates = React.useMemo(() => {
45+
let coordinates
46+
const customBezier = easeCustom.split(',').map(Number)
47+
if (customBezier.length <= 1) {
48+
if (easing === 'steps') {
49+
coordinates = stepsCoordinates(stops, 'skip-none')
50+
} else {
51+
const { x1, y1, x2, y2 } = easeMap[easing]
52+
coordinates = cubicCoordinates(x1, y1, x2, y2, stops)
53+
}
54+
} else {
55+
coordinates = cubicCoordinates(customBezier[0], customBezier[1], customBezier[2], customBezier[3], stops)
56+
}
57+
58+
return coordinates
59+
}, [easing, easeCustom, stops])
60+
61+
const allStops = interpolate([colorFrom, colorMid, colorTo], (from, mid, to) => {
62+
const blend = createInterpolator({ range: [0, 0.5, 1], output: [from, mid, to] })
63+
64+
return coordinates.map(({ x, y }) => {
65+
const color = blend(y)
66+
67+
return `${color} ${x * 100}%`
68+
})
69+
})
70+
71+
return (
72+
<animated.div
73+
className={styles.container}
74+
style={{
75+
backgroundImage: allStops.to((...args) => `linear-gradient(${angle}deg, ${args.join(', ')})`),
76+
}}
77+
/>
78+
)
79+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as React from 'react'
2+
import { createRoot } from 'react-dom/client'
3+
import App from './App'
4+
import './index.css'
5+
6+
const rootElement = document.getElementById('root')!
7+
createRoot(rootElement).render(
8+
<React.StrictMode>
9+
<App />
10+
</React.StrictMode>
11+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.container {
2+
width: 100%;
3+
height: 100%;
4+
}
2.3 MB
Loading
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"compilerOptions": {
3+
"outDir": "build/dist",
4+
"module": "esnext",
5+
"target": "es5",
6+
"lib": ["es6", "dom"],
7+
"sourceMap": true,
8+
"allowJs": true,
9+
"jsx": "react",
10+
"moduleResolution": "node",
11+
"rootDir": "src",
12+
"forceConsistentCasingInFileNames": true,
13+
"noImplicitReturns": true,
14+
"noImplicitThis": true,
15+
"noImplicitAny": true,
16+
"strictNullChecks": true,
17+
"suppressImplicitAnyIndexErrors": true,
18+
"noUnusedLocals": true
19+
}
20+
}

0 commit comments

Comments
 (0)