Skip to content

Instantly share code, notes, and snippets.

@joshblack
Created June 24, 2015 05:02
Show Gist options
  • Save joshblack/60680adfd0b9eabb076d to your computer and use it in GitHub Desktop.
Save joshblack/60680adfd0b9eabb076d 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 projectile(start) {
const node = insert(particle(), start);
const gravity = [0, 1];
let velocity = vector(100, rad(-75));
return function loop() {
const resultant = add(velocity, gravity),
pos = position(node),
newPos = add(pos, resultant);
// Let's prevent the particle from going out of
// the screen
if (newPos[1] > innerHeight - 50) {
newPos[1] = innerHeight - 50;
}
interpolate(pos, newPos)(node);
velocity = resultant;
if (newPos[1] < innerHeight - 50) {
requestAnimationFrame(loop);
}
}
}
function interpolate(a, b) {
let alpha = 0;
return function loop(node) {
if (alpha < 1) {
place(node, intermediate(alpha, a, b));
alpha += 0.1;
requestAnimationFrame(loop.bind(null, node));
}
}
}
function intermediate(alpha, a, b) {
return add(scale(a, 1 - alpha), scale(b, alpha));
}
function scale(vector, scalar) {
return vector.map((e) => e * scalar);
}
projectile([0, innerHeight - 50])();
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%';
node.style.transformOrigin = 'center center';
return node;
}
// insert(node, position: vector) -> DOM node
function insert(node, position) {
const { translate } = transform;
node.style.transform =
translate(...position.map(px));
return document.body.appendChild(node);
}
function place(node, position) {
const { translate } = transform;
node.style.transform =
translate(...position.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];
}

Overview

Extremely fun and rewarding final product! Ran into a major problem when I realized that generating a resultant vector (from adding velocity and gravity together) would make the particle jump from one position to the next instead of animating between them. I decided to revisit interpolation to see if this was finally a good use case for it and it was! I ended up using a formula for LERP that is (1 - alpha)a + alpha * b where a is the vector we are interpolating from and b is the vector we are interpolating to. We can use this as a useful way to interpolate between values by changing the value of alpha in incremental steps from 0 - 1, where an alpha value of 0 is the a vector and an alpha value of 1 is the b vector. By setting alpha to intermediate values we can make a seemingly smooth transition between two vectors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment