├── .gitignore ├── .tern-port ├── README.md ├── build ├── Thumbs.db ├── asset-manifest.json ├── favicon.ico ├── index.html └── static │ └── css │ ├── main.b1a1cd0e.css │ └── main.b1a1cd0e.css.map ├── package.json ├── public ├── Thumbs.db ├── favicon.ico └── index.html ├── src ├── App.css ├── App.js ├── App.test.js ├── index.css ├── index.js ├── logo.svg └── react-construct │ └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /.tern-port: -------------------------------------------------------------------------------- 1 | 34838 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > This is just a weird experiment 2 | 3 | # http://worms.io/react-construct/ 4 | 5 | ### `app.js` 6 | ```javascript 7 | import React, { Component } from 'react'; 8 | import './App.css'; 9 | 10 | import Construct, { 11 | GameObject, KeyDown, 12 | bullet, boundToWindow, fade, rotate, 13 | } from './react-construct' 14 | 15 | class App extends Component { 16 | constructor() { 17 | super() 18 | this.state = { 19 | playerAngle: 0 20 | } 21 | } 22 | render() { 23 | return ( 24 | 25 |

Try WASD

26 | this.setState({ playerAngle: 270 }) } /> 27 | this.setState({ playerAngle: 180 }) } /> 28 | this.setState({ playerAngle: 90 }) } /> 29 | this.setState({ playerAngle: 0 }) } /> 30 | 44 |
45 | ); 46 | } 47 | } 48 | 49 | export default App; 50 | ``` 51 | 52 | ### `index.js` 53 | ```javascript 54 | import React from 'react'; 55 | import ReactDOM from 'react-dom'; 56 | import App from './App'; 57 | import './index.css'; 58 | 59 | ReactDOM.render( 60 | , 61 | document.getElementById('root') 62 | ); 63 | ``` 64 | 65 | ### `index.html` 66 | ```javascript 67 | 68 |
69 | 70 | ``` 71 | -------------------------------------------------------------------------------- /build/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoWorms/react-construct/7a66b743c4216f0393f951d138dc854ac41520ea/build/Thumbs.db -------------------------------------------------------------------------------- /build/asset-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "main.css": "static/css/main.b1a1cd0e.css", 3 | "main.css.map": "static/css/main.b1a1cd0e.css.map", 4 | "main.js": "static/js/main.b9820750.js", 5 | "main.js.map": "static/js/main.b9820750.js.map" 6 | } -------------------------------------------------------------------------------- /build/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoWorms/react-construct/7a66b743c4216f0393f951d138dc854ac41520ea/build/favicon.ico -------------------------------------------------------------------------------- /build/index.html: -------------------------------------------------------------------------------- 1 | React App
-------------------------------------------------------------------------------- /build/static/css/main.b1a1cd0e.css: -------------------------------------------------------------------------------- 1 | .App{text-align:center}.App-logo{-webkit-animation:App-logo-spin infinite 20s linear;animation:App-logo-spin infinite 20s linear;height:80px}.App-header{background-color:#222;height:150px;padding:20px;color:#fff}.App-intro{font-size:large}@-webkit-keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes App-logo-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}body{margin:0;padding:0;font-family:sans-serif}body,html{height:100%} 2 | /*# sourceMappingURL=main.b1a1cd0e.css.map*/ -------------------------------------------------------------------------------- /build/static/css/main.b1a1cd0e.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"static/css/main.b1a1cd0e.css","sourceRoot":""} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-construct", 3 | "homepage": "http://worms.io/react-construct", 4 | "version": "0.1.0", 5 | "private": true, 6 | "devDependencies": { 7 | "react-scripts": "0.7.0" 8 | }, 9 | "dependencies": { 10 | "gh-pages": "^0.11.0", 11 | "react": "^15.3.2", 12 | "react-dom": "^15.3.2" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test --env=jsdom", 18 | "eject": "react-scripts eject", 19 | "deploy": "npm run build && gh-pages -d ./build" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /public/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoWorms/react-construct/7a66b743c4216f0393f951d138dc854ac41520ea/public/Thumbs.db -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MarcoWorms/react-construct/7a66b743c4216f0393f951d138dc854ac41520ea/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import Construct, { 4 | GameObject, 5 | KeyDown, 6 | bullet, 7 | boundToWindow, 8 | fade, 9 | rotate, 10 | } from './react-construct' 11 | 12 | class App extends Component { 13 | constructor() { 14 | super() 15 | this.state = { 16 | playerAngle: 0 17 | } 18 | } 19 | render() { 20 | return ( 21 | 22 |

Try WASD

23 | this.setState({ playerAngle: 270 }) } /> 24 | this.setState({ playerAngle: 180 }) } /> 25 | this.setState({ playerAngle: 90 }) } /> 26 | this.setState({ playerAngle: 0 }) } /> 27 | 49 |
50 | ); 51 | } 52 | } 53 | 54 | export default App; 55 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | 7 | html, body { 8 | height: 100%; 9 | } 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/react-construct/index.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | 3 | export default class Construct extends Component { 4 | render() { 5 | return ( 6 |
7 | {this.props.children} 8 |
9 | ) 10 | } 11 | } 12 | 13 | export class GameObject extends Component { 14 | constructor(props) { 15 | super(props) 16 | this.state = { 17 | update: this.props.behaviours.map( behaviour => 18 | behaviour.update), 19 | x: this.props.initial.x || 0, 20 | y: this.props.initial.y || 0, 21 | width: this.props.initial.width || 0, 22 | height: this.props.initial.height || 0, 23 | opacity: this.props.initial.opacity || 1, 24 | angle: 0, 25 | anchor: this.props.initial.anchor || [0.5, 0.5], 26 | lastFrame: performance.now() 27 | } 28 | } 29 | handleUpdate() { 30 | const now = performance.now() 31 | const dt = (now - this.state.lastFrame) / 1000 32 | 33 | const finalState = this.state.update.reduce((nextState, update) => { 34 | return update(nextState, dt) 35 | }, this.state) 36 | 37 | this.setState({ 38 | ...finalState, 39 | lastFrame: now, 40 | update: this.props.behaviours.map( behaviour => 41 | behaviour.update) 42 | }) 43 | 44 | window.requestAnimationFrame(this.handleUpdate.bind(this)) 45 | } 46 | componentDidMount() { 47 | this.handleUpdate() 48 | } 49 | render() { 50 | return ( 51 |
66 | ) 67 | } 68 | } 69 | 70 | export class KeyDown extends Component { 71 | componentDidMount() { 72 | document.addEventListener('keydown', (event) => { 73 | if (event.key === this.props.when) { 74 | this.props.do() 75 | } 76 | }) 77 | } 78 | shouldComponentUpdate() { 79 | return false 80 | } 81 | render() { 82 | return null 83 | } 84 | } 85 | 86 | export function bullet ({ speed, motionAngle, setAngle }) { 87 | return { 88 | update(state, dt) { 89 | const x = state.x + (speed * cos(motionAngle)) * dt 90 | const y = state.y + (speed * sin(motionAngle)) * dt 91 | const angle = setAngle ? motionAngle : state.angle 92 | 93 | const nextState = { ...state, x, y, angle } 94 | return nextState 95 | } 96 | } 97 | } 98 | 99 | export function rotate ({ speed }) { 100 | return { 101 | update(state, dt) { 102 | const angle = state.angle + (speed * dt) 103 | const nextState = { ...state, angle } 104 | return nextState 105 | } 106 | } 107 | } 108 | 109 | export function boundToWindow () { 110 | return { 111 | update(state, dt) { 112 | const width = document.body.getBoundingClientRect().right 113 | const height = document.body.getBoundingClientRect().bottom 114 | const x = (state.x < 0) 115 | ? 0 116 | : (state.x > width) 117 | ? width 118 | : state.x 119 | 120 | const y = (state.y < 0) 121 | ? 0 122 | : (state.y > height) 123 | ? height 124 | : state.y 125 | 126 | const nextState = { ...state, x, y } 127 | return nextState 128 | } 129 | } 130 | } 131 | 132 | export function fade ({ delay, enter, stay, leave, loop}) { 133 | delay = delay || 0 134 | enter = enter || 0 135 | stay = stay || 0 136 | leave = leave || 0 137 | return { 138 | update(state, dt) { 139 | 140 | const timeSpent = (state.timeSpent || 0) + dt 141 | 142 | const fadeState = 143 | (delay ? (timeSpent <= delay) : false) ? 'delay' : 144 | (enter ? (timeSpent <= delay + enter) : false) ? 'enter' : 145 | (stay ? (timeSpent <= delay + enter + stay) : false) ? 'stay' : 146 | (leave ? (timeSpent <= delay + enter + stay + leave) : false) ? 'leave' : 'end' 147 | 148 | const opacity = 149 | (fadeState === 'delay') ? 0 : 150 | (fadeState === 'enter') ? 151 | lerp( 152 | 0, 153 | 1, 154 | (timeSpent - delay) / enter 155 | ) : 156 | (fadeState === 'stay') ? 1 : 157 | (fadeState === 'leave') ? 158 | lerp( 159 | 1, 160 | 0, 161 | (timeSpent - (delay + enter + stay)) / leave 162 | ) : 163 | (fadeState === 'end') ? state.opacity : 0 164 | 165 | const nextState = { 166 | ...state, 167 | timeSpent: (fadeState === 'end' && loop) ? 0 : timeSpent, 168 | opacity 169 | } 170 | 171 | return nextState 172 | } 173 | } 174 | } 175 | 176 | function lerp (start, end, proporcao) { 177 | return start + (proporcao * (end - start)) 178 | } 179 | 180 | function sin (angle) { 181 | return Math.sin(Math.PI * (angle/180)) 182 | } 183 | 184 | function cos (angle) { 185 | return Math.cos(Math.PI * (angle/180)) 186 | } 187 | --------------------------------------------------------------------------------