Skip to content

Instantly share code, notes, and snippets.

@ajmers
Created March 31, 2015 16:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ajmers/3180bd24b2f8016f40d5 to your computer and use it in GitHub Desktop.
Save ajmers/3180bd24b2f8016f40d5 to your computer and use it in GitHub Desktop.
(function() {
console.log('gravity');
var triggerGrandparents = [];
var z = 1000;
var d = document;
var origCopies = [];
var fallCopies = [];
// var logo = document.getElementById('monetate-logo');
var buffer = 0;
var balls = {};
var Cd = 0.47; // Dimensionless
var rho = 1.22; // kg / m^3
var ag = 9.81; // m / s^
var frameRate = 1 / 40; // Seconds
var frameDelay = frameRate * 1000; // ms
var winHeight = window.innerHeight;
var winWidth = window.innerWidth;
function Ball(node) {
this.node = node;
this.velocity = {
x: 0,
y: 0
};
this.position = {
x: 0,
y: 0
};
this.radius = this.node.offsetHeight;
this.mass = this.node.offsetHeight * this.node.offsetWidth * 0.1;
this.restitution = -0.7;
this.reset = function() {
this.velocity = {
x: 0,
y: 0
};
}
}
function makePlaceholder(origNode) {
if (!(/monetate_placeholder/.exec(origNode.className))) {
// Clone found node, add classes to both, hide original.
var clonedNode = origNode.cloneNode(true);
origNode.className += ' monetate_placeholder_hide';
clonedNode.className += ' monetate_placeholder_dup';
clonedNode.style.cssText = 'display: inline-block; position: absolute !important; left: 0px; top: 0px';
origNode.style.visibility = 'hidden';
// Add a wrapper node as previous sibling to found node.
var wrapper = document.createElement('div');
wrapper.className = 'monetate_placeholder_wrap';
origNode.parentNode.insertBefore(wrapper, origNode);
// Add hidden original node and new cloned version to the wrapper.
wrapper.appendChild(origNode);
wrapper.appendChild(clonedNode);
// Add cloned node to the array to be returned.
var ball = new Ball(clonedNode);
fallCopies.push(ball);
} else {
var ball = balls[origNode];
}
return ball;
}
(function setStyles() {
var elementStyles = '.monetate_placeholder_wrap { ' +
'display: inline-block; width: auto !important; margin: 0 !important; ' +
'padding: 0 !important; position: relative; }' +
'.monetate_placeholder_hide {visibility: hidden}';
var head = document.head;
var e = document.createElement('style');
e.setAttribute('type', 'text/css');
if (e.styleSheet) { //IE
e.styleSheet.cssText = elementStyles;
} else { //EVERYONE ELSE
var stl = document.createTextNode(elementStyles);
e.appendChild(stl);
}
head.appendChild(e);
})();
function debounce(func, wait, opt_immediate) {
var timeout, result;
return function() {
var context = this;
var args = arguments;
var later = function() {
timeout = null;
if (!opt_immediate) {
result = func.apply(context, args);
}
};
var callNow = opt_immediate && !timeout;
window.clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
}
return result;
};
};
function fall(ball) {
ball.doneFalling = false;
ball.reset();
window.requestAnimationFrame(function innerLoop() {
loop(ball);
if (!ball.doneFalling) {
window.requestAnimationFrame(innerLoop);
}
});
}
var debouncedFall = debounce(fall, 600);
function loop(ball) {
var box = ball.node.getBoundingClientRect();
var A = Math.PI * ball.radius * ball.radius / (10000); // m^2
// Do physics
// Drag force: Fd = -1/2 * Cd * A * rho * v * v
// var Fx = -0.5 * Cd * A * rho * ball.velocity.x * ball.velocity.x * ball.velocity.x / Math.abs(ball.velocity.x);
var Fy = -0.5 * Cd * A * rho * ball.velocity.y * ball.velocity.y * ball.velocity.y / Math.abs(ball.velocity.y);
// Fx = (isNaN(Fx) ? 0 : Fx);
Fy = (isNaN(Fy) ? 0 : Fy);
if (box.bottom > window.innerHeight && Math.abs(Fy) < .01) {
ball.doneFalling = true;
}
// Calculate acceleration ( F = ma )
// var ax = Fx / ball.mass;
var ay = ag + (Fy / ball.mass);
// Integrate to get velocity
// ball.velocity.x += ax*frameRate;
ball.velocity.y += ay * frameRate;
// Integrate to get position
// ball.position.x += ball.velocity.x*frameRate*100;
ball.position.y += ball.velocity.y * frameRate * 100;
ball.node.style.top = ball.position.y + 'px';
// Handle collisions
if (box.bottom > window.innerHeight && ball.velocity.y > 0) {
ball.velocity.y *= ball.restitution;
}
}
function elementInViewport(el) {
var top = el.offsetTop;
var height = el.offsetHeight;
while (el.offsetParent) {
el = el.offsetParent;
top += el.offsetTop;
}
return (
top >= window.pageYOffset &&
(top + height) <= (window.pageYOffset + window.innerHeight) && top
);
}
function elementIsntHuge(el) {
var width = el.offsetWidth;
var height = el.offsetHeight;
return width / winWidth < 0.9 && height / winHeight < 0.9;
}
function init() {
document.body.addEventListener('mouseover', function(ev) {
var node = ev.target;
if (node.tagName === 'AREA') {
while (node.parentNode) {
for (var t = 0, l = triggerGrandparents.length; t < l; t++) {
if (node === triggerGrandparents[t]) {
return;
}
}
node = node.parentNode;
}
}
ev.preventDefault();
ev.stopPropagation();
if (elementIsntHuge(node)) {
var ball = makePlaceholder(node);
if (elementInViewport(ball.node)) {
fall(ball);
}
balls[ball.node] = ball;
}
});
window.addEventListener('scroll', function() {
for (var key in balls) {
if (balls.hasOwnProperty(key)) {
var ball = balls[key];
if (elementInViewport(ball.node)) {
debouncedFall(ball);
}
}
}
});
}
var clickzones = d.getElementsByTagName('area');
for (var i = 0, l = clickzones.length; i < l; i++) {
var href = clickzones[i].getAttribute('href');
if (href === '#falling') {
var banner = clickzones[i].parentNode && clickzones[i].parentNode.parentNode;
if (banner) {
triggerGrandparents.push(banner);
}
clickzones[i].onclick = init;
}
}
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment