├── .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 |
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 |
--------------------------------------------------------------------------------