Skip to content

Instantly share code, notes, and snippets.

@joshblack
Created June 24, 2015 02:32
Show Gist options
  • Save joshblack/3848edec17caed90be50 to your computer and use it in GitHub Desktop.
Save joshblack/3848edec17caed90be50 to your computer and use it in GitHub Desktop.
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)();

Arc

Overview

This demo was pretty interesting, ran into a lot of little snags along that way that dealt with figuring out how to do all the physics stuff that I've learned in the past with vectors in JavaScript. Some of the problems I encountered were:

  • Not realizing that Math functions take in radian units, so I had to convert them
  • Not realizing that the axis of the DOM is actually a flipped representation of what I think of (in this case Y is positive going down the page, and negative going up)
  • Realized that I need a kind of place or put function that places a node at a specific location if I give it an [x, y] vector so that it can translate the node
  • Remember that you have to offset a lot of these motion functions by half the width and height of the object, might also be able to fix by setting a transform origin on the DOM node. (Nope nevermind, need offset for nodes to appear off screen, might not be worth it though honestly).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment