diff --git a/css/controls.scss b/css/controls.scss index fd4edce..2343327 100644 --- a/css/controls.scss +++ b/css/controls.scss @@ -1,32 +1,48 @@ -.controls-label { +.controls-checkbox { cursor: pointer; - font-size: 13px; - margin-bottom: 5px; - padding: 10px; + line-height: 45px; + padding: 0 10px; &:hover { - background: #fff; + background: #d7d7d7; } } -.controls-label-checkbox { +.controls-checkbox-text { + font-size: 11px; +} + +.controls-checkbox-input { margin-right: 5px; } -.controls-label-text { +.controls-range { + cursor: pointer; + line-height: 20px; + padding: 0 10px; + + &:hover { + background: #d7d7d7; + } +} +.controls-range-text { + cursor: pointer; + font-size: 11px; } -.controls-slider { +.controls-range-input { cursor: pointer; - width: 100%; + display: block; + width: 100px; } .controls-animating { cursor: pointer; font-size: 18px; - line-height: 20px; - padding: 10px; + line-height: 45px; + margin-left: auto; + padding: 0 10px; user-select: none; [type=checkbox] { @@ -34,14 +50,10 @@ } &:hover { - background: #fff; + background: #d7d7d7; } &:focus { outline: 0; } } - -.controls-bottom { - margin-top: auto; -} diff --git a/css/index.scss b/css/index.scss index 4f08b55..f705c12 100644 --- a/css/index.scss +++ b/css/index.scss @@ -1,29 +1,28 @@ .outerContainer { - height: 400px; - margin: 10px auto; + height: 500px; + margin: 10px 0; overflow: hidden; position: relative; - width: 90%; + width: 100%; } .controlsContainer { - background: #ccc; - border-left: 2px groove #c0c0c0; - bottom: 0; + background: #e7e7e7; + border-bottom: 5px solid #aaa; display: flex; - flex-direction: column; - padding: 10px; + height: 50px; + left: 0; position: absolute; right: 0; top: 0; - width: 170px; z-index: 1; } .animationContainer { - background: rgba(102, 51, 153, 0.1); + background: rgba(102, 51, 153, 0.05); + // background: #f0f0f0; height: 100%; - margin-right: 170px; + margin-top: 50px; position: relative; z-index: 0; } diff --git a/css/particle.scss b/css/particle.scss index cc11192..4b60e43 100644 --- a/css/particle.scss +++ b/css/particle.scss @@ -1,10 +1,12 @@ .particle { $side: 20px; - background: url('../res/seahorse.svg') no-repeat center center #aaa; + // background: url('../res/seahorse.svg') no-repeat center center #aaa; + background-color: #555; background-size: $side $side; border-radius: $side / 2; color: #fff; + font-size: 11px; height: $side; line-height: $side; margin: #{-$side / 2} 0 0 #{-$side / 2}; @@ -13,40 +15,29 @@ width: $side; z-index: 1; - &.has-vision::after { - border: 50px solid; - border-color: lightgreen transparent transparent turquoise; - border-radius: 50px; - content: ' '; - height: 0; - left: -50px; - margin: #{$side / 2} 0 0 #{$side / 2}; - opacity: 0.5; - position: absolute; - transform: rotate(45deg); - top: -50px; - width: 0; - } + // &.has-vision::after { + // border: 50px solid; + // border-color: lightgreen transparent transparent turquoise; + // border-radius: 50px; + // content: ' '; + // height: 0; + // left: -50px; + // margin: #{$side / 2} 0 0 #{$side / 2}; + // opacity: 0.5; + // position: absolute; + // transform: rotate(45deg); + // top: -50px; + // width: 0; + // z-index: 0; + // } } .particle-movement-circle { - border: 2px dotted darkturquoise; + border: 3px dotted darkturquoise; + margin-left: 10px; + margin-top: 10px; position: absolute; - transition: left 0.2s, top 0.2s, height 0.2s, width 0.2s; z-index: 0; - - &:after { - background: darkturquoise; - border-radius: 2px; - content:' '; - height: 4px; - left: 50%; - margin-left: -2px; - margin-top: -2px; - position: absolute; - top: 50%; - width: 4px; - } } .particle-vision-grid { diff --git a/css/style.css b/css/style.css index 5dd3b91..3ba6b7e 100644 --- a/css/style.css +++ b/css/style.css @@ -6,36 +6,35 @@ body { font-family: sans-serif; } .outerContainer { - height: 400px; - margin: 10px auto; + height: 500px; + margin: 10px 0; overflow: hidden; position: relative; - width: 90%; } + width: 100%; } .controlsContainer { - background: #ccc; - border-left: 2px groove #c0c0c0; - bottom: 0; + background: #e7e7e7; + border-bottom: 5px solid #aaa; display: flex; - flex-direction: column; - padding: 10px; + height: 50px; + left: 0; position: absolute; right: 0; top: 0; - width: 170px; z-index: 1; } .animationContainer { - background: rgba(102, 51, 153, 0.1); + background: rgba(102, 51, 153, 0.05); height: 100%; - margin-right: 170px; + margin-top: 50px; position: relative; z-index: 0; } .particle { - background: url(../res/seahorse.svg) no-repeat center center #aaa; + background-color: #555; background-size: 20px 20px; border-radius: 10px; color: #fff; + font-size: 11px; height: 20px; line-height: 20px; margin: -10px 0 0 -10px; @@ -43,36 +42,13 @@ body { text-align: center; width: 20px; z-index: 1; } - .particle.has-vision::after { - border: 50px solid; - border-color: lightgreen transparent transparent turquoise; - border-radius: 50px; - content: ' '; - height: 0; - left: -50px; - margin: 10px 0 0 10px; - opacity: 0.5; - position: absolute; - transform: rotate(45deg); - top: -50px; - width: 0; } .particle-movement-circle { - border: 2px dotted darkturquoise; + border: 3px dotted darkturquoise; + margin-left: 10px; + margin-top: 10px; position: absolute; - transition: left 0.2s, top 0.2s, height 0.2s, width 0.2s; z-index: 0; } - .particle-movement-circle:after { - background: darkturquoise; - border-radius: 2px; - content: ' '; - height: 4px; - left: 50%; - margin-left: -2px; - margin-top: -2px; - position: absolute; - top: 50%; - width: 4px; } .particle-vision-grid { height: 100px; @@ -91,33 +67,45 @@ body { background: green; } .particle-vision-dot.touching { outline: 2px solid yellow; } -.controls-label { +.controls-checkbox { cursor: pointer; - font-size: 13px; - margin-bottom: 5px; - padding: 10px; } - .controls-label:hover { - background: #fff; } + line-height: 45px; + padding: 0 10px; } + .controls-checkbox:hover { + background: #d7d7d7; } + +.controls-checkbox-text { + font-size: 11px; } -.controls-label-checkbox { +.controls-checkbox-input { margin-right: 5px; } -.controls-slider { +.controls-range { cursor: pointer; - width: 100%; } + line-height: 20px; + padding: 0 10px; } + .controls-range:hover { + background: #d7d7d7; } + +.controls-range-text { + cursor: pointer; + font-size: 11px; } + +.controls-range-input { + cursor: pointer; + display: block; + width: 100px; } .controls-animating { cursor: pointer; font-size: 18px; - line-height: 20px; - padding: 10px; + line-height: 45px; + margin-left: auto; + padding: 0 10px; user-select: none; } .controls-animating [type=checkbox] { display: none; } .controls-animating:hover { - background: #fff; } + background: #d7d7d7; } .controls-animating:focus { outline: 0; } - -.controls-bottom { - margin-top: auto; } diff --git a/index.html b/index.html index 87ca976..a79967f 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@
- ++
The trickiest portion of this iteration was animating the curved paths. Elliptical geometry was the initial goal, but calculating arc length (to maintain a scalar speed) is quite difficult. Smoothstep cubic curves diff --git a/js/animation1.js b/js/animation1.js index fc3f656..697a7fc 100644 --- a/js/animation1.js +++ b/js/animation1.js @@ -8,40 +8,71 @@ function Animation1() { this.options = { animating: false, count: 1, + maxCount: 25, randomizeRadius: true, randomizeRotation: true, - // showMovementCircle: false, - // showVIsionGrid: false, + showMovementCircle: false, + // showVisionGrid: false, speed: 4 }; this.container = document.getElementById('animation1'); this.bounds = this.container.getBoundingClientRect(); + this.movementCircleCtrl = createMovementCircleControl(); this.particles = []; - const controls = new Controls(document.getElementById('controls1'), this.options); - const eventStack = controls.mount(); + const controls = new Controls( + document.getElementById('controls1'), + this.options, + [this.movementCircleCtrl] + ); + const eventStack = controls.mount() + .merge(Rx.Observable.fromEvent(this.movementCircleCtrl, 'change') + .map(evt => ({ key: CONTROLS.MOVEMENT_CIRCLE, value: evt.target.checked })) + ); + eventStack.subscribe(this.subscriber.bind(this)); this.updateAnimating(this.options.animating); this.updateCount(this.options.count); + this.updateMovementCircle(this.options.showMovementCircle); + + // TODO particle starts offscreen sometimes + + // TODO ANIM2 Max out to 1000 or 2000 particles - // TODO Change animal pic OR - use particle ##??? - // TODO show movement circle - // TODO cleanup movement circle - // TODO cleanup vision grid - // TODO add core UI - // TODO regen vision grid is option updated - // TODO expose particle ## - // TODO kill hover on slider, finalize control panel location + // TODO ANIM3 Show vision grid (including touches!) + // TODO ANIM3 regen vision grid is option updated + // TODO ANIM3 cleanup vision grid - // TODO ANIM2 Show vision grid (including touches!) + // TODO ANIM4 Pallet avoidance + + // TODO ANIM5 Flocking }; +function createMovementCircleControl() { + const label = document.createElement('label'); + label.className = 'controls-checkbox'; + + const text = document.createElement('span'); + text.innerHTML = 'Show movement circle'; + text.className = 'controls-checkbox-text'; + + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.className = 'controls-checkbox-input'; + + label.appendChild(checkbox); + label.appendChild(text); + + return label; +} + Animation1.prototype.subscriber = function({ key, value }) { switch(key) { case CONTROLS.ANIMATING: this.updateAnimating(value); break; case CONTROLS.COUNT: this.updateCount(value); break; + case CONTROLS.MOVEMENT_CIRCLE: this.updateMovementCircle(value); break; case CONTROLS.RADIUS: this.updateRadius(value); break; case CONTROLS.ROTATION: this.updateRotation(value); break; case CONTROLS.SPEED: this.updateSpeed(value); break; @@ -75,6 +106,12 @@ Animation1.prototype.updateCount = function(count) { } } +Animation1.prototype.updateMovementCircle = function(showMovementCircle) { + this.options.showMovementCircle = showMovementCircle; + this.movementCircleCtrl.querySelector('[type=checkbox]').checked = showMovementCircle; + this.particles.forEach(p => p.updateOptions({ showMovementCircle })); +} + Animation1.prototype.updateRadius = function(randomizeRadius) { this.options.randomizeRadius = randomizeRadius; this.particles.forEach(p => p.updateOptions({ randomizeRadius })); diff --git a/js/bundle.js b/js/bundle.js index fb5629e..2003824 100644 --- a/js/bundle.js +++ b/js/bundle.js @@ -3464,25 +3464,33 @@ var _enums = __webpack_require__(76); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -function Controls(container, _ref) { +function Controls(container, _ref, customNodes) { var animating = _ref.animating, count = _ref.count, + maxCount = _ref.maxCount, randomizeRadius = _ref.randomizeRadius, randomizeRotation = _ref.randomizeRotation, speed = _ref.speed; this.nodes = { animating: createAnimatingControl(animating), - count: createCountControl(count), + count: createCountControl(count, maxCount), radius: createRadiusControl(randomizeRadius), rotation: createRotationControl(randomizeRotation), speed: createSpeedControl(speed) }; container.appendChild(this.nodes.count); + container.appendChild(this.nodes.speed); container.appendChild(this.nodes.radius); container.appendChild(this.nodes.rotation); - container.appendChild(this.nodes.speed); + + if (customNodes !== undefined) { + customNodes.forEach(function (node) { + container.appendChild(node); + }); + } + container.appendChild(this.nodes.animating); this.updateOptions({ key: _enums.CONTROLS.ANIMATING, value: animating }); @@ -3557,20 +3565,20 @@ var createAnimatingControl = function createAnimatingControl(value) { return label; }; -var createCountControl = function createCountControl(value) { +var createCountControl = function createCountControl(value, max) { var label = document.createElement('label'); - label.className = 'controls-label'; + label.className = 'controls-range'; var text = document.createElement('span'); text.innerHTML = '...'; - text.className = 'controls-label-text'; + text.className = 'controls-range-text'; var slider = document.createElement('input'); slider.type = 'range'; slider.min = 1; - slider.max = 5; + slider.max = max; slider.value = value; - slider.className = 'controls-slider'; + slider.className = 'controls-range-input'; label.appendChild(text); label.appendChild(slider); @@ -3580,15 +3588,15 @@ var createCountControl = function createCountControl(value) { var createRadiusControl = function createRadiusControl(value) { var label = document.createElement('label'); - label.className = 'controls-label'; + label.className = 'controls-checkbox'; var text = document.createElement('span'); text.innerHTML = 'Random radii'; - text.className = 'controls-label-text'; + text.className = 'controls-checkbox-text'; var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; - checkbox.className = 'controls-label-checkbox'; + checkbox.className = 'controls-checkbox-input'; checkbox.checked = value; label.appendChild(checkbox); @@ -3599,15 +3607,15 @@ var createRadiusControl = function createRadiusControl(value) { var createRotationControl = function createRotationControl(value) { var label = document.createElement('label'); - label.className = 'controls-label'; + label.className = 'controls-checkbox'; var text = document.createElement('span'); text.innerHTML = 'Random rotation'; - text.className = 'controls-label-text'; + text.className = 'controls-checkbox-text'; var checkbox = document.createElement('input'); checkbox.type = 'checkbox'; - checkbox.className = 'controls-label-checkbox'; + checkbox.className = 'controls-checkbox-input'; checkbox.checked = value; label.appendChild(checkbox); @@ -3618,17 +3626,17 @@ var createRotationControl = function createRotationControl(value) { var createSpeedControl = function createSpeedControl(value) { var label = document.createElement('label'); - label.className = 'controls-label'; + label.className = 'controls-range'; var text = document.createElement('span'); - text.className = 'controls-label-text'; + text.className = 'controls-range-text'; var slider = document.createElement('input'); slider.type = 'range'; slider.min = 1; - slider.max = 10; + slider.max = 50; slider.value = value; - slider.className = 'controls-slider'; + slider.className = 'controls-range-input'; label.appendChild(text); label.appendChild(slider); @@ -6266,36 +6274,61 @@ function Animation1() { this.options = { animating: false, count: 1, + maxCount: 25, randomizeRadius: true, randomizeRotation: true, - // showMovementCircle: false, - // showVIsionGrid: false, + showMovementCircle: false, + // showVisionGrid: false, speed: 4 }; this.container = document.getElementById('animation1'); this.bounds = this.container.getBoundingClientRect(); + this.movementCircleCtrl = createMovementCircleControl(); this.particles = []; - var controls = new _controls2.default(document.getElementById('controls1'), this.options); - var eventStack = controls.mount(); + var controls = new _controls2.default(document.getElementById('controls1'), this.options, [this.movementCircleCtrl]); + var eventStack = controls.mount().merge(_rxjs2.default.Observable.fromEvent(this.movementCircleCtrl, 'change').map(function (evt) { + return { key: _enums.CONTROLS.MOVEMENT_CIRCLE, value: evt.target.checked }; + })); + eventStack.subscribe(this.subscriber.bind(this)); this.updateAnimating(this.options.animating); this.updateCount(this.options.count); + this.updateMovementCircle(this.options.showMovementCircle); + + // TODO particle starts offscreen sometimes - // TODO Change animal pic OR - use particle ##??? - // TODO show movement circle - // TODO cleanup movement circle - // TODO cleanup vision grid - // TODO add core UI - // TODO regen vision grid is option updated - // TODO expose particle ## - // TODO kill hover on slider, finalize control panel location + // TODO ANIM2 Max out to 1000 or 2000 particles - // TODO ANIM2 Show vision grid (including touches!) + // TODO ANIM3 Show vision grid (including touches!) + // TODO ANIM3 regen vision grid is option updated + // TODO ANIM3 cleanup vision grid + + // TODO ANIM4 Pallet avoidance + + // TODO ANIM5 Flocking }; +function createMovementCircleControl() { + var label = document.createElement('label'); + label.className = 'controls-checkbox'; + + var text = document.createElement('span'); + text.innerHTML = 'Show movement circle'; + text.className = 'controls-checkbox-text'; + + var checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.className = 'controls-checkbox-input'; + + label.appendChild(checkbox); + label.appendChild(text); + + return label; +} + Animation1.prototype.subscriber = function (_ref) { var key = _ref.key, value = _ref.value; @@ -6305,6 +6338,8 @@ Animation1.prototype.subscriber = function (_ref) { this.updateAnimating(value);break; case _enums.CONTROLS.COUNT: this.updateCount(value);break; + case _enums.CONTROLS.MOVEMENT_CIRCLE: + this.updateMovementCircle(value);break; case _enums.CONTROLS.RADIUS: this.updateRadius(value);break; case _enums.CONTROLS.ROTATION: @@ -6346,6 +6381,14 @@ Animation1.prototype.updateCount = function (count) { } }; +Animation1.prototype.updateMovementCircle = function (showMovementCircle) { + this.options.showMovementCircle = showMovementCircle; + this.movementCircleCtrl.querySelector('[type=checkbox]').checked = showMovementCircle; + this.particles.forEach(function (p) { + return p.updateOptions({ showMovementCircle: showMovementCircle }); + }); +}; + Animation1.prototype.updateRadius = function (randomizeRadius) { this.options.randomizeRadius = randomizeRadius; this.particles.forEach(function (p) { @@ -6414,17 +6457,13 @@ var RAD = { var CONTROLS = { ANIMATING: 'animating', COUNT: 'count', + MOVEMENT_CIRCLE: 'movementCircle', RADIUS: 'radius', ROTATION: 'rotation', SPEED: 'speed' }; -var IMAGES = { - SEAHORSE: 'seahorse' -}; - exports.CONTROLS = CONTROLS; -exports.IMAGES = IMAGES; exports.RAD = RAD; /***/ }), @@ -6480,35 +6519,35 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de // import DOM from './dom'; var random = { - bool: function bool() { - return Math.random() < 0.5; + bool: function bool(weight) { + return Math.random() < (weight || 0.5); }, num: function num(min, max) { return min + Math.round(Math.random() * max); + }, + color: function color() { + return 'rgb(' + Math.floor(Math.random() * 255) + ', ' + Math.floor(Math.random() * 255) + ', ' + Math.floor(Math.random() * 255) + ')'; } }; /* ===== Constructor ===== */ function Particle(container, bounds, options) { - var _this = this; + var color = random.color(); this.container = container; this.bounds = bounds; - this.visionGridPoints = calculateVisionGridPoints(); + // this.visionGridPoints = calculateVisionGridPoints(); this.node = document.createElement('div'); this.node.className = 'particle has-vision'; - - this.circle = document.createElement('div'); - this.circle.className = 'particle-movement-circle'; + this.node.style.backgroundColor = color; this.container.appendChild(this.node); - this.container.appendChild(this.circle); - this.visionGridPoints.forEach(function (point) { - _this.container.appendChild(point.div); - }); + // this.visionGridPoints.forEach(point => { + // this.container.appendChild(point.div); + // }); this.options = Object.assign({ randomizeRadius: false, @@ -6527,11 +6566,12 @@ function Particle(container, bounds, options) { this.particle = { clockwise: random.bool(), + color: color, x: 0, y: 0 }; - this.interval = 0; + this.interval = random.num(_enums.RAD.t90, _enums.RAD.t360); this.updateOptions(this.options); this.nextFrame(); @@ -6540,31 +6580,38 @@ function Particle(container, bounds, options) { Particle.prototype.nextFrame = function () { this.move(); repaintParticle(this.arc, this.node, this.particle); + repaintCircle(this.arc, this.circle, this.particle); - if (this.options.showMovementCircle) { - repaintCircle(this.arc, this.circle); - } - - if (this.options.showVisionGrid) { - repaintVisionGrid(this.arc, this.particle, this.visionGridPoints); - } + // if (this.options.showVisionGrid === true) { + // repaintVisionGrid(this.arc, this.particle, this.visionGridPoints); + // } }; Particle.prototype.updateOptions = function (options) { Object.assign(this.options, options); - // this.particleImage = options.image || IMAGES.SEAHORSE; + + if (options.showMovementCircle === true && this.circle === undefined) { + this.circle = document.createElement('div'); + this.circle.className = 'particle-movement-circle'; + this.circle.style.borderColor = this.particle.color; + this.node.appendChild(this.circle); + } + + if (options.showMovementCircle === false && this.circle !== undefined) { + this.circle.parentNode.removeChild(this.circle); + delete this.circle; + } }; Particle.prototype.move = function () { // Randomly change radius and rotation direction. - this.interval -= 1; if (this.interval <= 0) { - this.interval = random.num(50, 100); + this.interval = random.num(_enums.RAD.t90, _enums.RAD.t360); if (this.options.randomizeRadius === true) { this.arc = moveArc(this.arc, random.num(100, 200)); - if (random.bool() && this.options.randomizeRotation === true) { + if (random.bool(0.8) && this.options.randomizeRotation === true) { this.particle.clockwise = !this.particle.clockwise; this.arc = changeDirection(this.arc); } @@ -6573,6 +6620,8 @@ Particle.prototype.move = function () { // Ensure constant velocity and theta between 0 and 2π. var delta = this.options.speed / this.arc.r; + this.interval -= delta; + this.arc.t += this.particle.clockwise ? -delta : +delta; this.arc.t = this.arc.t > 0 ? this.arc.t % _enums.RAD.t360 : _enums.RAD.t360 - this.arc.t; @@ -6668,14 +6717,18 @@ function repaintParticle(arc, node, particle) { node.style.left = particle.x + 'px'; node.style.top = particle.y + 'px'; - node.style.transform = 'rotate(' + rad + 'rad)'; } -function repaintCircle(arc, node) { +function repaintCircle(arc, node, particle) { + if (node === undefined) { + return; + } + node.style.width = 2 * arc.r + 'px'; node.style.height = 2 * arc.r + 'px'; - node.style.left = arc.x - arc.r + 'px'; - node.style.top = arc.y - arc.r + 'px'; + + node.style.left = -particle.x - arc.r + arc.x + 'px'; + node.style.top = -particle.y - arc.r + arc.y + 'px'; node.style.borderRadius = arc.r + 'px'; } diff --git a/js/controls.js b/js/controls.js index 5cebe4a..8607044 100644 --- a/js/controls.js +++ b/js/controls.js @@ -1,19 +1,24 @@ import Rx, { Observable } from 'rxjs'; import { CONTROLS } from './enums'; -function Controls(container, { animating, count, randomizeRadius, randomizeRotation, speed }) { +function Controls(container, { animating, count, maxCount, randomizeRadius, randomizeRotation, speed }, customNodes) { this.nodes = { animating: createAnimatingControl(animating), - count: createCountControl(count), + count: createCountControl(count, maxCount), radius: createRadiusControl(randomizeRadius), rotation: createRotationControl(randomizeRotation), speed: createSpeedControl(speed), } container.appendChild(this.nodes.count); + container.appendChild(this.nodes.speed); container.appendChild(this.nodes.radius); container.appendChild(this.nodes.rotation); - container.appendChild(this.nodes.speed); + + if (customNodes !== undefined) { + customNodes.forEach(node => { container.appendChild(node); }); + } + container.appendChild(this.nodes.animating); this.updateOptions({ key: CONTROLS.ANIMATING, value: animating }); @@ -38,7 +43,6 @@ Controls.prototype.updateOptions = function({ key, value}) { this.nodes.animating.querySelector('span').innerHTML = (value ? '◼ Stop' : '▶ Start'); } - } Controls.prototype.mount = function(customNodes) { @@ -90,20 +94,20 @@ const createAnimatingControl = function(value) { return label; } -const createCountControl = function(value) { +const createCountControl = function(value, max) { const label = document.createElement('label'); - label.className = 'controls-label'; + label.className = 'controls-range'; const text = document.createElement('span'); text.innerHTML = '...'; - text.className = 'controls-label-text'; + text.className = 'controls-range-text'; const slider = document.createElement('input'); slider.type = 'range'; slider.min = 1; - slider.max = 5; + slider.max = max; slider.value = value; - slider.className = 'controls-slider'; + slider.className = 'controls-range-input'; label.appendChild(text); label.appendChild(slider); @@ -113,15 +117,15 @@ const createCountControl = function(value) { const createRadiusControl = function(value) { const label = document.createElement('label'); - label.className = 'controls-label'; + label.className = 'controls-checkbox'; const text = document.createElement('span'); text.innerHTML = 'Random radii'; - text.className = 'controls-label-text'; + text.className = 'controls-checkbox-text'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; - checkbox.className = 'controls-label-checkbox'; + checkbox.className = 'controls-checkbox-input'; checkbox.checked = value; label.appendChild(checkbox); @@ -132,15 +136,15 @@ const createRadiusControl = function(value) { const createRotationControl = function(value) { const label = document.createElement('label'); - label.className = 'controls-label'; + label.className = 'controls-checkbox'; const text = document.createElement('span'); text.innerHTML = 'Random rotation'; - text.className = 'controls-label-text'; + text.className = 'controls-checkbox-text'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; - checkbox.className = 'controls-label-checkbox'; + checkbox.className = 'controls-checkbox-input'; checkbox.checked = value; label.appendChild(checkbox); @@ -151,17 +155,17 @@ const createRotationControl = function(value) { const createSpeedControl = function(value) { const label = document.createElement('label'); - label.className = 'controls-label'; + label.className = 'controls-range'; const text = document.createElement('span'); - text.className = 'controls-label-text'; + text.className = 'controls-range-text'; const slider = document.createElement('input'); slider.type = 'range'; slider.min = 1; - slider.max = 10; + slider.max = 50; slider.value = value; - slider.className = 'controls-slider'; + slider.className = 'controls-range-input'; label.appendChild(text); label.appendChild(slider); diff --git a/js/enums.js b/js/enums.js index 7b2187b..2378c42 100644 --- a/js/enums.js +++ b/js/enums.js @@ -9,13 +9,10 @@ const RAD = { const CONTROLS = { ANIMATING: 'animating', COUNT: 'count', + MOVEMENT_CIRCLE: 'movementCircle', RADIUS: 'radius', ROTATION: 'rotation', SPEED: 'speed' }; -const IMAGES = { - SEAHORSE: 'seahorse' -}; - -export { CONTROLS, IMAGES, RAD }; +export { CONTROLS, RAD }; diff --git a/js/particle.js b/js/particle.js index fb6fc3d..a6c3930 100644 --- a/js/particle.js +++ b/js/particle.js @@ -4,29 +4,29 @@ import { RAD } from './enums'; import Store from './store'; const random = { - bool: () => Math.random() < 0.5, - num: (min, max) => min + Math.round(Math.random() * max) + bool: (weight) => Math.random() < (weight || 0.5), + num: (min, max) => min + Math.round(Math.random() * max), + color: () => `rgb(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)})` } /* ===== Constructor ===== */ function Particle(container, bounds, options) { + const color = random.color(); + this.container = container; this.bounds = bounds; - this.visionGridPoints = calculateVisionGridPoints(); + // this.visionGridPoints = calculateVisionGridPoints(); this.node = document.createElement('div'); this.node.className = 'particle has-vision'; - - this.circle = document.createElement('div'); - this.circle.className = 'particle-movement-circle'; + this.node.style.backgroundColor = color; this.container.appendChild(this.node); - this.container.appendChild(this.circle); - this.visionGridPoints.forEach(point => { - this.container.appendChild(point.div); - }); + // this.visionGridPoints.forEach(point => { + // this.container.appendChild(point.div); + // }); this.options = Object.assign({ randomizeRadius: false, @@ -46,11 +46,12 @@ function Particle(container, bounds, options) { this.particle = { clockwise: random.bool(), + color, x: 0, y: 0 } - this.interval = 0; + this.interval = random.num(RAD.t90, RAD.t360); this.updateOptions(this.options); this.nextFrame(); @@ -59,31 +60,38 @@ function Particle(container, bounds, options) { Particle.prototype.nextFrame = function() { this.move(); repaintParticle(this.arc, this.node, this.particle); + repaintCircle(this.arc, this.circle, this.particle); - if (this.options.showMovementCircle) { - repaintCircle(this.arc, this.circle); - } - - if (this.options.showVisionGrid) { - repaintVisionGrid(this.arc, this.particle, this.visionGridPoints); - } + // if (this.options.showVisionGrid === true) { + // repaintVisionGrid(this.arc, this.particle, this.visionGridPoints); + // } } Particle.prototype.updateOptions = function(options) { Object.assign(this.options, options); - // this.particleImage = options.image || IMAGES.SEAHORSE; + + if (options.showMovementCircle === true && this.circle === undefined) { + this.circle = document.createElement('div'); + this.circle.className = 'particle-movement-circle'; + this.circle.style.borderColor = this.particle.color; + this.node.appendChild(this.circle); + } + + if (options.showMovementCircle === false && this.circle !== undefined) { + this.circle.parentNode.removeChild(this.circle); + delete this.circle; + } } Particle.prototype.move = function() { // Randomly change radius and rotation direction. - this.interval -= 1; if (this.interval <= 0) { - this.interval = random.num(50, 100); + this.interval = random.num(RAD.t90, RAD.t360); if (this.options.randomizeRadius === true) { this.arc = moveArc(this.arc, random.num(100, 200)); - if (random.bool() && this.options.randomizeRotation === true) { + if (random.bool(0.8) && this.options.randomizeRotation === true) { this.particle.clockwise = !this.particle.clockwise; this.arc = changeDirection(this.arc); } @@ -92,6 +100,8 @@ Particle.prototype.move = function() { // Ensure constant velocity and theta between 0 and 2π. const delta = this.options.speed / this.arc.r; + this.interval -= delta; + this.arc.t += (this.particle.clockwise ? -delta : +delta); this.arc.t = (this.arc.t > 0 ? this.arc.t % RAD.t360 : RAD.t360 - this.arc.t); @@ -189,14 +199,18 @@ function repaintParticle(arc, node, particle) { node.style.left = `${particle.x}px`; node.style.top = `${particle.y}px`; - node.style.transform = `rotate(${rad}rad)`; } -function repaintCircle(arc, node) { +function repaintCircle(arc, node, particle) { + if (node === undefined) { + return; + } + node.style.width = `${2 * arc.r}px`; node.style.height = `${2 * arc.r}px`; - node.style.left = `${arc.x - arc.r}px`; - node.style.top = `${arc.y - arc.r}px`; + + node.style.left = `${-particle.x - arc.r + arc.x}px`; + node.style.top = `${-particle.y - arc.r + arc.y}px`; node.style.borderRadius = `${arc.r}px`; }