parent
0d97878596
commit
f6f690837e
13 changed files with 491 additions and 383 deletions
@ -0,0 +1,131 @@ |
|||||||
|
// Scare mechanic, single particle.
|
||||||
|
import Rx, { Observable } from 'rxjs'; |
||||||
|
import AnimationBase from './animationBase'; |
||||||
|
import DOM from './dom'; |
||||||
|
import Store from './store'; |
||||||
|
|
||||||
|
const evtScare = (scareX, scareY) => new CustomEvent("scare", { detail: { scareX, scareY } }); |
||||||
|
|
||||||
|
const particleDiv = document.createElement('div'); |
||||||
|
particleDiv.className = 'anim2-particle'; |
||||||
|
|
||||||
|
function checkScare([evt, store]) { |
||||||
|
const state = store.get(); |
||||||
|
|
||||||
|
const { evtX, evtY } = DOM.getEventOffsetCoords(evt, DOM.containerBounds); |
||||||
|
const diffX = Math.abs(state.x - evtX); |
||||||
|
const diffY = Math.abs(state.y - evtY); |
||||||
|
|
||||||
|
if (evt.target === particleDiv) { |
||||||
|
DOM.container.dispatchEvent(evtScare(evtX, evtY)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
function move(acc, i) { |
||||||
|
let { x, y, dx, dy } = acc; |
||||||
|
|
||||||
|
const east = DOM.containerBounds.width - particleDiv.offsetWidth; |
||||||
|
const south = DOM.containerBounds.height - particleDiv.offsetHeight; |
||||||
|
|
||||||
|
x += dx; |
||||||
|
y += dy; |
||||||
|
|
||||||
|
if (x < 0) { |
||||||
|
x = Math.abs(x); |
||||||
|
dx = -dx; |
||||||
|
} |
||||||
|
|
||||||
|
if (x > east) { |
||||||
|
x = Math.round(2 * east - x); |
||||||
|
dx = -dx; |
||||||
|
} |
||||||
|
|
||||||
|
if (y < 0) { |
||||||
|
y = Math.abs(y); |
||||||
|
dy = -dy; |
||||||
|
} |
||||||
|
|
||||||
|
if (y > south) { |
||||||
|
y = Math.round(2 * south - y); |
||||||
|
dy = -dy; |
||||||
|
} |
||||||
|
|
||||||
|
return { x, y, dx, dy }; |
||||||
|
}; |
||||||
|
|
||||||
|
function flee([evt, store]) { |
||||||
|
const initialState = store.get(); |
||||||
|
const fleeRadius = 200; |
||||||
|
const { scareX, scareY } = evt.detail; |
||||||
|
const fps$ = Rx.Observable.interval(1000 / 32); |
||||||
|
|
||||||
|
const frames$ = fps$ |
||||||
|
.scan(move, initialState) |
||||||
|
.takeWhile(state => { |
||||||
|
const xDanger = Math.abs(initialState.x - state.x) < fleeRadius; |
||||||
|
const yDanger = Math.abs(initialState.y - state.y) < fleeRadius; |
||||||
|
|
||||||
|
return xDanger && yDanger; |
||||||
|
}) |
||||||
|
|
||||||
|
frames$.last().subscribe(finalState => { |
||||||
|
store.set(finalState); |
||||||
|
store.set(randomMoveVector()); |
||||||
|
}); |
||||||
|
|
||||||
|
frames$.subscribe(state => { |
||||||
|
particleDiv.style.left = `${state.x}px`; |
||||||
|
particleDiv.style.top = `${state.y}px`; |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
function randomMoveVector() { |
||||||
|
const speed = 10; |
||||||
|
let dx = Math.round(Math.random() * speed); |
||||||
|
let dy = Math.pow(Math.pow(speed, 2) - Math.pow(dx, 2), 0.5); |
||||||
|
|
||||||
|
const negX = Math.random() < 0.5 ? -1 : 1; |
||||||
|
const negY = Math.random() < 0.5 ? -1 : 1; |
||||||
|
|
||||||
|
dx *= negX; |
||||||
|
dy *= negY; |
||||||
|
|
||||||
|
return { dx, dy }; |
||||||
|
} |
||||||
|
|
||||||
|
function reset() { |
||||||
|
if (particleDiv.parentNode) { |
||||||
|
DOM.container.removeChild(particleDiv); |
||||||
|
} |
||||||
|
|
||||||
|
const { dx, dy } = randomMoveVector(); |
||||||
|
const store = new Store({ x: 0, y: 0, dx, dy }); |
||||||
|
const state = store.get(); |
||||||
|
|
||||||
|
particleDiv.style.top = `${state.y}px`; |
||||||
|
particleDiv.style.left = `${state.x}px`; |
||||||
|
|
||||||
|
DOM.container.appendChild(particleDiv); |
||||||
|
|
||||||
|
return store; |
||||||
|
}; |
||||||
|
|
||||||
|
function init() { |
||||||
|
const store = reset(); |
||||||
|
|
||||||
|
const click$ = Rx.Observable |
||||||
|
.fromEvent(DOM.container, 'click') |
||||||
|
// .do(DOM.calcBounds)
|
||||||
|
.do(DOM.highlight) |
||||||
|
.map(evt => [evt, store]) |
||||||
|
.subscribe(checkScare); |
||||||
|
|
||||||
|
Rx.Observable |
||||||
|
.fromEvent(DOM.container, 'scare') |
||||||
|
.map(evt => [evt, store]) |
||||||
|
.subscribe(flee); |
||||||
|
}; |
||||||
|
|
||||||
|
const Animation2 = Object.assign({}, AnimationBase, { init, reset }); |
||||||
|
|
||||||
|
export default Animation2; |
@ -1,131 +1,87 @@ |
|||||||
// Scare mechanic, single particle.
|
|
||||||
import Rx, { Observable } from 'rxjs'; |
import Rx, { Observable } from 'rxjs'; |
||||||
import AnimationBase from './animationBase'; |
import Particle from './particle'; |
||||||
import DOM from './dom'; |
|
||||||
import Store from './store'; |
import Store from './store'; |
||||||
|
import Controls from './controls'; |
||||||
|
import { CONTROLS } from './enums'; |
||||||
|
|
||||||
const evtScare = (scareX, scareY) => new CustomEvent("scare", { detail: { scareX, scareY } }); |
function Animation2() { |
||||||
|
this.options = { |
||||||
|
animating: false, |
||||||
|
count: 1, |
||||||
|
maxCount: 3000, |
||||||
|
speed: 4 |
||||||
|
}; |
||||||
|
|
||||||
const particleDiv = document.createElement('div'); |
this.container = document.getElementById('animation2'); |
||||||
particleDiv.className = 'anim2-particle'; |
this.bounds = this.container.getBoundingClientRect(); |
||||||
|
|
||||||
function checkScare([evt, store]) { |
this.particles = []; |
||||||
const state = store.get(); |
|
||||||
|
|
||||||
const { evtX, evtY } = DOM.getEventOffsetCoords(evt, DOM.containerBounds); |
const controls = new Controls( |
||||||
const diffX = Math.abs(state.x - evtX); |
document.getElementById('controls2'), |
||||||
const diffY = Math.abs(state.y - evtY); |
this.options |
||||||
|
); |
||||||
|
|
||||||
if (evt.target === particleDiv) { |
controls.mount().subscribe(this.subscriber.bind(this)); |
||||||
DOM.container.dispatchEvent(evtScare(evtX, evtY)); |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
function move(acc, i) { |
this.updateAnimating(this.options.animating); |
||||||
let { x, y, dx, dy } = acc; |
this.updateCount(this.options.count); |
||||||
|
|
||||||
const east = DOM.containerBounds.width - particleDiv.offsetWidth; |
// TODO X dimension modified by core UI
|
||||||
const south = DOM.containerBounds.height - particleDiv.offsetHeight; |
// TODO "shaky" / "stuck" bug
|
||||||
|
|
||||||
x += dx; |
// TODO ANIM3 Show vision grid (including touches!)
|
||||||
y += dy; |
// TODO ANIM3 regen vision grid is option updated
|
||||||
|
// TODO ANIM3 cleanup vision grid
|
||||||
if (x < 0) { |
// TODO ANIM3 Pallet avoidance
|
||||||
x = Math.abs(x); |
|
||||||
dx = -dx; |
|
||||||
} |
|
||||||
|
|
||||||
if (x > east) { |
// TODO ANIM4 Flocking
|
||||||
x = Math.round(2 * east - x); |
|
||||||
dx = -dx; |
|
||||||
} |
|
||||||
|
|
||||||
if (y < 0) { |
|
||||||
y = Math.abs(y); |
|
||||||
dy = -dy; |
|
||||||
} |
|
||||||
|
|
||||||
if (y > south) { |
|
||||||
y = Math.round(2 * south - y); |
|
||||||
dy = -dy; |
|
||||||
} |
|
||||||
|
|
||||||
return { x, y, dx, dy }; |
|
||||||
}; |
}; |
||||||
|
|
||||||
function flee([evt, store]) { |
Animation2.prototype.subscriber = function({ key, value }) { |
||||||
const initialState = store.get(); |
switch(key) { |
||||||
const fleeRadius = 200; |
case CONTROLS.ANIMATING: this.updateAnimating(value); break; |
||||||
const { scareX, scareY } = evt.detail; |
case CONTROLS.COUNT: this.updateCount(value); break; |
||||||
const fps$ = Rx.Observable.interval(1000 / 32); |
case CONTROLS.SPEED: this.updateSpeed(value); break; |
||||||
|
} |
||||||
const frames$ = fps$ |
} |
||||||
.scan(move, initialState) |
|
||||||
.takeWhile(state => { |
|
||||||
const xDanger = Math.abs(initialState.x - state.x) < fleeRadius; |
|
||||||
const yDanger = Math.abs(initialState.y - state.y) < fleeRadius; |
|
||||||
|
|
||||||
return xDanger && yDanger; |
|
||||||
}) |
|
||||||
|
|
||||||
frames$.last().subscribe(finalState => { |
|
||||||
store.set(finalState); |
|
||||||
store.set(randomMoveVector()); |
|
||||||
}); |
|
||||||
|
|
||||||
frames$.subscribe(state => { |
|
||||||
particleDiv.style.left = `${state.x}px`; |
|
||||||
particleDiv.style.top = `${state.y}px`; |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
function randomMoveVector() { |
Animation2.prototype.nextFrame = function() { |
||||||
const speed = 10; |
this.particles.forEach(p => p.nextFrame()); |
||||||
let dx = Math.round(Math.random() * speed); |
} |
||||||
let dy = Math.pow(Math.pow(speed, 2) - Math.pow(dx, 2), 0.5); |
|
||||||
|
|
||||||
const negX = Math.random() < 0.5 ? -1 : 1; |
Animation2.prototype.updateAnimating = function(isAnimating) { |
||||||
const negY = Math.random() < 0.5 ? -1 : 1; |
this.options.animating = isAnimating; |
||||||
|
|
||||||
dx *= negX; |
if (isAnimating) { |
||||||
dy *= negY; |
const fps$ = Rx.Observable.interval(1000 / 32) |
||||||
|
.takeWhile(_ => this.options.animating); |
||||||
|
|
||||||
return { dx, dy }; |
fps$.subscribe(this.nextFrame.bind(this)); |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
function reset() { |
Animation2.prototype.updateCount = function(count) { |
||||||
if (particleDiv.parentNode) { |
while (this.particles.length >= count) { |
||||||
DOM.container.removeChild(particleDiv); |
const p = this.particles.pop(); |
||||||
|
this.container.removeChild(p.node); |
||||||
} |
} |
||||||
|
|
||||||
const { dx, dy } = randomMoveVector(); |
while (this.particles.length < count) { |
||||||
const store = new Store({ x: 0, y: 0, dx, dy }); |
const p = new Particle(this.container, this.bounds, this.options); |
||||||
const state = store.get(); |
this.particles.push(p); |
||||||
|
} |
||||||
particleDiv.style.top = `${state.y}px`; |
} |
||||||
particleDiv.style.left = `${state.x}px`; |
|
||||||
|
|
||||||
DOM.container.appendChild(particleDiv); |
|
||||||
|
|
||||||
return store; |
|
||||||
}; |
|
||||||
|
|
||||||
function init() { |
|
||||||
const store = reset(); |
|
||||||
|
|
||||||
const click$ = Rx.Observable |
|
||||||
.fromEvent(DOM.container, 'click') |
|
||||||
// .do(DOM.calcBounds)
|
|
||||||
.do(DOM.highlight) |
|
||||||
.map(evt => [evt, store]) |
|
||||||
.subscribe(checkScare); |
|
||||||
|
|
||||||
Rx.Observable |
Animation2.prototype.updateMovementCircle = function(showMovementCircle) { |
||||||
.fromEvent(DOM.container, 'scare') |
this.options.showMovementCircle = showMovementCircle; |
||||||
.map(evt => [evt, store]) |
this.movementCircleCtrl.querySelector('[type=checkbox]').checked = showMovementCircle; |
||||||
.subscribe(flee); |
this.particles.forEach(p => p.updateOptions({ showMovementCircle })); |
||||||
}; |
} |
||||||
|
|
||||||
const Animation2 = Object.assign({}, AnimationBase, { init, reset }); |
Animation2.prototype.updateSpeed = function(speed) { |
||||||
|
this.options.speed = speed; |
||||||
|
this.particles.forEach(p => p.updateOptions({ speed })); |
||||||
|
} |
||||||
|
|
||||||
export default Animation2; |
export default Animation2; |
||||||
|
@ -1,110 +0,0 @@ |
|||||||
// Join mechanic, multiple particles
|
|
||||||
// ????? Chase, maybe?
|
|
||||||
// ???? Occupied?
|
|
||||||
|
|
||||||
// called each frame update
|
|
||||||
// find if going to hit a wall
|
|
||||||
// find if palette nearby
|
|
||||||
|
|
||||||
// const grid = {};
|
|
||||||
// for (let x = 0; x <= 600; x += 5) {
|
|
||||||
// grid[x] = {};
|
|
||||||
// for (let y = 0; y <= 600; y += 5) {
|
|
||||||
// grid[x][y] = { type: null };
|
|
||||||
//
|
|
||||||
// if (x === 0 || y === 0 || x === 600 || y === 600) {
|
|
||||||
// grid[x][y] = { type: 'wall' };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
import Rx, { Observable } from 'rxjs'; |
|
||||||
import AnimationBase from './animationBase'; |
|
||||||
import DOM from './dom'; |
|
||||||
import Store from './store'; |
|
||||||
|
|
||||||
const evtScare = (detail) => new CustomEvent('scare', { detail }); |
|
||||||
|
|
||||||
const [particles, state] = (new Array(5)).fill(null).reduce((acc, v, i) => { |
|
||||||
const div = document.createElement('div'); |
|
||||||
div.className = 'anim3-particle'; |
|
||||||
div.innerHTML = '' |
|
||||||
|
|
||||||
const x = 0; |
|
||||||
const y = i * 25; |
|
||||||
|
|
||||||
div.style.left = 0 |
|
||||||
div.style.top = `${y}px`; |
|
||||||
|
|
||||||
acc[0].push(div); |
|
||||||
acc[1].push({ x, y }); |
|
||||||
|
|
||||||
return acc; |
|
||||||
}, [[], []]); |
|
||||||
|
|
||||||
const palettes = (new Array(1)).fill(null).reduce((acc, v, i) => { |
|
||||||
const div = document.createElement('div'); |
|
||||||
div.className = 'palette'; |
|
||||||
div.style.left = '200px'; |
|
||||||
div.style.top = '200px'; |
|
||||||
|
|
||||||
acc.push(div); |
|
||||||
|
|
||||||
return acc; |
|
||||||
}, []); |
|
||||||
|
|
||||||
function scare(evt) { |
|
||||||
const bounds = DOM.container.getBoundingClientRect(); |
|
||||||
const { evtX: x, evtY: y } = DOM.getEventOffsetCoords(evt, bounds); |
|
||||||
const scareRadius = 50; |
|
||||||
|
|
||||||
state.forEach((coord, i) => { |
|
||||||
const diffX = Math.abs(coord.x - x + 10); |
|
||||||
const diffY = Math.abs(coord.y - y + 10); |
|
||||||
|
|
||||||
if (diffX < 100 && diffY < 100) { |
|
||||||
coord.lastScare = { x, y } // TODO set state with last scare, then judge per frame based on that number to avoid jump
|
|
||||||
DOM.container.dispatchEvent(evtScare({ x, y, i })); |
|
||||||
} |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function reset() { |
|
||||||
while (DOM.container.childNodes.length) { |
|
||||||
DOM.container.removeChild(DOM.container.firstChild); |
|
||||||
} |
|
||||||
|
|
||||||
particles.forEach((div) => { |
|
||||||
div.innerHTML = ''; |
|
||||||
DOM.container.appendChild(div) |
|
||||||
}); |
|
||||||
|
|
||||||
console.warn(palettes) |
|
||||||
|
|
||||||
palettes.forEach((div) => { |
|
||||||
DOM.container.appendChild(div) |
|
||||||
}); |
|
||||||
}; |
|
||||||
|
|
||||||
function init() { |
|
||||||
reset(); |
|
||||||
|
|
||||||
const click$ = Rx.Observable.fromEvent(DOM.container, 'click'); |
|
||||||
const scare$ = Rx.Observable.fromEvent(DOM.container, 'scare').auditTime(100); |
|
||||||
|
|
||||||
scare$.subscribe(evt => { |
|
||||||
particles[evt.detail.i].innerHTML = 'S' |
|
||||||
DOM.addClass(particles[evt.detail.i], 'scared'); |
|
||||||
const p = particles[evt.detail.i]; |
|
||||||
setTimeout(() => { |
|
||||||
p.innerHTML = ''; |
|
||||||
DOM.removeClass(p, 'scared'); |
|
||||||
}, 1000); |
|
||||||
}); |
|
||||||
|
|
||||||
click$.subscribe(scare); |
|
||||||
}; |
|
||||||
|
|
||||||
const Animation3 = Object.assign({}, AnimationBase, { init, reset }); |
|
||||||
|
|
||||||
export default Animation3; |
|
Loading…
Reference in new issue