├── demo
└── src
│ ├── static
│ └── usa-flag.png
│ ├── crate.js
│ ├── Demo.js
│ ├── docs
│ └── Api.js
│ ├── Loading.js
│ ├── index.js
│ ├── style.css
│ ├── demos
│ ├── Trippy.js
│ ├── List.js
│ ├── Slider.js
│ ├── ColorSearch.js
│ ├── America.js
│ ├── Scatterplot.js
│ └── svg-paths.js
│ └── App.js
├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── .babelrc
├── src
├── __tests__
│ ├── __snapshots__
│ │ └── index.js.snap
│ └── index.js
└── index.js
├── rollup.config.js
├── LICENSE
├── README.md
├── docs
└── api.md
└── package.json
/demo/src/static/usa-flag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tkh44/data-driven-motion/HEAD/demo/src/static/usa-flag.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /demo/dist
3 | /dist
4 | /es
5 | /lib
6 | /node_modules
7 | /umd
8 | npm-debug.log
9 | .idea
10 |
--------------------------------------------------------------------------------
/demo/src/crate.js:
--------------------------------------------------------------------------------
1 | import { Crate } from 'react-crate'
2 | import loadable from 'react-crate/lib/loadable'
3 |
4 | export default Crate.of({
5 | loadable
6 | })
7 |
--------------------------------------------------------------------------------
/demo/src/Demo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default ({ style, tall = false, children }) => {
4 | return (
5 |
Loading...
: null // Don't flash "Loading..." when we don't need to.
6 | } else if (error) {
7 | return (
8 | {
21 | this.node = node
22 | }}
23 | style={{
24 | flex: 1,
25 | width: '100%',
26 | height: '100%',
27 | backgroundColor: '#212529',
28 | perspective: 1000,
29 | overflow: 'hidden'
30 | }}
31 | onMouseMove={({ pageX: x, pageY: y }) => this.setState({ x, y })}
32 | onMouseDown={() => this.setState({ mouseDown: true })}
33 | onMouseUp={() => this.setState({ mouseDown: false })}
34 | onMouseEnter={() => {
35 | const rect = this.node.getBoundingClientRect()
36 | this.setState({ offsetTop: rect.top, offsetLeft: rect.left })
37 | }}
38 | onMouseLeave={() => this.setState({ mouseDown: false })}
39 | />
40 | ),
41 | getKey: data => data.key,
42 | onComponentMount: () => ({ x, y, z: 0, hue: 0 }),
43 | onRender: (data, i, spring) => ({
44 | x: spring(x, WOBBLY_SPRING),
45 | y: spring(y, WOBBLY_SPRING),
46 | z: spring(this.state.mouseDown ? i * 30 : 0, WOBBLY_SPRING),
47 | hue: spring((x * i + y * i) / 2 % 360)
48 | }),
49 | onRemount: () => ({ x, y, z: 0, hue: 0 }), // Does not matter since data does not change
50 | onUnmount: () => ({ x, y, z: 0, hue: 0 }), // Does not matter since data does not change
51 | render: this.renderCircle
52 | })
53 | }
54 |
55 | renderCircle = (key, data, style, dataIndex, layerIndex) => {
56 | return (
57 |
74 | )
75 | };
76 | }
77 |
78 | export default () => {
79 | return (
80 |
81 |
82 |
91 | {'Move mouse & Hold mouse down'}
92 |
93 |
94 | )
95 | }
96 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "data-driven-motion",
3 | "version": "0.0.10",
4 | "description": "Declarative animation in react.",
5 | "jsnext:main": "dist/data-driven-motion.es.js",
6 | "module": "dist/data-driven-motion.es.js",
7 | "main": "dist/data-driven-motion.js",
8 | "umd:main": "dist/data-driven-motion.umd.js",
9 | "files": [
10 | "dist"
11 | ],
12 | "scripts": {
13 | "build": "npm-run-all clean -p rollup -p minify:* -s size",
14 | "clean": "rimraf dist",
15 | "test": "standard src test && jest --coverage",
16 | "test:watch": "jest --watch",
17 | "rollup": "rollup -c",
18 | "minify:cjs": "uglifyjs $npm_package_main -cm toplevel -o $npm_package_main -p relative --in-source-map ${npm_package_main}.map --source-map ${npm_package_main}.map",
19 | "minify:umd": "uglifyjs $npm_package_umd_main -cm -o $npm_package_umd_main -p relative --in-source-map ${npm_package_umd_main}.map --source-map ${npm_package_umd_main}.map",
20 | "size": "echo \"Gzipped Size: $(strip-json-comments --no-whitespace $npm_package_main | gzip-size)\"",
21 | "release": "npm run test && npm run build && npm version patch && npm publish && git push --tags"
22 | },
23 | "dependencies": {
24 | "prop-types": "^15.5.10",
25 | "react-motion": "^0.4.7"
26 | },
27 | "peerDependencies": {
28 | "react": "15.x"
29 | },
30 | "devDependencies": {
31 | "babel-eslint": "^7.2.1",
32 | "babel-jest": "^19.0.0",
33 | "babel-polyfill": "^6.23.0",
34 | "babel-preset-env": "^1.4.0",
35 | "babel-preset-react": "^6.24.1",
36 | "babel-preset-stage-2": "^6.24.1",
37 | "color-name": "^1.1.2",
38 | "d3-array": "^1.1.1",
39 | "d3-random": "^1.0.3",
40 | "d3-scale": "^1.0.5",
41 | "d3-shape": "^1.0.6",
42 | "gh-pages": "^0.12.0",
43 | "github-markdown-css": "^2.5.0",
44 | "gzip-size-cli": "^2.0.0",
45 | "html-loader": "^0.4.5",
46 | "jest": "^20.0.1",
47 | "markdown-loader": "^2.0.0",
48 | "npm-run-all": "^4.0.2",
49 | "open-color": "^1.5.1",
50 | "preact": "^8.0.0",
51 | "preact-compat": "^3.14.3",
52 | "prettier": "^0.20.0",
53 | "pretty-bytes-cli": "^2.0.0",
54 | "raw-loader": "^0.5.1",
55 | "react": "^15.5.4",
56 | "react-addons-test-utils": "^15.5.1",
57 | "react-crate": "^2.0.0",
58 | "react-dom": "^15.5.4",
59 | "react-loadable": "^3.0.1",
60 | "react-motion": "^0.4.7",
61 | "react-router-dom": "^4.0.0",
62 | "react-svg-morph": "^0.1.10",
63 | "react-test-renderer": "^15.5.4",
64 | "rebass": "^0.4.0-beta.9",
65 | "recompose": "^0.22.0",
66 | "rgb-to-hsl": "0.0.3",
67 | "rimraf": "^2.6.1",
68 | "rollup": "^0.41.6",
69 | "rollup-plugin-babel": "^2.7.1",
70 | "rollup-plugin-commonjs": "^8.0.2",
71 | "rollup-plugin-node-resolve": "^3.0.0",
72 | "standard": "^10.0.2",
73 | "strip-json-comments-cli": "^1.0.1",
74 | "uglify-js": "^2.8.22"
75 | },
76 | "author": "Kye Hohenberger",
77 | "homepage": "https://github.com/tkh44/data-driven-motion#readme",
78 | "license": "MIT",
79 | "repository": {
80 | "type": "git",
81 | "url": "git+https://github.com/tkh44/data-driven-motion.git"
82 | },
83 | "keywords": [
84 | "react",
85 | "react-motion",
86 | "animtion",
87 | "react-animation",
88 | "data",
89 | "driven",
90 | "motion"
91 | ],
92 | "directories": {
93 | "test": "test"
94 | },
95 | "bugs": {
96 | "url": "https://github.com/tkh44/data-driven-motion/issues"
97 | },
98 | "eslintConfig": {
99 | "extends": "standard",
100 | "parser": "babel-eslint"
101 | },
102 | "standard": {
103 | "parser": "babel-eslint",
104 | "ignore": [
105 | "/dist/",
106 | "/demo/"
107 | ]
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/demo/src/demos/List.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Motion } from '../../../src'
3 | import Demo from '../Demo'
4 |
5 | let items = []
6 | for (let i = 100; i > 0; i--) {
7 | items[i] = { id: i, name: `Item ${i}`, username: `User ${i}` }
8 | }
9 | const WOBBLY_SPRING = { stiffness: 280, damping: 30 }
10 |
11 | class ListDemo extends Component {
12 | state = {
13 | filter: 2
14 | };
15 |
16 | render () {
17 | return (
18 |
21 |
this.setState({ filter: value })}
32 | />
33 |
40 | Divisible by {this.state.filter}
41 |
42 |
d.id % this.state.filter === 0)
45 | .map((d, i) => {
46 | d.index = i
47 | return d
48 | })}
49 | />
50 |
51 | )
52 | }
53 | }
54 |
55 | class ListWrapper extends Component {
56 | render () {
57 | return (
58 |
68 | }
69 | getKey={(data, i) => i + ''}
70 | onComponentMount={(data, i) => {
71 | return {
72 | opacity: 1,
73 | hue: i * 4 % 360,
74 | xOffset: 0,
75 | yOffset: i * 14
76 | }
77 | }}
78 | onRender={(data, i, spring) => {
79 | return {
80 | opacity: spring(1),
81 | hue: spring(i * 4 % 360),
82 | xOffset: spring(0, WOBBLY_SPRING),
83 | yOffset: spring(i * 16, WOBBLY_SPRING)
84 | }
85 | }}
86 | onRemount={({ key, data, style }) => {
87 | return {
88 | opacity: 1,
89 | hue: 0,
90 | xOffset: -100,
91 | yOffset: 0
92 | }
93 | }}
94 | onUnmount={({ key, data, style }, spring) => {
95 | return {
96 | opacity: spring(0),
97 | hue: spring(0),
98 | xOffset: spring(100, WOBBLY_SPRING),
99 | yOffset: spring(0, WOBBLY_SPRING)
100 | }
101 | }}
102 | render={[this.renderLi, this.renderLi]}
103 | />
104 | )
105 | }
106 |
107 | renderLi = (key, data, style, dataIndex, layerIndex) => {
108 | return (
109 |
124 | {data.username}
125 |
126 | )
127 | };
128 | }
129 |
130 | export default () => {
131 | return (
132 |
133 |
134 |
135 | )
136 | }
137 |
--------------------------------------------------------------------------------
/demo/src/demos/Slider.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PureComponent } from 'react'
2 | import { Motion } from '../../../src'
3 | import Demo from '../Demo'
4 |
5 | const WOBBLY_SPRING = { stiffness: 420, damping: 80 }
6 |
7 | let DATA = []
8 | for (let i = 0; i < 5; i++) {
9 | DATA[i] = {
10 | id: i + 1 + '',
11 | text: `Slide ${i + 1}`,
12 | url: `//lorempixel.com/800/450/nature/${i}`
13 | }
14 | }
15 |
16 | class Slider extends PureComponent {
17 | state = {
18 | slide: 0
19 | };
20 |
21 | componentDidMount () {
22 | this.interval = window.setInterval(
23 | () => {
24 | this.setState(prev => ({ slide: (prev.slide + 1) % DATA.length }))
25 | },
26 | 2000
27 | )
28 | }
29 |
30 | componentWillUnmount () {
31 | window.clearInterval(this.interval)
32 | }
33 |
34 | render () {
35 | return (
36 |
39 | this.setState(prev => ({ slide: (prev.slide + 1) % DATA.length }))}
40 | >
41 |
42 |
43 | )
44 | }
45 | }
46 |
47 | class Slides extends Component {
48 | render () {
49 | return (
50 |
63 | }
64 | getKey={(data, i) => data.id}
65 | onComponentMount={(data, i) => {
66 | return {
67 | opacity: 1,
68 | x: this.props.slide * -100,
69 | textX: this.props.slide * -100
70 | }
71 | }}
72 | onRender={(data, i, spring) => {
73 | return {
74 | opacity: spring(1),
75 | x: spring(this.props.slide * -100, WOBBLY_SPRING),
76 | textX: spring(this.props.slide * -100, {
77 | stiffness: 200,
78 | damping: 80
79 | })
80 | }
81 | }}
82 | render={[this.renderSlide, this.renderText]}
83 | />
84 | )
85 | }
86 |
87 | renderSlide = (key, data, style, dataIndex, layerIndex) => {
88 | return (
89 |
101 |
108 |
109 | )
110 | };
111 |
112 | renderText = (key, data, style, dataIndex, layerIndex) => {
113 | return (
114 |
126 |
134 | {data.text}
135 |
136 |
137 | )
138 | };
139 | }
140 |
141 | export default () => {
142 | return (
143 |
144 |
145 |
146 | )
147 | }
148 |
--------------------------------------------------------------------------------
/demo/src/demos/ColorSearch.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Motion } from '../../../src'
3 | import Demo from '../Demo'
4 | import colorMap from 'color-name'
5 | import rgbToHsl from 'rgb-to-hsl'
6 |
7 | const STIFF_SPRING = { stiffness: 200, damping: 20 }
8 | const colors = Object.keys(colorMap).map((name, i) => ({
9 | name,
10 | index: i,
11 | rgb: colorMap[name],
12 | hsl: rgbToHsl(...colorMap[name])
13 | }))
14 |
15 | class Input extends Component {
16 | render () {
17 | return (
18 |
41 | )
42 | }
43 | }
44 |
45 | class ColorList extends Component {
46 | render () {
47 | return (
48 |
64 | }
65 | getKey={this.getKey}
66 | onComponentMount={this.onComponentMount}
67 | onRender={this.onRender}
68 | onRemount={this.onRemount}
69 | render={this.renderResult}
70 | />
71 | )
72 | }
73 |
74 | getKey = (data, i) => data.name;
75 |
76 | onComponentMount = (data, i) => ({
77 | width: 1 / (this.props.data.length / 100),
78 | o: 0
79 | });
80 |
81 | onRender = (data, i, spring) => ({
82 | width: data.found
83 | ? spring(1 / (this.props.foundCount / 100), STIFF_SPRING)
84 | : 0,
85 | o: spring(1, STIFF_SPRING)
86 | });
87 |
88 | renderResult = (key, data, { width, o }) => {
89 | return (
90 |
105 | )
106 | };
107 | }
108 |
109 | class ColorSearch extends Component {
110 | state = { colorName: '' };
111 |
112 | render () {
113 | const { colorName } = this.state
114 | let foundCount = 0
115 | const data = colors.map(color => {
116 | const found = color.name.includes(colorName)
117 | if (found) ++foundCount
118 | return { ...color, found }
119 | })
120 |
121 | return (
122 |
123 |
124 |
125 |
126 | )
127 | }
128 |
129 | handleInputChange = ({ target: { value: colorName } }) => {
130 | this.setState(() => ({colorName}))
131 | };
132 | }
133 |
134 | export default () => (
135 |
136 |
137 |
138 | )
139 |
--------------------------------------------------------------------------------
/demo/src/demos/America.js:
--------------------------------------------------------------------------------
1 | import flagImgUrl from '../static/usa-flag.png'
2 | import React, { Component, PureComponent } from 'react'
3 | import { Motion } from '../../../src'
4 | import Demo from '../Demo'
5 | import { normalizePaths, getProgress } from 'react-svg-morph/lib/utils/morph'
6 | import scalePath from 'react-svg-morph/lib/utils/scalePath'
7 | import { EAGLE_PATH, USA_PATH } from './svg-paths'
8 |
9 | const WOBBLY_SPRING = { stiffness: 200, damping: 15 }
10 | const width = 800
11 | const height = 450
12 |
13 | class America extends PureComponent {
14 | render () {
15 | const { from: usa, to: eagle } = normalizePaths(
16 | [{ path: scalePath(USA_PATH, height, 640, 412), trans: {} }],
17 | [{ path: scalePath(EAGLE_PATH, height, 794, 1122), trans: {} }]
18 | )
19 |
20 | return (
21 |
27 |
28 |
34 |
41 |
42 |
43 |
44 |
45 | )
46 | }
47 | }
48 |
49 | class Patriot extends Component {
50 | render () {
51 | return (
52 |
}
55 | getKey={this.getKey}
56 | onComponentMount={this.onCpm}
57 | onRender={this.onRender}
58 | render={this.renderAmerica}
59 | />
60 | )
61 | }
62 |
63 | getKey = (data, i) => i + '-USA';
64 |
65 | onCpm = (data, i) => ({ val: data });
66 |
67 | onRender = (data, i, spring) => ({ val: spring(data, WOBBLY_SPRING) });
68 |
69 | renderAmerica = (key, data, { val }, dataIndex) => {
70 | const [snapshot] = getProgress(this.props.usa, this.props.eagle, val)
71 |
72 | return (
73 |
80 | )
81 | };
82 | }
83 |
84 | export default class extends Component {
85 | constructor (props) {
86 | super(props)
87 |
88 | this.state = { val: 0 }
89 | }
90 |
91 | render () {
92 | return (
93 |
94 |
109 | this.setState({ val: value / 100 })}
110 | />
111 |
114 | this.setState(prev => ({ val: prev.val === 0 ? 1 : 0 }))}
115 | >
116 | Toggle
117 |
118 |
119 |
120 | )
121 | }
122 | }
123 |
124 | const buttonStyle = {
125 | position: 'absolute',
126 | top: 8,
127 | right: 8,
128 | backgroundColor: 'transparent',
129 | color: '#37b24d',
130 | fontWeight: 'bold',
131 | borderRadius: 4,
132 | height: 32,
133 | lineHeight: 2.5,
134 | paddingLeft: 16,
135 | paddingRight: 16,
136 | outline: 'none',
137 | border: '1px solid #37b24d'
138 | }
139 |
--------------------------------------------------------------------------------
/demo/src/demos/Scatterplot.js:
--------------------------------------------------------------------------------
1 | // Inspired by https://bl.ocks.org/mbostock/4060954
2 | import React, { Component, PureComponent } from 'react'
3 | import { Motion } from '../../../src'
4 | import Demo from '../Demo'
5 | import { scaleLinear } from 'd3-scale'
6 | import { min, max } from 'd3-array'
7 | import { randomUniform } from 'd3-random'
8 | const WOBBLY_SPRING = { stiffness: 60, damping: 15 }
9 |
10 | const radiusGenerator = randomUniform(2, 8)
11 | function randomRadius () {
12 | return radiusGenerator()
13 | }
14 |
15 | const width = 800
16 | const height = 450
17 |
18 | class Scatterplot extends PureComponent {
19 | render () {
20 | const { points } = this.props
21 | const maxY = max(points, d => d[1])
22 | const minY = min(points, d => d[1])
23 | const maxX = max(points, d => d[0])
24 | const xScale = scaleLinear().domain([0, maxX]).range([0, width])
25 | const yScale = scaleLinear().domain([minY, maxY]).range([height, 0])
26 |
27 | return (
28 |
34 |
42 |
43 | )
44 | }
45 | }
46 |
47 | class Layer extends Component {
48 | render () {
49 | return (
50 |
}
53 | getKey={this.getKey}
54 | onComponentMount={this.onCpm}
55 | onRender={this.onRender}
56 | onRemount={this.onRe}
57 | onUnmount={this.onUn}
58 | render={this.renderCircle}
59 | />
60 | )
61 | }
62 |
63 | getKey = (data, i) => i + '-' + (randomUniform(1, 2)() | 0);
64 |
65 | onCpm = (data, i) => {
66 | return {
67 | opacity: 0,
68 | r: 0,
69 | x: this.props.xScale(0),
70 | y: this.props.yScale(i % 2 ? this.props.maxY : 0)
71 | }
72 | };
73 |
74 | onRender = (data, i, spring) => {
75 | return {
76 | opacity: spring(1),
77 | r: spring(data.radius),
78 | x: spring(this.props.xScale(data[0]), WOBBLY_SPRING),
79 | y: spring(this.props.yScale(data[1]), WOBBLY_SPRING)
80 | }
81 | };
82 |
83 | onRe = ({ key, data, style }, i) => {
84 | return {
85 | opacity: 0,
86 | r: 0,
87 | x: this.props.xScale(0),
88 | y: this.props.yScale(i % 2 ? this.props.maxY : 0)
89 | }
90 | };
91 |
92 | onUn = ({ key, data, style }, spring) => {
93 | return {
94 | opacity: spring(0),
95 | r: spring(0),
96 | x: spring(this.props.xScale(this.props.maxX), WOBBLY_SPRING),
97 | y: spring(this.props.yScale(this.props.minY), WOBBLY_SPRING)
98 | }
99 | };
100 |
101 | renderCircle = (key, data, style, dataIndex) => {
102 | return (
103 |
111 | )
112 | };
113 | }
114 |
115 | export default class extends Component {
116 | constructor (props) {
117 | super(props)
118 |
119 | this.state = {
120 | points: this.generateLayers(5)
121 | }
122 | }
123 |
124 | componentDidMount () {
125 | this.interval = window.setInterval(
126 | () => {
127 | return this.setState({ points: this.generateLayers(randomUniform(1, 500)() | 0) })
128 | },
129 | 1000
130 | )
131 | }
132 |
133 | componentWillUnmount () {
134 | window.clearInterval(this.interval)
135 | }
136 |
137 | render () {
138 | return (
139 |
140 |
141 | {`Animating ${this.state.points.length} circle elements`}
142 |
143 |
144 |
145 | )
146 | }
147 |
148 | generateLayers = itemCount => {
149 | let points = []
150 | for (let i = itemCount; i > -1; i--) {
151 | points[i] = [i, Math.random() * 100 | 0]
152 | points[i].radius = randomRadius()
153 | }
154 |
155 | return points
156 | };
157 | }
158 |
159 | const buttonStyle = {
160 | position: 'absolute',
161 | top: 8,
162 | right: 8,
163 | backgroundColor: 'transparent',
164 | color: '#37b24d',
165 | fontWeight: 'bold',
166 | borderRadius: 4,
167 | height: 32,
168 | lineHeight: 2.5,
169 | paddingLeft: 16,
170 | paddingRight: 16,
171 | border: 'none',
172 | outline: 'none'
173 | }
174 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import {TransitionMotion, spring} from 'react-motion'
4 |
5 | const h = React.createElement
6 |
7 | export class Motion extends React.PureComponent {
8 | constructor (props) {
9 | super(props)
10 |
11 | this.renderCurrentStyles = this.renderCurrentStyles.bind(this)
12 | this.getKey = this.getKey.bind(this)
13 | this.getDefaultStyles = this.getDefaultStyles.bind(this)
14 | this.getStyles = this.getStyles.bind(this)
15 | this.willLeave = this.willLeave.bind(this)
16 | this.willEnter = this.willEnter.bind(this)
17 | }
18 |
19 | render () {
20 | const {data, onRemount, onUnmount, onComponentMount} = this.props
21 |
22 | return h(TransitionMotion, {
23 | defaultStyles: data.length && onComponentMount
24 | ? this.getDefaultStyles()
25 | : undefined,
26 | styles: this.getStyles(),
27 | willEnter: onRemount && this.willEnter,
28 | willLeave: onUnmount && this.willLeave,
29 | children: this.renderCurrentStyles
30 | })
31 | }
32 |
33 | renderCurrentStyles (currentStyles) {
34 | const {component, render} = this.props
35 | let children
36 | if (Array.isArray(render)) {
37 | // If render is an array, children becomes and array of arrays.
38 | // This is useful if you want to create layers of animation
39 | children = new Array(render.length)
40 | for (let i = 0; i < render.length; ++i) {
41 | children[i] = new Array(currentStyles.length)
42 | for (let j = 0; j < currentStyles.length; ++j) {
43 | const {key, data, style} = currentStyles[j]
44 | children[i][j] = render[i](key, data, style, j, i)
45 | }
46 | }
47 | } else {
48 | children = new Array(currentStyles.length)
49 | for (let j = 0; j < currentStyles.length; ++j) {
50 | const {key, data, style} = currentStyles[j]
51 | children[j] = render(key, data, style, j, 0)
52 | }
53 | }
54 |
55 | return React.cloneElement(component, {}, children)
56 | }
57 |
58 | getKey (data, i) {
59 | const {getKey} = this.props
60 | return getKey(data, i)
61 | }
62 |
63 | getDefaultStyles () {
64 | const {
65 | getKey,
66 | onComponentMount,
67 | data
68 | } = this.props
69 |
70 | let mappedData = new Array(data.length)
71 | for (let i = 0; i < data.length; ++i) {
72 | mappedData[i] = {
73 | key: getKey(data[i], i),
74 | data: data[i],
75 | style: onComponentMount(data[i], i)
76 | }
77 | }
78 |
79 | return mappedData
80 | }
81 |
82 | getStyles () {
83 | const {
84 | getKey,
85 | onRender,
86 | data
87 | } = this.props
88 |
89 | let mappedData = new Array(data.length)
90 | for (let i = 0; i < data.length; ++i) {
91 | mappedData[i] = {
92 | key: getKey(data[i], i),
93 | data: data[i],
94 | style: onRender(data[i], i, spring)
95 | }
96 | }
97 |
98 | return mappedData
99 | }
100 |
101 | willEnter (config) {
102 | const {onRemount} = this.props
103 | return onRemount(config)
104 | }
105 |
106 | willLeave (config) {
107 | const {onUnmount} = this.props
108 | return onUnmount(config, spring)
109 | }
110 | }
111 |
112 | Motion.defaultProps = {
113 | component: h('div'),
114 | data: []
115 | }
116 |
117 | Motion.propTypes = {
118 | // wrapper element ex:
119 | component: PropTypes.element,
120 |
121 | // array of data
122 | data: PropTypes.array,
123 |
124 | // (key, data, style) => ReactElement
125 | // called on render for each item in `data` with the key from `getKey`, `data` from `data[index]`, and
126 | // `style`, the result of: `onComponentMount`, `onRender`, `onRemount`, or `onUnmount`
127 | //
128 | // If an array is provided it will render the resulting elements in order.
129 | // This is useful for creating animated layers.
130 | render: PropTypes.oneOfType([
131 | PropTypes.func,
132 | PropTypes.arrayOf(PropTypes.func)
133 | ]),
134 |
135 | // (data, i) => string
136 | // help identify which items have been changed, added, or are removed
137 | // https://facebook.github.io/react/docs/lists-and-keys.html
138 | getKey: PropTypes.func,
139 |
140 | // (data, i) => Style object
141 | // data === props.data[i]
142 | // called when `component` mounts
143 | // do not wrap values in springs
144 | onComponentMount: PropTypes.func,
145 |
146 | // (data, i, spring) => Style object
147 | // data === props.data[i]
148 | // called on every render
149 | // ok to wrap values in springs
150 | onRender: PropTypes.func,
151 |
152 | // ({ data }, i) => Style object
153 | // data === props.data[i]
154 | // do not wrap values in springs
155 | onRemount: PropTypes.func,
156 |
157 | // ({ data }, i, spring) => Style object
158 | // data === props.data[i]
159 | // ok to wrap values in springs
160 | onUnmount: PropTypes.func
161 | }
162 |
--------------------------------------------------------------------------------
/demo/src/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {
3 | HashRouter as Router,
4 | Route,
5 | Link,
6 | matchPath,
7 | withRouter
8 | } from 'react-router-dom'
9 | import crate from './crate'
10 | import Loading from './Loading'
11 | import { Motion } from '../../src'
12 | import colors from 'open-color'
13 | import TrippyDemo from './demos/Trippy'
14 | import ListDemo from './demos/List'
15 | import Scatterplot from './demos/Scatterplot'
16 | import America from './demos/America'
17 | import Slider from './demos/Slider'
18 | import ColorSearch from './demos/ColorSearch'
19 |
20 | const WOBBLY_SPRING = { stiffness: 350, damping: 15, precision: 0.1 }
21 |
22 | const ApiDocs = crate.asyncCompile({
23 | loader: () => import('./docs/Api'),
24 | LoadingComponent: Loading,
25 | delay: 200
26 | })
27 |
28 | // const TrippyDemo = crate.asyncCompile({
29 | // loader: () => import('./demos/Trippy'),
30 | // LoadingComponent: Loading,
31 | // delay: 200
32 | // })
33 | //
34 | // const ListDemo = crate.asyncCompile({
35 | // loader: () => import('./demos/List'),
36 | // LoadingComponent: Loading,
37 | // delay: 200
38 | // })
39 |
40 | const HomePage = ({ style }) => {
41 | return (
42 |
43 |
44 |
data-driven-motion
45 |
Easily animate your data in react
46 |
47 | npm install -S data-driven-motion
48 |
49 |
71 |
72 |
73 | )
74 | }
75 |
76 | const DemosPage = ({ style }) => (
77 |
78 |
79 |
80 | SVG Path Transformation
81 |
85 | Source
86 |
87 |
88 |
89 |
90 |
91 | Scatter Plot (1 - 500 elements)
92 |
96 | Source
97 |
98 |
99 |
100 |
101 |
110 |
111 |
112 |
121 |
122 |
123 |
124 | Trippy Perspective
125 |
129 | Source
130 |
131 |
132 |
133 |
134 |
135 | List with multiple layers
136 |
140 | Source
141 |
142 |
143 |
144 |
145 |
146 | )
147 |
148 | const AnimationExample = () => (
149 |
150 |
151 |
161 |
162 | data-driven-motion
163 |
164 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 | )
195 |
196 | const AnimatedSwitch = withRouter(
197 | class AnimatedSwitch extends React.Component {
198 | render () {
199 | const { children, route, style } = this.props
200 | const location = this.props.location || this.context.route.location
201 | let match, child
202 | React.Children.forEach(children, element => {
203 | if (!React.isValidElement(element)) return
204 |
205 | const { path: pathProp, exact, strict, from } = element.props
206 | const path = pathProp || from
207 |
208 | if (match == null) {
209 | child = element
210 | match = path
211 | ? matchPath(location.pathname, { path, exact, strict })
212 | : route.match
213 | }
214 | })
215 |
216 | return (
217 |
}
220 | render={(key, data, style) => {
221 | return React.cloneElement(data.child, {
222 | key,
223 | location: data.location,
224 | computedMatch: data.match,
225 | style: {
226 | transform: `translate3d(0, ${style.y}%, 0)`,
227 | opacity: style.o
228 | }
229 | })
230 | }}
231 | getKey={({ child, location, match }) => {
232 | return child.props.getKey // param values used when generating keys
233 | ? child.props.getKey({ location, match })
234 | : child.props.path || child.props.from
235 | }}
236 | onComponentMount={data => ({ y: 50, o: 0.75 })}
237 | onRender={(data, i, spring) => ({
238 | y: spring(0, WOBBLY_SPRING),
239 | o: spring(1)
240 | })}
241 | onRemount={({ data: { child } }) => ({ y: 5, o: 0 })}
242 | onUnmount={({ data: { child } }, spring) => ({
243 | y: spring(20, WOBBLY_SPRING),
244 | o: spring(0)
245 | })}
246 | />
247 | )
248 | }
249 | }
250 | )
251 |
252 | const AnimatedRoute = ({ component: Component, style, getKey, ...rest }) => {
253 | return (
254 |
(
257 |
269 | )}
270 | />
271 | )
272 | }
273 |
274 | export default AnimationExample
275 |
--------------------------------------------------------------------------------
/demo/src/demos/svg-paths.js:
--------------------------------------------------------------------------------
1 | export const EAGLE_PATH = 'm61.742065,683.960693c6.568085,-4.750671 16.420258,-11.40155 29.556458,-10.451416c13.136246,0.950134 42.692734,2.850403 53.639572,0.950134c10.946823,-1.900269 19.7043,0.950134 22.988373,-6.650879c3.284027,-7.601013 4.378708,-32.304382 13.136169,-41.805664c8.757538,-9.501343 32.840591,-19.952759 36.124603,-29.454041c3.284027,-9.501221 4.378769,-13.301758 0,-22.803101c-4.378693,-9.501282 -14.230911,-18.052368 -28.461807,-23.753174c-14.230835,-5.700806 -27.367065,-9.501343 -27.367065,-9.501343c0,0 -28.461823,22.803162 -29.556488,28.503906c-1.094681,5.700745 2.189377,11.401489 -4.378738,14.251953c-6.568115,2.850342 -17.514984,-5.700745 -19.704361,-20.902832c-2.189331,-15.202087 5.473434,-33.254517 4.378769,-43.705933c-1.094711,-10.451416 -8.757462,-19.952698 -2.189392,-30.404114c6.56813,-10.451447 21.893723,-26.603577 33.935272,-34.204651c12.041489,-7.601013 32.840515,-8.551178 36.124542,-18.052399c3.284088,-9.501373 -20.799042,-45.606171 -20.799042,-54.15741c0,-8.551117 3.284103,-24.703308 -4.378693,-38.005096c-7.662796,-13.301819 -16.420258,-41.805664 -49.260788,-59.858124c-32.840515,-18.052429 -55.828949,-25.653473 -61.302338,-38.955246c-5.473419,-13.301834 -1.094681,-13.301834 5.473389,-17.102341c6.56813,-3.800537 -17.514908,-25.653488 -25.177704,-46.55629c-7.662796,-20.902863 -20.799042,-62.708511 -15.325592,-66.509041c5.47345,-3.800507 21.893707,0 25.177765,7.601036c3.284058,7.601028 16.420242,54.157349 16.420242,54.157349c0,0 -3.284042,-35.15477 2.189362,-45.606186c5.47345,-10.451431 12.041519,-20.902832 18.60965,-18.052437c6.5681,2.850388 3.284042,64.608742 3.284042,64.608742c0,0 6.568115,-14.251923 10.946854,-22.803085c4.378738,-8.551147 7.662796,-16.152191 13.136246,-11.40155c5.473404,4.750671 1.094666,50.356842 1.094666,50.356842c0,0 8.757462,-11.40155 15.325577,-14.251938c6.56813,-2.850388 9.852142,-4.750656 14.230881,1.900269c4.378769,6.650879 3.284058,15.202057 3.284058,15.202057c0,0 3.284058,-9.501297 10.946884,-9.501297c7.662796,0 17.514938,2.850388 14.230835,10.451416c-3.284027,7.601044 29.556488,0.950134 27.367142,14.251907c-2.189346,13.301804 0,25.653503 8.757462,25.653503c8.757462,0 14.230911,10.451385 31.745834,6.650864c10.946899,5.700775 -5.473434,40.85553 2.189362,36.104858c7.662796,-4.75061 25.177795,-8.551117 31.745911,1.90036c6.568115,10.451385 -8.757462,22.80307 0,32.304352c8.757477,9.501251 0,18.052429 13.136169,25.653442c13.1362,7.601074 14.230896,1.900299 17.515015,19.002563c3.283997,17.102295 3.283997,24.703369 8.757446,31.354309c5.473358,6.650818 12.041473,11.401489 10.946808,25.653442c-1.094666,14.251953 -10.946808,27.553711 2.189423,25.653473c13.136139,-1.900299 18.609619,-13.301819 28.461731,-18.05246c9.852203,-4.75061 12.041595,-5.700806 25.177734,-14.251892c13.13623,-8.551208 15.325653,-19.952728 22.988373,-22.803101c7.662872,-2.850403 14.230988,-1.900269 25.177765,-5.700806c10.946838,-3.800507 21.893738,-12.351624 27.367188,-16.152161c5.473358,-3.800507 5.473358,-5.700806 15.3255,-13.301819c9.852203,-7.601013 19.704346,-12.351685 26.272461,-24.703308c6.568115,-12.351685 5.47345,-13.301819 13.136169,-31.354248c7.662872,-18.05246 5.47345,-23.753265 9.852203,-31.354309c4.378723,-7.601013 4.378723,-14.251923 12.041504,-20.902802c7.662842,-6.650894 14.230896,-14.251923 15.325562,-7.601059c1.094666,6.65094 1.094666,16.152206 1.094666,21.853012c0,5.700775 9.852234,-8.551178 18.60968,-22.803116c8.757446,-14.251938 4.378723,-16.152191 7.662781,-28.503876c3.284058,-12.351639 0,-20.902817 1.094666,-35.154739c1.094666,-14.251923 3.284119,-15.202072 4.378784,-36.104904c1.094727,-20.902824 3.040405,-24.412949 3.040405,-39.614998c0,-15.202057 -0.636597,-21.351097 -0.851013,-32.594765c3.800476,-8.630112 8.059021,-11.269028 10.946777,-10.451416c3.556946,0.817596 8.33197,0.871185 11.190552,12.351662c2.189331,12.351685 4.164246,21.562569 6.324463,44.656044c-2.189453,22.803085 -3.284119,42.755798 1.094666,48.456589c4.378662,5.70076 7.662781,4.750626 10.946838,-1.900284c3.284119,-6.650879 3.284119,-9.501266 7.662781,-20.902832c4.378784,-11.401535 4.1026,-18.872925 6.353638,-34.785416c-1.094666,-23.75322 0.214478,-34.573967 1.309143,-52.626419c1.094666,-18.052444 -0.120117,-18.579636 2.189392,-29.453979c1.974976,-10.293533 4.742493,-12.746368 14.230896,-11.401535c5.473389,5.70076 6.568115,19.952698 8.757507,32.304352c2.189331,12.3517 2.189331,11.40155 3.284058,34.204636c1.094604,22.803101 0.152588,31.906815 -2.036743,39.507828c-0.516479,8.472237 -2.462158,16.890884 -1.247314,20.350296c6.568054,-3.800537 6.568054,-10.451431 13.136169,-23.753235c6.568054,-13.301788 12.041565,-24.703323 14.230896,-39.905396c2.189392,-15.202057 8.029907,-20.770309 11.768677,-37.503281c8.757507,-16.152191 10.674011,-20.056992 16.693115,-20.454559c6.35376,3.958389 9.364868,7.522095 8.757507,19.952698c-3.284058,13.301788 -3.284058,19.952713 -8.757507,33.254494c-5.47345,13.301804 -4.378723,19.002579 -12.041504,33.254501c-4.651611,12.50956 -5.22644,16.758339 -7.662842,20.90284c10.94696,0.950134 10.794189,-2.825043 17.514954,-9.501297c11.070313,-18.292145 6.873474,-14.438019 10.946899,-19.952705c6.080811,-11.032196 11.281433,-15.094879 16.420227,-13.301773c5.47345,7.601013 7.662781,9.501274 3.284119,18.052429c-4.378723,8.551155 -1.094727,2.850388 -7.662781,15.202049c-6.568115,12.351685 -13.136292,17.10231 -4.378784,16.152191c8.757446,-0.950119 9.852173,0 15.325623,-7.601036c5.47345,-7.601021 0,-8.551155 12.041504,-13.301781c12.041504,-4.750656 19.704285,-13.301804 22.988403,-6.650917c3.284058,6.650917 4.378662,5.700768 -3.284119,15.202072c-7.662781,9.501274 -15.325562,12.351662 -12.041443,17.102303c3.283997,4.750641 12.041443,5.700775 18.609619,5.700775c6.567993,0 7.66272,0.950119 4.378662,10.451431c-3.284058,9.501266 -4.378662,9.501266 -5.473389,14.251907c-1.094727,4.750671 4.378662,8.551163 4.378662,8.551163c0,0 9.852234,-5.70076 2.189331,8.551163c-7.66272,14.251938 -12.041443,10.451416 -16.420227,17.102325c-4.378662,6.650879 -8.757385,13.301788 -2.189331,13.301788c6.568115,0 13.136292,-0.950134 12.041565,3.800507c-1.094727,4.750656 -10.946899,9.501282 -5.47345,12.351669c5.47345,2.850403 9.852112,-0.950119 14.230896,2.850403c4.378784,3.800522 0,10.451431 0,15.202072c0,4.750595 -1.094727,8.551132 1.094604,13.301804c2.189514,4.750641 6.568237,9.501251 3.28418,14.251923c-3.28418,4.750671 1.094666,5.700745 -7.662842,10.451355c-8.757507,4.750671 -14.230896,3.800568 -14.230896,8.551208c0,4.750671 -1.094727,3.800537 4.378784,7.601013c5.47345,3.800537 12.041443,0.950134 7.662781,9.501343c-4.378784,8.551147 -3.284058,7.601013 -5.473389,15.202026c-2.189392,7.601074 1.094604,3.800476 -8.757568,21.852966c-9.852112,18.05246 -19.704285,31.354279 -29.556396,36.104858c-9.852234,4.750671 -7.662781,15.202118 -1.094727,17.102356c6.568115,1.900208 7.66272,10.451416 1.094727,22.803101c-6.568115,12.351624 -14.230896,28.503845 -16.420349,37.054993c-2.189331,8.551178 -4.378723,9.501282 -15.325623,19.952698c-10.946716,10.451416 -19.704224,19.952698 -29.556396,22.803101c-9.852173,2.850342 -10.946899,12.351624 -17.514954,17.102234c-6.568054,4.750732 -8.757507,7.601074 -22.988403,7.601074c-14.230896,0 -14.230896,-2.850342 -28.461731,0.950134c-14.230896,3.800537 -20.799011,7.601074 -21.893677,13.30188c-1.094666,5.700623 -5.47345,8.551025 -14.230896,6.650818c-8.757507,-1.900208 -2.189392,3.800537 -13.136292,7.601074c-10.946777,3.800537 -7.662781,8.551147 -20.799011,7.600952c-13.136169,-0.950012 -19.704285,-9.501221 -22.988342,0c-3.284058,9.501343 -4.378723,17.102417 -18.60965,8.551208c-14.230896,-8.551208 -21.893646,-31.354187 -29.556458,-7.601013c-7.662781,23.753174 -7.662781,29.454041 1.094666,36.104858c8.757477,6.65094 10.946869,12.351685 30.651184,19.002625c19.704315,6.650879 19.704315,14.251892 33.935272,19.002563c14.230896,4.750549 15.325562,6.650879 37.219238,16.152161c21.893677,9.501343 27.367065,12.351685 40.503296,19.002563c13.136169,6.65094 16.420288,13.301819 19.704285,19.952698c3.284119,6.650879 17.515015,14.251953 12.041565,23.753235c-5.47345,9.501282 -16.420227,3.800476 -16.420227,8.551147c0,4.750671 2.189331,8.551086 -2.189453,18.05249c-4.378662,9.501282 -3.283997,13.301819 -18.609619,17.102234c-15.325562,3.800598 -24.083008,5.700806 -31.745789,2.850464c-7.662842,-2.850464 -9.852173,8.551086 -17.514954,5.700745c-7.662781,-2.850403 -10.946899,-11.40155 -16.420349,-6.650879c-5.473328,4.75061 4.378784,7.601013 -8.757385,10.451355c-13.13623,2.850464 -14.230957,-4.750549 -15.325623,1.900208c-1.094666,6.651001 3.284058,13.30188 -10.946838,6.651001c-14.230927,-6.651001 -9.852142,-19.952759 -20.799011,-10.451416c-10.946838,9.501221 -5.47345,20.902832 -25.177765,11.40155c-19.704315,-9.501343 -13.1362,-10.451477 -28.461792,-9.501343c-15.325562,0.950134 -15.325562,1.900208 -20.79895,-8.551086c-5.473419,-10.451477 5.473389,-7.601013 -18.60968,-8.551208c-24.083038,-0.950134 -12.041504,12.351746 -27.367065,-5.700684c-15.325592,-18.052551 -18.60968,-36.10498 -25.177795,-16.152344c-6.568115,19.952759 0,24.70343 -9.852142,30.404236c-9.852142,5.700745 -60.207642,3.800537 -60.207642,3.800537c0,0 -1.094696,21.852905 -5.47345,25.653442c-4.378693,3.800537 -15.325592,-3.800537 -25.177719,3.800537c-9.852142,7.601013 -3.284027,12.351624 -12.041565,19.002563c-8.757385,6.650879 -26.2724,7.600952 -26.2724,7.600952c0,0 12.041565,-8.551086 15.325592,-16.152161c3.284088,-7.601013 7.662781,-20.902771 7.662781,-20.902771c0,0 -17.514908,5.700745 -25.177704,6.650879c-7.662796,0.950073 -19.704361,-0.950134 -19.704361,-0.950134l3.284088,21.852905c0,0 -20.799026,0 -20.799026,-7.601013c0,-7.601013 6.56813,-28.503845 12.041565,-31.354187c5.473373,-2.850464 13.136169,-1.90033 13.136169,-1.90033c0,0 -2.189362,-9.501221 3.284088,-11.401489c5.473373,-1.90033 8.757477,0 17.514938,0c8.757462,0 48.166092,-10.451416 48.166092,-10.451416c0,0 -1.094681,-24.70343 3.284088,-35.154785c4.378708,-10.451416 25.177719,-39.905396 15.325577,-44.656067c-9.852203,-4.750671 -17.514999,-0.950073 -22.988358,3.800537c-5.47345,4.75061 -15.325592,19.002502 -36.124619,22.803101c-20.799026,3.800476 -37.219284,5.700806 -37.219284,5.700806c0,0 -4.378693,1.900208 -4.378693,9.501221c0,7.601013 7.662796,15.202087 -3.284103,24.703308c-10.946808,9.501404 -19.704269,18.052551 -31.745819,13.30188c-12.041534,-4.750671 -14.230942,-10.451416 -14.230942,-10.451416c0,0 8.757492,4.75061 22.988434,1.900269c14.230865,-2.850403 8.757431,-26.603638 7.66275,-31.354248c-1.094681,-4.750671 -12.041519,-9.501343 -18.609634,-4.750671c-6.568085,4.750671 -15.325592,17.102295 -9.852142,21.852966c5.473404,4.750549 -15.325577,-10.451416 -14.230896,-17.102295c1.094666,-6.65094 4.378708,-11.40155 4.378708,-11.40155c0,0 -14.230865,0 -22.988358,-0.950195c-8.757477,-0.950073 -15.325577,-4.750671 -15.325577,0.950195c0,5.700745 4.378769,31.354187 -5.473404,15.202087c-9.852188,-16.152283 -14.230927,-19.952698 -4.378754,-28.503906z'
2 | export const USA_PATH = 'M323.14,389.83c0,3.24-.62,6.05-1.45,7.23,1.48,3.11,4.9,7.48,2.9,12v0.48c1.61,0.11,1.83-.58,2.9,1.45l-5.8,1c-3-5.51-20.27-3.82-22.22-9.64-1.17-.73-5.47-12.61-5.8-14.94V385.5c-4.31-3.18-18.48-20.58-16.91-22.65l-5.8-4.34-2.9-5.3c-12.29-7-18.79,6.33-24.15,10.12-9.31-2.68-22.4-14.92-22.7-25.06,0.31-.4-3.17-2.57-1.45-3.85-5.62-5.38-15.59-11.07-16.42-17.35l-23.67-1.93-1,7.23L139.11,318c-4.07-3.82-23.72-16.39-28-16.86l-2.41-1.45-1.93-1.45-1.45-.48-1.93-1.93c-1.64-2-11.77-3.59-10.14-7.23l-30.91-4.82c-0.16-8.85-2-16.47-7.24-21.2l-4.35-1c-2.06-2.33-1.3-4.16-2.42-4.82-0.9-1.16-3.07-.73-4.35-1.45-3.52-2-4.93-5.89-7.25-9.16-5.4,1-8.1-3-13-5.3,0-3.92,2.89-9.93,1-12-2,0-10.63-21.2-10.63-21.2-0.3-3.28,3.29-3.61,1.93-7.23,0,0-5.09-4.86-5.8-6.27q0.24-4.34.48-8.67h1.45l-0.48,1c1.44-1,.58-0.34,1-1.45a6.72,6.72,0,0,0-1.45-4.34c-0.39,1.51-.06,1.12-2.42,1.93-0.37-2.37-2.72-2.15-3.38-5.78a3.19,3.19,0,0,0,1-3.86c-1.78-3.63-1.91-7.46-4.83-12-0.93-1.47.9-1.1,1-1.44,1.6-8.72.91-12.76-2.42-19.28v-5.3c1.62-2.19,6.28-5.78,6.28-5.78L5.8,124.8c3.1-6.65-.33-13,1.93-19.27,3.53-9.76,7-21.06,14-28.43l0.48-5.3a36.59,36.59,0,0,1,5.31-7.71l2.41-12h4.35l-1-1c-0.31-1.48-1.08-1-2.41-3.37V46.26c2.21-.29,1.44.34,2.9-1.45l-1.93-3.37,1.93-1.93c-1.68-6.38-.79-15-2.41-20.24l2.41-1.93c7.55,5.14,10,5.42,18.35,10.6-2.09.94-2.79,1.89-3.38,4.34,1.71,0.78,1.68-.42,2.41,2.41l-1.93,2.89c-2.42-.52-5.91-0.88-3.38,2.41v1a35.61,35.61,0,0,0,7.73-2.89c-0.78-3-.38-5.72,1.45-7.23A4.31,4.31,0,0,0,57,30.36c-0.6-3.78.36-15.17-3.38-16.87L54.1,12l32.84,10.6c9.26,4.85,20.69,3.56,29.95,7.71l10.63,1.45c17.05,5.29,36.12,5.73,56.51,10.12,13.25,2.85,31.36,6.08,46.85,5.3,24.52-1.23,54.23,5.07,79.7,1,8.5-1.37,16.22,1.58,23.67-1.45l2.9-5.3c1.29,1.18.74,0.66,1,1.93C340,44,340.34,48.2,342,50.6c9.92-1.72,10.85,2.62,21.74-.48l-0.48,1a5.37,5.37,0,0,1,1.45,4.34h1.45c0.22-1.29-.3-0.73,1-1.93,2.58,0.67,7.1,2.17,7.73,4.34,5.49-1.82,9.75-2.88,18.84-1v0.48l-1,1-1.93,1.93c-5.89.14-12.49,10.24-17.39,16.87h-1.45l0.48,1.45c5.39,0,7.44-.7,10.63-2.89l1.45,0.48V80c16.11,0,20-14,31.39-17.83-1.55,3-6.58,5-4.83,10.6,7.16-4.94,12.75,7.55,19.32,3.85,2.08-1.17,4.61-4.37,6.28-5.3,2.49-1.4,6.52-.94,7.73-3.37l1,0.48c0,2.62-.37,4.56,1,3.85l1,1.45c2.73-.19,5.3-0.64,6.28-1.93,1,0.85,4.68,4.08,4.35,5.3l-1,1c-2.6-1.38-3.5-1.27-5.8,1.93-3.24-1.77-4.64-1.45-8.21-2.41-0.84,3.76-8.35,6.63-10.14,10.6l-1-.48,1-2.89-1,.48c-2.1.3-.75,0.13-2.41,1.45l-1.93-1.45c-0.6,8.25-5.6,10.14-7.25,21.68h1.45c1.07-2.33,4.36-9.27,7.24-10.12l0.49-1c-0.85,11.56-9.6,28-1.93,38.55v4.34c0,6.24,1.8,7.84,5.8,11.08,13.57-4,11.23-24.6,4.35-35.66q1-8.43,1.93-16.87c3.53-.54,2.17-2.21,4.83-4.34h0.48l0.48,3.85h2.41V90.59c1.52-1.32,2.09-.62,3.38-2.89-1.34-.22-1.22,0-2.41-1.45,1.2-1.94,2.26-3.18,4.83-3.86,0.19,1.16,13.48,4.15,15.46,5.3-2.07,4.54.85,4,1.93,7.23,1.82,5.53-3.59,12.81-4.83,14.94,0.9,0.67.77,0,.48,1.93l1.93,0.48c1.88-3.08,5.3-8,9.66-8.19,0.44,4.47,6.46,10,7.73,19.27l-4.35,1.44c-0.75,6.87-3.3,12-3.86,16.38a41.79,41.79,0,0,1,9.18,3.37c0.42-1.07,7.93-5.38,10.14-5.78,5.85-1.05,26.68-26.36,29.46-27-1.65-4.61-2.82-4.71-4.83-8.19,5.82-2.14,12-3,17.87-5.3l1,0.48c3.89-2.87,7-5.95,12.07-7.23-1.82-4.23-.33-4.31-1.93-9.16-2.17.58-4.1,0.08-3.38-1l-1,.48c0.53-3.14,3.1-4.87,4.83-6.26,1.15-.92-0.35-3,0.48-4.82,2.36-5.23,8.89-12.61,17.39-13,0,0,10.38-4.13,10.63-3.85,2.09-1.73,11.12-6.4,13.52-5.3-0.49-2.08-.07-2.71.48-4.34,2.65-1.1,3.08-1.69,4.83-1.45,0-6.32.63-34.62,4.35-38.55l3.86,1.93c3.53-3,4.81-4.48,6.76-6.26,1.59,1,3.41.2,4.83,1.93,3.68,2.55,8,15.37,7.73,17.83,5.56-1,9.24,8.09,14,9.64-0.4,6.43-8.71,8.79-7.73,12.53h-2.9l-1,3.86H626c-0.23-1.59.23-1.37-1.93-2.41l-1,1.93c6.14,3.79-18.41,24.56-11.11,31.8L614.4,79a3.56,3.56,0,0,0-1,5.3c1.76,3,4.62,4.91,6.28,7.23,3.35-.93,3.58-0.3,5.8-1.93L625,87.22l-1.93-2.41v-1h0.48l5.31,2.89a11.31,11.31,0,0,0-1,2.89c-2.49,1.09-9.71,6-10.63,8.67-2.47-.64-1.64-1-3.38-1.93-1,2.55-1.16,5.15-2.9,6.75-0.65.08-29.28,14.62-23.19,25.54l3.87,0.48c-0.52,6-2.06,21.19-6.76,25.06v-4.34a14.69,14.69,0,0,1-7.25-1.93c0.36,2.47.21,7,2.9,6.26,0.9,2,2.34,2.07,4.35,2.89,0.54,8,.22,20.36-4.35,23.13-0.05-4.78.76-4.46,1.45-9.15-3.3,0-2.75-.24-3.38-1.45-1.75-1.49-1.95-6.35-5.8-5.3,1-4.59-1.79-3.71-2.89-7.23,1.23-.61.62-4.61,1.45-7.23h-1.93a33.19,33.19,0,0,1-3.38,7.23c1.28,1.72,4,10.53,5.31,10.12l1.93,1.93c-1.19.94-.19,0.27-1.45,0.48l-1.45,1.45c1,1.06.67,0.91,2.41,1.44l1.93,1.93v2.89h-2.42c-0.29-.43-1.19-1.65-2.41.48l1-.48c0.8,1.63,3.54,2.8,4.83,4.34l-2.41,1.45v1.45c2.13,0.29,2.33.66,3.38,2.89-2.24-.66-2.9-1.52-4.35,1,1.26,0.2.26-.47,1.45,0.48l7.73-.48,2.9,4.34c-1.78,1.2-.08,4.21,1,6.26-5.61.46-3.74,2.85-9.66,1.93q0.24,2.41.48,4.82h1.45c4-2.9,5.69-1.08,8.69-1l1-3.37c1.8,0.88,2,.88,1.93,2.41h1c-0.57,3.74-1.38,7.77-4.35,8.19-0.85,1.74-3.36.15-4.83-.48-1.22,3.55-2.14,2-2.9,4.82l3.38-.48-1,5.3a4.82,4.82,0,0,0-3.38,1l0.48,1,1-.48c3.28,0.66,3.36-2.2,5.8-2.89l1,2.41c-1.18.82,0.17,1.79-1.45,1.44,0,0-10.33,7.05-12.56,9.64-0.21,1.93-.17,7.51-2.9,9.16-2.07,1.24-5.75,1.19-7.73,3.37-0.78,5-16,31.07-23.18,30.36,0.92,5.68-2.7,15.12-4.35,21.69l1.93,9.64c-0.34.4-5.36,3.4-2.42,5.78,0.21,1-.16.81,1,1.93-0.08-3.72.25-4.71,1.93-5.78h0.48c0,10.91,7.56,17.8,15,26.5l-1.45.48c-1.13-1,.06-0.21-1.45-0.48,4,6.81,24.64,34.47,11.11,53.49-5.87.07-2.49,1.72-7.25,1l1-1.45c1.26-.2.26,0.47,1.45-0.48v-1c-11.67-2.26-15.53-19.24-19.8-21.2-2.72-1.25-3.91,1-5.31-.48-2.49-2.63-2.22-5.79-4.35-6.27,1.23-2.17,2.89-4.16,0-6.75-1.65,1.13-1.1.57-1.45,1.93-1.47-.94-0.5-0.16-1-1.45-3-2.41.32-12.47-1.93-15.9-3.41-5.2-8.93-3.29-12.07-7.23-2.38-3-1.56-3.52-4.83-6.75l-8.21.48c0.23,1.25,1.18,4-1,2.89-0.26,2-7.29,4.64-8.7,3.86-1.26-.25-6.75-6-7.25-5.78a97.2,97.2,0,0,0-20.29-.48c-0.19,3.44-.2,2-1.93,3.37l-5.31.48c-1.75-1.48-1.1-2.23-3.86-3.37l0.48,1c-2,1.1-.89,1.84-2.41,2.89l-10.62.48c-1.69,1-3.74,4.2-5.8,4.34-3,.85-7.61-1-8.21-2.41l-6.28,2.41,1,1.45c1.19,0.72.44,0.57,1.45,1.93h5.79s0.29-3,1-1.45h1.93v2.41l6.76-1,1.45,1.45a3.18,3.18,0,0,0-.48,1.93c-3.19.25-2.91,0.64-4.35,2.41,0.62,1.54-.15,1.1,1,1.93,1.29,1.69,4.66,2,6.76,4.34l-0.48,1-2.42,2.89h-0.48c-0.75-.89-11.59-8.68-11.59-8.68-0.91-1.18-.27-0.46-2.41-1,0,0.33.23,1.78-.48,1a3.17,3.17,0,0,1,.48,1.93c2.24,0.2,2.19-.4,3.38,2.41l0.48,2.89-9.18,1.93a2.07,2.07,0,0,0-1.45,1.93,10,10,0,0,1-5.8-2.89c0.25-2.36.36-7.83-1-9.16-0.54-1.35-2.12-1.29-3.38-1.93,1,2.75,3.64,5.15,2.42,7.23-1.11-1.65-5.66-1.9-6.76-3.85-1.85.82-4,2-3.86,4.82-7.19.09-12.7,0-15-2.41-2,1.51-18.2,6.79-19.81,7.23,1-1.74.82-.94,1-2.89l-1.45-2.41h-1.45c-1.62,14.73-11.18,16.56-21.25,17.83v0.48c0.77,1.74,1.62,1.12.48,2.41-1.67.31-1.51,0.28-3.38,1.93-1-1.8.21-.79-1.93-1.45,0,4.36-1.92,2.89-2.9,6.26-2.46-.2-1.72,0-2.42.48-0.63.74-.63,2.14-0.48,3.85-0.95.06-2.52,0-3.38,1.45h1c0.75,0.89-.07.6,1.45,0.48-0.93,2.72-1.27,2.85-1,5.78l-2.9-1-0.48,1.45,0.48,1h2.41Z'
3 |
--------------------------------------------------------------------------------