|
const transform = { |
|
translate(x, y) { |
|
return transformFunction('translate', x, y); |
|
} |
|
} |
|
|
|
function transformFunction(property, ...values) { |
|
return `${property}(${values.join(', ')})`; |
|
} |
|
|
|
function px(unit) { |
|
return `${unit}px`; |
|
} |
|
|
|
function add(a, b) { |
|
const result = Array(a.length).fill(0); |
|
|
|
for (let i = 0; i < a.length; i++) { |
|
result[i] = a[i] + b[i]; |
|
} |
|
|
|
return result; |
|
} |
|
|
|
// particle |
|
|
|
// particle() -> DOM node |
|
function particle() { |
|
const node = document.createElement('div'); |
|
|
|
node.style.background = 'black'; |
|
node.style.position = 'absolute'; |
|
node.style.width = px(50); |
|
node.style.height = px(50); |
|
node.style.borderRadius = '50%'; |
|
|
|
return node; |
|
} |
|
|
|
// insert(node, position: vector) -> DOM node |
|
function insert(node, position) { |
|
const { translate } = transform; |
|
|
|
node.style.transform = |
|
translate(...position.map((p) => p - 25).map(px)); |
|
|
|
return document.body.appendChild(node); |
|
} |
|
|
|
function place(node, position) { |
|
const { translate } = transform; |
|
|
|
node.style.transform = |
|
translate(...position.map((p) => p - 25).map(px)); |
|
|
|
return node; |
|
} |
|
|
|
// position(node) -> vector |
|
function position(node) { |
|
const translate = node.style.transform, |
|
p = translate.slice(10, translate.length - 1); |
|
|
|
return p.split(',').map((s) => parseInt(s.trim(), 10)); |
|
} |
|
|
|
// remove(node) -> node |
|
function remove(node) { |
|
return node.parentNode.removeChild(node); |
|
} |
|
|
|
// shift(node, to: vector) -> node |
|
function shift(node, to) { |
|
const { translate } = transform; |
|
|
|
node.style.transform = translate(...to.map(px)); |
|
|
|
return node; |
|
} |
|
|
|
// move(node, velocity: vector) -> null |
|
function move(node, velocity) { |
|
const shifted = shift(node, add(position(node), velocity)); |
|
|
|
requestAnimationFrame( |
|
move.bind(null, shifted, velocity)); |
|
} |
|
|
|
function speed([a, b]) { |
|
const { pow, sqrt } = Math; |
|
|
|
return sqrt(pow(a, 2) + pow(b, 2)); |
|
} |
|
|
|
function direction([a, b]) { |
|
return deg(Math.atan2(a, b)); |
|
} |
|
|
|
function deg(radian) { |
|
return radian * (180 / Math.PI); |
|
} |
|
|
|
function rad(degree) { |
|
return degree * (Math.PI / 180); |
|
} |
|
|
|
// magnitude: scalar, direction: rad -> vector |
|
function vector(magnitude, direction) { |
|
const a = Math.cos(direction) * magnitude, |
|
b = Math.sin(direction) * magnitude; |
|
|
|
return [a, b]; |
|
} |
|
|
|
function arc(magnitude) { |
|
const node = insert(particle(), vector(magnitude, rad(0))); |
|
let direction = 0, increasing = true; |
|
|
|
return function loop() { |
|
place(node, vector(magnitude, rad(direction))); |
|
|
|
if (direction === 0 && !increasing) { |
|
increasing = true; |
|
} |
|
else if (direction === 90) { |
|
increasing = false; |
|
} |
|
|
|
direction = increasing ? direction + 1 : direction - 1; |
|
|
|
requestAnimationFrame(loop); |
|
} |
|
} |
|
|
|
arc(50)(); |
|
arc(150)(); |
|
arc(250)(); |
|
arc(350)(); |
|
arc(450)(); |
|
arc(550)(); |
|
arc(650)(); |