├── .babelrc ├── .eslintrc.json ├── .gitignore ├── .prettierrc ├── README.md ├── design └── slides.sketch ├── package.json ├── public ├── application.js ├── favicon.png ├── images │ ├── about-author.jpg │ ├── actuation.png │ ├── architecture.jpg │ ├── dan-article.jpg │ ├── dialog-close.svg │ ├── dirty-circle.png │ ├── gifs │ │ ├── abstract.gif │ │ ├── bean.gif │ │ ├── factory.gif │ │ ├── robot-dance.gif │ │ ├── storm.gif │ │ └── wave.gif │ ├── guys │ │ ├── alex.svg │ │ ├── avram.svg │ │ ├── gustav.svg │ │ ├── jose.svg │ │ ├── pierrot.svg │ │ └── tikhon.svg │ ├── immutable-chain.png │ ├── immutable-logo.png │ ├── immutable-map.png │ ├── moon-emoji.png │ ├── producthunt-badge.jpg │ ├── rams-radio.jpg │ ├── resume-screen-1.jpg │ ├── resumeio-logo.png │ ├── sequence.jpg │ ├── state-map.png │ ├── stateful-circle.png │ ├── stateless-circle.png │ ├── stateless-square.png │ ├── sun-emoji.png │ ├── sunglasses-emoji.png │ ├── talks │ │ ├── acko.gif │ │ ├── aerotwist.gif │ │ ├── anim-vdom.gif │ │ ├── eyeo.gif │ │ ├── fsm.gif │ │ └── immut-uis.gif │ ├── timeline-ideal.jpg │ ├── timeline-raf.jpg │ └── timeline-set-timeout.jpg ├── index.html └── sounds │ ├── game-start.ogg │ ├── slap.ogg │ └── whoop.mp3 ├── src ├── application.js ├── blocks │ ├── button.js │ ├── dynamic-code.js │ ├── figure-caption.js │ ├── frame-background.js │ └── layout.js ├── colors.js ├── ficus │ ├── bubble-poll │ │ ├── index.js │ │ └── point-force.js │ ├── classic-poll │ │ ├── classic-poll.js │ │ ├── classic-poll.scss │ │ └── index.js │ ├── cloud-poll │ │ ├── cloud-poll.scss │ │ └── index.js │ ├── ficus-poll-adapter.js │ └── simple-poll │ │ ├── index.js │ │ ├── simple-poll.js │ │ └── simple-poll.scss ├── presentation.js └── slides │ ├── actuation │ ├── action-logger.js │ ├── talking-heads.js │ └── this-guy.js │ ├── boids │ ├── index.js │ ├── neighbour-detector.js │ └── simulator.js │ ├── enter-exit │ ├── 01-dialog-enter.js │ ├── 02-dialog-exit.js │ ├── animated.js │ ├── fake-dialog.js │ ├── index.js │ └── state-monitor.js │ ├── etc │ ├── animation-expectations.js │ ├── golden-rule.js │ ├── links.js │ ├── resources.js │ └── summary.js │ ├── flip │ ├── animated-route.js │ └── index.js │ ├── golden-rule │ └── index.js │ ├── motion-ghost-slide.js │ ├── own-render │ ├── index.js │ └── rotator.js │ ├── poll-slides.js │ ├── poll-slides.scss │ ├── raf-vs-timeout │ ├── comparison-slide.js │ ├── index.js │ ├── raf-slides.js │ ├── rolling-meter.js │ ├── timeline-with-meter.js │ └── timeline.js │ ├── transistor.js │ ├── transistor.scss │ └── transitions │ └── index.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "jest/globals": true 6 | }, 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaVersion": 2016, 10 | "sourceType": "module", 11 | "ecmaFeatures": { 12 | "experimentalObjectRestSpread": true, 13 | "jsx": true 14 | } 15 | }, 16 | "plugins": ["react", "prettier", "jest"], 17 | "extends": [ 18 | "standard", 19 | "plugin:react/recommended", 20 | "prettier", 21 | "prettier/flowtype", 22 | "prettier/react" 23 | ], 24 | "rules": { 25 | "react/prop-types": "off", 26 | "prettier/prettier": [ 27 | "error", 28 | { 29 | "singleQuote": true, 30 | "semi": false 31 | } 32 | ], 33 | "max-len": [ 34 | "warn", 35 | { 36 | "code": 80, 37 | "ignoreUrls": true 38 | } 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node,osx 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 26 | .grunt 27 | 28 | # node-waf configuration 29 | .lock-wscript 30 | 31 | # Compiled binary addons (http://nodejs.org/api/addons.html) 32 | build/Release 33 | 34 | # Dependency directories 35 | node_modules 36 | jspm_packages 37 | 38 | # Optional npm cache directory 39 | .npm 40 | 41 | # Optional eslint cache 42 | .eslintcache 43 | 44 | # Optional REPL history 45 | .node_repl_history 46 | 47 | # Output of 'npm pack' 48 | *.tgz 49 | 50 | # Yarn Integrity file 51 | .yarn-integrity 52 | 53 | ### OSX ### 54 | *.DS_Store 55 | .AppleDouble 56 | .LSOverride 57 | 58 | # Icon must end with two \r 59 | Icon 60 | # Thumbnails 61 | ._* 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | # Directories potentially created on remote AFP share 71 | .AppleDB 72 | .AppleDesktop 73 | Network Trash Folder 74 | Temporary Items 75 | .apdisk 76 | 77 | 78 | .vscode -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Animations in a Stateful World 2 | 3 | Slides and demos. 4 | -------------------------------------------------------------------------------- /design/slides.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/design/slides.sketch -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stateful-animations", 3 | "version": "0.0.1", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "webpack-dev-server", 7 | "build": "NODE_ENV=production webpack --progress", 8 | "deploy": "gh-pages-deploy" 9 | }, 10 | "gh-pages-deploy": { 11 | "staticpath": "public", 12 | "prep": ["build"], 13 | "noprompt": false 14 | }, 15 | "dependencies": { 16 | "classnames": "^2.2.5", 17 | "d3-array": "^1.0.1", 18 | "d3-ease": "^1.0.2", 19 | "d3-force": "^1.0.3", 20 | "d3-interpolate": "^1.1.5", 21 | "d3-selection": "^1.0.2", 22 | "d3-transition": "^1.0.3", 23 | "howler": "^2.0.2", 24 | "lodash": "^4.17.2", 25 | "polished": "^1.9.0", 26 | "presa": "1.0.3", 27 | "react": "^16.2.0", 28 | "react-dom": "^16.2.0", 29 | "react-motion": "^0.5.2", 30 | "react-redux": "^5.0.6", 31 | "react-syntax-highlighter": "^6.0.4", 32 | "react-transition-group": "^2.2.1", 33 | "redux": "^3.6.0", 34 | "redux-actuator": "^2.0.1", 35 | "styled-components": "^2.2.4", 36 | "three": "^0.88.0", 37 | "velocity-animate": "^1.3.1" 38 | }, 39 | "devDependencies": { 40 | "babel-cli": "^6.14.0", 41 | "babel-core": "^6.14.0", 42 | "babel-eslint": "^8.0.2", 43 | "babel-loader": "^7.1.2", 44 | "babel-polyfill": "^6.16.0", 45 | "babel-preset-es2015": "^6.14.0", 46 | "babel-preset-react": "^6.11.1", 47 | "babel-preset-stage-0": "^6.16.0", 48 | "css-loader": "^0.28.7", 49 | "eslint": "^4.11.0", 50 | "eslint-config-prettier": "^2.7.0", 51 | "eslint-config-standard": "^10.2.1", 52 | "eslint-plugin-import": "^2.8.0", 53 | "eslint-plugin-jest": "^21.3.2", 54 | "eslint-plugin-node": "^5.2.1", 55 | "eslint-plugin-prettier": "^2.3.1", 56 | "eslint-plugin-promise": "^3.6.0", 57 | "eslint-plugin-react": "^7.4.0", 58 | "eslint-plugin-standard": "^3.0.1", 59 | "gh-pages-deploy": "^0.4.2", 60 | "node-sass": "^4.7.2", 61 | "prettier": "^1.8.2", 62 | "sass-loader": "^6.0.6", 63 | "style-loader": "^0.19.0", 64 | "uglifyjs-webpack-plugin": "^1.1.2", 65 | "webpack": "^3.8.1", 66 | "webpack-dev-server": "^2.9.5" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/favicon.png -------------------------------------------------------------------------------- /public/images/about-author.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/about-author.jpg -------------------------------------------------------------------------------- /public/images/actuation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/actuation.png -------------------------------------------------------------------------------- /public/images/architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/architecture.jpg -------------------------------------------------------------------------------- /public/images/dan-article.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/dan-article.jpg -------------------------------------------------------------------------------- /public/images/dialog-close.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/dirty-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/dirty-circle.png -------------------------------------------------------------------------------- /public/images/gifs/abstract.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/gifs/abstract.gif -------------------------------------------------------------------------------- /public/images/gifs/bean.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/gifs/bean.gif -------------------------------------------------------------------------------- /public/images/gifs/factory.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/gifs/factory.gif -------------------------------------------------------------------------------- /public/images/gifs/robot-dance.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/gifs/robot-dance.gif -------------------------------------------------------------------------------- /public/images/gifs/storm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/gifs/storm.gif -------------------------------------------------------------------------------- /public/images/gifs/wave.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/gifs/wave.gif -------------------------------------------------------------------------------- /public/images/guys/alex.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/guys/avram.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/guys/gustav.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/guys/jose.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/guys/pierrot.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/guys/tikhon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/images/immutable-chain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/immutable-chain.png -------------------------------------------------------------------------------- /public/images/immutable-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/immutable-logo.png -------------------------------------------------------------------------------- /public/images/immutable-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/immutable-map.png -------------------------------------------------------------------------------- /public/images/moon-emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/moon-emoji.png -------------------------------------------------------------------------------- /public/images/producthunt-badge.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/producthunt-badge.jpg -------------------------------------------------------------------------------- /public/images/rams-radio.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/rams-radio.jpg -------------------------------------------------------------------------------- /public/images/resume-screen-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/resume-screen-1.jpg -------------------------------------------------------------------------------- /public/images/resumeio-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/resumeio-logo.png -------------------------------------------------------------------------------- /public/images/sequence.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/sequence.jpg -------------------------------------------------------------------------------- /public/images/state-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/state-map.png -------------------------------------------------------------------------------- /public/images/stateful-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/stateful-circle.png -------------------------------------------------------------------------------- /public/images/stateless-circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/stateless-circle.png -------------------------------------------------------------------------------- /public/images/stateless-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/stateless-square.png -------------------------------------------------------------------------------- /public/images/sun-emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/sun-emoji.png -------------------------------------------------------------------------------- /public/images/sunglasses-emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/sunglasses-emoji.png -------------------------------------------------------------------------------- /public/images/talks/acko.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/talks/acko.gif -------------------------------------------------------------------------------- /public/images/talks/aerotwist.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/talks/aerotwist.gif -------------------------------------------------------------------------------- /public/images/talks/anim-vdom.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/talks/anim-vdom.gif -------------------------------------------------------------------------------- /public/images/talks/eyeo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/talks/eyeo.gif -------------------------------------------------------------------------------- /public/images/talks/fsm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/talks/fsm.gif -------------------------------------------------------------------------------- /public/images/talks/immut-uis.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/talks/immut-uis.gif -------------------------------------------------------------------------------- /public/images/timeline-ideal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/timeline-ideal.jpg -------------------------------------------------------------------------------- /public/images/timeline-raf.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/timeline-raf.jpg -------------------------------------------------------------------------------- /public/images/timeline-set-timeout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/molefrog/stateful-animations/e237ff128cb9b113ade862c147dcfe2686499576/public/images/timeline-set-timeout.jpg -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |{children}
35 |
36 | export class HightlightSegment extends Component {
37 | componentDidUpdate(prevProps) {
38 | if (!this.$root) {
39 | return
40 | }
41 |
42 | if (!this.props.pure || this.props.text !== prevProps.text) {
43 | this.$root.classList.add('code-preview__highlight--flash')
44 |
45 | // No good :)
46 | setTimeout(
47 | () =>
48 | this.$root &&
49 | this.$root.classList.remove('code-preview__highlight--flash'),
50 | 100
51 | )
52 | }
53 | }
54 |
55 | render() {
56 | return (
57 | {
59 | this.$root = e
60 | }}
61 | className="code-preview__highlight"
62 | >
63 | {this.props.text}
64 |
65 | )
66 | }
67 | }
68 |
69 | DynamicCode.H = HightlightSegment
70 | export default DynamicCode
71 |
--------------------------------------------------------------------------------
/src/blocks/figure-caption.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const FigureCaption = styled.div`
4 | padding: 24px;
5 | font-size: 22px;
6 | min-width: 240px;
7 | max-width: 80%;
8 | text-align: center;
9 | margin: 0 auto;
10 | line-height: 1.4;
11 | `
12 |
13 | export default FigureCaption
14 |
--------------------------------------------------------------------------------
/src/blocks/frame-background.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const FrameBackground = styled.iframe`
4 | position: absolute;
5 | top: 0;
6 | left: 0;
7 | width: 100%;
8 | height: 100%;
9 |
10 | overflow: hidden;
11 | border: none;
12 | `
13 |
14 | export default FrameBackground
15 |
--------------------------------------------------------------------------------
/src/blocks/layout.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Layout = styled.div`
4 | width: 100%;
5 | height: 100%;
6 |
7 | padding: 60px 80px;
8 | box-sizing: border-box;
9 | `
10 |
11 | export default Layout
12 |
--------------------------------------------------------------------------------
/src/colors.js:
--------------------------------------------------------------------------------
1 | const colors = {
2 | green: '#00C967',
3 | gray: '#CFCFCF',
4 | textGray: '#656565',
5 | red: '#FD6669',
6 | yellow: '#FFCC33',
7 | black: '#000000',
8 | pink: '#FB799D'
9 | }
10 |
11 | export default colors
12 |
--------------------------------------------------------------------------------
/src/ficus/bubble-poll/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | import {
4 | forceSimulation,
5 | forceCollide,
6 | forceCenter
7 | } from 'd3-force'
8 |
9 | import pointForce from './point-force'
10 |
11 | class BubblePoll extends Component {
12 | componentDidUpdate () {
13 | this.nodes = this.props.poll.voters.map((v, idx) => {
14 | let node = this.nodes[idx] ? this.nodes[idx] : {}
15 |
16 | return {
17 | r: Math.random() * 4 + 10,
18 | x: 0,
19 | y: 0,
20 | ...node,
21 | choice: v.choice
22 | }
23 | })
24 |
25 | this.choicesIds = this.props.poll.choices.map(ch => ch.id)
26 |
27 | this.simulation.stop()
28 | this.simulation.nodes(this.nodes)
29 | this.simulation.restart()
30 | }
31 |
32 | componentWillUnmount () {
33 | this.simulation.stop()
34 | }
35 |
36 | onTick () {
37 | const canvas = this.pollEl.querySelector('canvas')
38 | const context = canvas.getContext('2d')
39 |
40 | const width = canvas.width
41 | const height = canvas.height
42 |
43 | context.clearRect(0, 0, width, height)
44 | context.save()
45 | context.translate(width / 2, height / 2)
46 |
47 | this.props.poll.choices.forEach(choice => {
48 | context.beginPath()
49 |
50 | this.nodes.forEach(function (d) {
51 | if (d.choice === choice.id) {
52 | context.moveTo(d.x + d.r, d.y)
53 | context.arc(d.x, d.y, d.r, 0, 2 * Math.PI)
54 | }
55 | })
56 |
57 | context.fillStyle = choice.color
58 | context.fill()
59 | })
60 |
61 | context.restore()
62 | }
63 |
64 | nodeTarget (node) {
65 | const r = 220
66 |
67 | const idx = this.choicesIds.indexOf(node.choice)
68 |
69 | return [
70 | r * Math.cos(idx * 2 * Math.PI / this.choicesIds.length),
71 | r * Math.sin(idx * 2 * Math.PI / this.choicesIds.length),
72 | 0.015
73 | ]
74 | }
75 |
76 | componentDidMount () {
77 | this.nodes = this.props.poll.voters.map((n) => ({
78 | r: Math.random() * 4 + 6,
79 | choice: n.choice
80 | }))
81 |
82 | this.choicesIds = this.props.poll.choices.map(ch => ch.id)
83 |
84 | this.simulation = forceSimulation(this.nodes)
85 | .velocityDecay(0.2)
86 | .alphaDecay(0.00001)
87 | .force('target', pointForce(x => this.nodeTarget(x)))
88 | .force('collide', forceCollide().radius(function (d) { return d.r + 3 }).iterations(2))
89 | .force('center', forceCenter())
90 | .on('tick', () => this.onTick())
91 | }
92 |
93 | render () {
94 | return (
95 | {`const redraw = _ => {
121 | points.forEach(point => {
122 |
123 | // make sure \`will-change: transform\` is set
124 | point.element.style.transform = \`
125 | translate3d($\{point.x}px, $\{point.y}px, 0.0px)
126 | rotate($\{point.angle}rad)\`
127 | })
128 | }
129 |
130 | const tick = ts => {
131 | _lastRaf = requestAnimationFrame(tick)
132 |
133 | physicsStep(delta)
134 | redraw(delta)
135 | }`}
136 | requestAnimationFrame
-based game skeleton.
138 | {`
210 | class Dialog extends Component {
211 | componentDidMount() {
212 | const node = findDOMNode(this)
213 |
214 | // Or $.animate, anime.js, GSAP, D3 ...
215 | Velocity(node, { scale: 1.5 },
216 | { duration: 1000 })
217 | }
218 |
219 | render() { ... }
220 | }`}
221 | componentDidMount
lifecycle hook.
224 | {`class Dialog extends Component {
229 | componentDidMount() {
230 | const node = findDOMNode(this)
231 |
232 | // animate returns a cancellable
233 | // Promise-like object
234 | this._anim = animate(node, { ... })
235 | }
236 |
237 | componentWillUnmount() {
238 | this._anim && this._anim.cancel()
239 | }
240 | }`}
241 | {`
250 |
251 | {this.state.showDialog && }
252 |
253 | `}
254 | {`
262 |
263 | {this.state.showDialog && }
264 |
265 | `}
266 | Animated
— a helper component that supports
268 | exit animation
269 | Animated
component
276 | {`const element =
281 |
282 | // => { type: Dialog, props: { size: 'medium' }, ... }
283 | const element = React.createElement(Dialog, { size: 'medium' })`}
284 |
285 | {`
297 | componentWillReceiveProps(nextProps) {
298 | // Exit transition
299 | if (this.props.children && !nextProps.children) {
300 | return this.transitionState(st.EXITING,
301 | { children: this.props.children })
302 | }
303 | }
304 |
305 | transitionState(transitionTo, opt = {}) {
306 | // .. FSM logic ..
307 | // Wait for \`this._content.animateExit()\`
308 | }
309 | `}
310 | Animated
component implementation: {`import Transition
323 | from 'react-transition-group/Transition'
324 |
325 | // \`state\` is 'entered', 'entering', 'exited' etc.
326 |
327 | {state =>
328 | }
329 |
330 | `}
331 |
332 | react-transition-group@2.0
provides {`render() {
342 | return
343 | }
344 |
345 | // Render only once!
346 | shouldComponentUpdate() { return false }
347 |
348 | componentWillReceiveProps(nextProps) {
349 | if (this.props.color != nextProps.color) {
350 | // Animate on canvas...
351 | }
352 | }`}
353 | Canvas
, WebGL
, WebAudio
etc.
357 |
365 | but it can only be used to devide state from props,
366 | so use componentDidUpdate
instead.
367 |
{`// Limit delta to avoid divergence
374 | const delta = Math.min(100.0, ts - prevTs)
375 | const P = 0.001 * delta
376 |
377 | this.x += P * (this.target - x)`}
378 |
379 |
394 | {`import { Actuator, actuate } from 'redux-actuator'
395 |
396 | // Inside the component
397 |
398 |
399 | // Where the business logic is
400 | store.dispatch(actuate('animateBadge'))
401 | store.dispatch(actuate('highlightUser', { id: 1 }))`}
402 |
403 |
427 | {`import { Animated } from 'react-native'
428 |
429 | // inside a constructor
430 | const animValue = new Animated.Value(0)
431 | this.state = { animValue }`}
432 |
433 |
440 | {`const { animValue } = this.state
441 |
442 | `}
454 |
455 |
462 | {`
463 | // or \`spring\`, \`decay\` etc.
464 | Animated.timing(animValue, {
465 | toValue: 0,
466 | duration: 500
467 | }).start()
468 | `}
469 |
470 | {props.name}
10 |
16 | CSS transitions, react-motion,
react-transition-group,
17 | React Move, ...
18 |
19 | Web Animations, Velocity, GSAP, D3, anime.js, ...
30 | {`this.x = props.rotX`}
88 | {`this.x += P * (props.rotX - this.x)`}
95 | {`// Or use a polyfill:
14 | // import requestAnimationFrame from 'raf'
15 | const { requestAnimationFrame } = window
16 |
17 | const animate = () => {
18 | requestAnimationFrame(animate)
19 |
20 | // Perform an animation step
21 | x += velocity
22 | }
23 |
24 | // Fire it up 🔥
25 | requestAnimationFrame(animate)`}
26 | {`requestAnimationFrame(timestamp => {
36 | // DOMHighResTimeStamp
37 | // timestamp ~> 30485.84100000153
38 | })`}
39 | {`const animate = timestamp => {
53 | requestAnimationFrame(animate)
54 |
55 | const delta = timestamp - prevTimestamp
56 |
57 | // Note, it's a function now!
58 | x += velocity(delta)
59 | }`}
60 | {`
13 |
14 | {interpolated =>
15 | }
16 | `}
17 | function-as-a-prop
pattern.
20 | {`
31 | // CSS property
32 | // transition: transform 1s ease;
33 |
34 | // Conditional state change
35 |
36 |
37 | // Direct style manipulation
38 | `}
39 | transition
property + state change → animation.
42 |