├── 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 |
6 |
7 | {children} 8 |
9 |
10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /demo/src/docs/Api.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import apiMarkdown from 'html-loader!markdown-loader!../../../docs/api.md' 3 | 4 | export default (props) => { 5 | return ( 6 |
7 |
10 |
11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "7" 5 | 6 | cache: 7 | directories: 8 | - node_modules 9 | 10 | before_install: 11 | - npm install codecov 12 | 13 | after_success: 14 | - cat ./coverage/lcov.info | ./node_modules/codecov/bin/codecov 15 | 16 | cache: 17 | directories: 18 | - node_modules 19 | 20 | branches: 21 | only: 22 | - master 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | [Node.js](http://nodejs.org/) must be installed. 4 | 5 | ## Installation 6 | 7 | * Running `npm install` in the module's root directory will install everything you need for development. 8 | 9 | ## Running Tests 10 | 11 | * `npm test` will run the tests once. 12 | 13 | ## Building 14 | 15 | * `npm run build` will build the module for publishing to npm. 16 | * `npm run clean` will delete built resources. 17 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "modules": false, 7 | "loose": true 8 | } 9 | ], 10 | "stage-2", 11 | "react" 12 | ], 13 | "env": { 14 | "test": { 15 | "presets": [ 16 | [ 17 | "env", 18 | { 19 | "loose": true 20 | } 21 | ], 22 | "stage-2", 23 | "react" 24 | ] 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /demo/src/Loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Loading ({isLoading, error, pastDelay}) { 4 | if (isLoading) { 5 | return pastDelay ?
Loading...
: null // Don't flash "Loading..." when we don't need to. 6 | } else if (error) { 7 | return ( 8 |
 9 |         

Error

10 | {JSON.stringify(error, null, 2)} 11 |
12 | ) 13 | } else { 14 | return null 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /demo/src/index.js: -------------------------------------------------------------------------------- 1 | import 'github-markdown-css' 2 | import './style.css' 3 | // eslint-disable-next-line no-unused-vars 4 | import React from 'react' 5 | import { render } from 'react-dom' 6 | 7 | function run () { 8 | const Root = require('./App').default 9 | render(, document.querySelector('#demo')) 10 | } 11 | 12 | run() 13 | 14 | if (module.hot) { 15 | // Whenever a new version of App.js is available 16 | module.hot.accept('./App', function () { 17 | // Require the new version and render it instead 18 | run() 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/index.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ddm renders elements with styles 1`] = ` 4 | 32 | `; 33 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import babel from 'rollup-plugin-babel' 3 | import resolve from 'rollup-plugin-node-resolve' 4 | import commonjs from 'rollup-plugin-commonjs' 5 | 6 | const pkg = JSON.parse(fs.readFileSync('./package.json')) 7 | 8 | export default { 9 | entry: 'src/index.js', 10 | external: ['react', 'prop-types', 'react-motion'], 11 | exports: 'named', 12 | globals: { react: 'React', 'prop-types': 'PropTypes', 'react-motion': 'ReactMotion' }, 13 | useStrict: false, 14 | sourceMap: true, 15 | plugins: [ 16 | babel({ 17 | exclude: 'node_modules/**' 18 | }), 19 | resolve({ 20 | jsnext: false, 21 | main: true, 22 | browser: true 23 | }), 24 | commonjs({ 25 | ignoreGlobal: true, 26 | include: 'node_modules/**' 27 | }) 28 | ], 29 | targets: [ 30 | {dest: pkg.main, format: 'cjs'}, 31 | {dest: pkg.module, format: 'es'}, 32 | {dest: pkg['umd:main'], format: 'umd', moduleName: pkg.name} 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kye Hohenberger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # data-driven-motion 2 | 3 | [![npm version](https://badge.fury.io/js/data-driven-motion.svg)](https://badge.fury.io/js/data-driven-motion) 4 | [![Build Status](https://travis-ci.org/tkh44/data-driven-motion.svg?branch=master)](https://travis-ci.org/tkh44/data-driven-motion) 5 | [![codecov](https://codecov.io/gh/tkh44/data-driven-motion/branch/master/graph/badge.svg)](https://codecov.io/gh/tkh44/data-driven-motion) 6 | [![Standard - JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://img.shields.io/badge/code_style-standard-brightgreen.svg) 7 | 8 | 9 | 10 | Easily animate your data in react 11 | 12 | This is a small wrapper around [react-motion](https://github.com/chenglou/react-motion) with the intention of simplifying the api for my most common use case. 13 | 14 | ## [Demos and Docs](https://tkh44.github.io/data-driven-motion/) 15 | 16 | ```bash 17 | npm install -S data-driven-motion 18 | ``` 19 | 20 | ## Motion 21 | 22 | ```jsx 23 | } 26 | render={(key, data, style) =>
  • {data.name}
  • } 27 | getKey={data => data.name} 28 | onComponentMount={data => ({ top: data.top, left: data.left })} 29 | onRender={(data, i, spring) => ({ top: spring(data.top), left: spring(data.left) })} 30 | onRemount={({ data }) => ({ top: data.top - 32, left: data.left - 32 })} 31 | onUnmount={({ data }, spring) => ({ top: spring(data.top + 32), left: spring(data.left + 32) })} 32 | /> 33 | ``` 34 | -------------------------------------------------------------------------------- /demo/src/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | width: 100%; 4 | height: 100%; 5 | margin: 0; 6 | padding: 0; 7 | font-family: sans-serif; 8 | background-color: #f8f9fa; 9 | color: #343a40; 10 | overflow: hidden; 11 | display: flex; 12 | flex-direction: column; 13 | } 14 | 15 | #demo, 16 | .full { 17 | flex: 1; 18 | width: 100%; 19 | display: flex; 20 | flex-direction: column; 21 | } 22 | 23 | .scrollable { 24 | overflow: auto; 25 | -webkit-overflow-scrolling: touch; 26 | } 27 | 28 | a, 29 | .markdown-body a { 30 | color: #37b24d; 31 | text-decoration: none; 32 | padding: 0 8px; 33 | } 34 | 35 | a:hover, 36 | .markdown-body a:hover { 37 | color: #2b8a3e; 38 | text-decoration: none; 39 | } 40 | 41 | .header-headline { 42 | font-size: calc((1.5em + 1vh + 1vw) / 3); 43 | } 44 | 45 | @media (min-width: 600px) { 46 | .header-headline { 47 | font-size: calc((3em + 1vh + 1vw) / 3); 48 | } 49 | } 50 | 51 | .api-docs { 52 | overflow: auto; 53 | } 54 | 55 | .demo-page-inner, 56 | .home-page-inner { 57 | max-width: 768px; 58 | margin: 0 auto; 59 | } 60 | 61 | .home-page-inner { 62 | display: flex; 63 | align-items: center; 64 | justify-content: center; 65 | flex-direction: column; 66 | flex: 1; 67 | } 68 | 69 | .home-page-inner li a { 70 | font-size: 125%; 71 | } 72 | 73 | .demo-page-inner h3 > a { 74 | font-size: 80%; 75 | font-weight: normal; 76 | } 77 | 78 | .demo-container { 79 | position: relative; 80 | border-radius: 4px; 81 | border: 1px solid #ced4da; 82 | 83 | } 84 | 85 | .demo-container:before { 86 | display: block; 87 | content: ""; 88 | width: 100%; 89 | padding-top: 56.25%; 90 | } 91 | 92 | .demo-container.tall:before { 93 | display: block; 94 | content: ""; 95 | width: 100%; 96 | padding-top: 100%; 97 | } 98 | 99 | .demo-container > .content { 100 | position: absolute; 101 | top: 0; 102 | left: 0; 103 | right: 0; 104 | bottom: 0; 105 | } 106 | -------------------------------------------------------------------------------- /src/__tests__/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | /* eslint-disable jsx-quotes */ 3 | import React from 'react' 4 | import renderer from 'react-test-renderer' 5 | const TestUtils = require('react-dom/test-utils') 6 | import {Motion} from '../index' 7 | 8 | describe('ddm', () => { 9 | test('renders elements with styles', () => { 10 | const getKey = (data, i) => data.name + ' ' + i 11 | 12 | const tree = renderer 13 | .create( 14 | } 20 | render={[(key, data, style, i, j) => ( 21 |
  • {data.name + ' ' + j}
  • 22 | )]} 23 | getKey={getKey} 24 | onRender={(data, i, spring) => ({ 25 | top: spring(data.top), 26 | left: spring(data.left) 27 | })} 28 | onRemount={({data}) => ({ 29 | top: data.top - 32, 30 | left: data.left - 32 31 | })} 32 | onUnmount={({data}, spring) => ({ 33 | top: spring(data.top + 32), 34 | left: spring(data.left + 32) 35 | })} 36 | /> 37 | ) 38 | .toJSON() 39 | 40 | expect(tree).toMatchSnapshot() 41 | }) 42 | test('renders elements with styles', done => { 43 | let renderCount = 0 44 | 45 | TestUtils.renderIntoDocument( 46 | } 49 | render={(key, data, style) => { 50 | ++renderCount 51 | if (renderCount === 1) { 52 | expect(data.name).toBe('Arrow') 53 | } 54 | 55 | if (renderCount === 2) { 56 | done() 57 | } 58 | return
  • {data.name}
  • 59 | }} 60 | getKey={(data, i) => data.name + ' ' + i} 61 | onComponentMount={data => ({x: 5})} 62 | onRender={(data, i, spring) => ({ 63 | x: 5 64 | })} 65 | onRemount={({data}) => ({ 66 | x: 32 67 | })} 68 | onUnmount={({data}, spring) => ({ 69 | x: 55 70 | })} 71 | /> 72 | ) 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # Api 2 | 3 | ### Props 4 | 5 | * #### component 6 | `PropTypes.element` 7 | 8 | wrapper element 9 | ex: `
    ` 10 | 11 | 12 | * #### data 13 | 14 | `PropTypes.array` 15 | 16 | array of data 17 | 18 | **It does not matter what `data` contains, as long as it is an array** 19 | 20 | * #### render 21 | 22 | `PropTypes.oneOfType([PropTypes.func, PropTypes.arrayOf(PropTypes.func)])` 23 | 24 | ```js 25 | (key, data, style) => ReactElement 26 | ``` 27 | 28 | called on render for each item in `data` with the `key` from `getKey`, `data` from `data[index]`, and 29 | `style`, the result of: `onComponentMount`, `onRender`, `onRemount`, or `onUnmount` 30 | 31 | If an array is provided it will render the resulting elements in order. 32 | This is useful for creating animated layers. 33 | 34 | 35 | * #### getKey 36 | 37 | `PropTypes.func` 38 | 39 | ```js 40 | (data, i) => string 41 | ``` 42 | 43 | help identify which items have been changed, added, or are removed 44 | 45 | [React docs on lists and keys](https://facebook.github.io/react/docs/lists-and-keys.html) 46 | 47 | 48 | * #### onComponentMount 49 | 50 | `PropTypes.func` 51 | 52 | ```js 53 | (data, i) => Style object 54 | ``` 55 | 56 | `data === props.data[i]` 57 | 58 | called when `component` mounts 59 | 60 | __do not wrap values in springs__ 61 | 62 | 63 | * #### onRender 64 | 65 | `PropTypes.func` 66 | 67 | ```js 68 | (data, i, spring) => Style object 69 | ``` 70 | 71 | `data === props.data[i]` 72 | 73 | called when `props.component` mounts 74 | 75 | __ok to wrap values in springs__ 76 | 77 | 78 | * #### onRemount 79 | 80 | `PropTypes.func` 81 | 82 | ```js 83 | ({ key, data, style }) => Style object 84 | ``` 85 | 86 | *Notice the argument is wrapped in an object* 87 | 88 | The argument is the computed config from `onRender` 89 | 90 | __do not wrap values in springs__ 91 | 92 | 93 | * #### onUnmount 94 | 95 | `PropTypes.func` 96 | 97 | ```js 98 | ({ key, data, style }) => Style object 99 | ``` 100 | 101 | *Notice the argument is wrapped in an object* 102 | 103 | The argument is the computed config from `onRender`. 104 | 105 | __ok to wrap values in springs__ 106 | -------------------------------------------------------------------------------- /demo/src/demos/Trippy.js: -------------------------------------------------------------------------------- 1 | import React, { createElement as h, Component } from 'react' 2 | import { Motion } from '../../../src' 3 | import Demo from '../Demo' 4 | 5 | const WOBBLY_SPRING = { stiffness: 280, damping: 30 } 6 | 7 | class BoxContainer extends Component { 8 | state = { x: 0, y: 0, mouseDown: false, offsetTop: 0, offsetLeft: 0 }; 9 | 10 | render () { 11 | const { x: xPos, y: yPos, offsetLeft, offsetTop } = this.state 12 | 13 | const x = xPos - offsetLeft 14 | const y = yPos - offsetTop 15 | 16 | return h(Motion, { 17 | data: Array.from({ length: 33 }, (v, i) => ({ key: 'circle-' + i })), 18 | component: ( 19 |
    { 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 | 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 |

    102 | Slider 103 | 107 | Source 108 | 109 |

    110 | 111 |
    112 |

    113 | Color Search 114 | 118 | Source 119 | 120 |

    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 | --------------------------------------------------------------------------------