Skip to content

Instantly share code, notes, and snippets.

@hagb4rd
Created January 12, 2019 04:30
Show Gist options
  • Save hagb4rd/af62b841511fd1caa844e2439ebbe9e8 to your computer and use it in GitHub Desktop.
Save hagb4rd/af62b841511fd1caa844e2439ebbe9e8 to your computer and use it in GitHub Desktop.
canvas dwitter background animation - https://hypnotic-hip.glitch.me
<!DOCTYPE html>
<head>
<link id="favicon" rel="icon" href="https://glitch.com/edit/favicon-app.ico" type="image/x-icon">
<meta charset="utf-8">
<link rel="stylesheet" href="/style.css">
<style>
:root {
background: black;
overflow: hidden;
}
body {
margin: 0;
}
<style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
'use strict';
const TAU = Math.PI * 2;
const width = window.innerWidth;
const height = window.innerHeight;
const context = canvas.getContext('2d');
const circles = Array(10 + random(40) | 0).fill(0).map(_ => createCircle());
function createCircle() {
const speed = 50 + random(100);
return {
x: random(width),
y: random(height),
dx: deviate(speed),
dy: deviate(speed),
radius: 50 + random(100),
intersectionAngles: [],
};
}
function random(x) {
return Math.random() * x;
}
function deviate(x) {
return Math.random() * 2 * x - x;
}
function range(n) {
const result = [];
for (let i = 0; i < n; ++i)
result.push(i);
return result;
}
function intersectionPoints({x:aX, y:aY, radius:aRadius}, {x:bX, y:bY, radius:bRadius}) {
const dx = bX - aX;
const dy = bY - aY;
const distanceSquared = dx**2 + dy**2;
if (distanceSquared >= (aRadius + bRadius)**2)
return [];
if (distanceSquared <= (aRadius - bRadius)**2)
return [];
// Solve: (x - aX)**2 + (y - aY)**2 == aRadius**2 && (x - bX)**2 + (y - bY)**2 == bRadius**2
// Align plane to have circle A at the origin and rotate it such that circle B is along the positive x axis.
// aRadius**2 - x**2 == bRadius**2 - (x - bX)**2 == bRadius**2 - x**2 + 2 * x * bX - bX**2
// aRadius**2 - bRadius**2 == 2 * x * bX - bX**2
// x == (aRadius**2 - bRadius**2 + bX**2) / (2 * bX)
const distance = Math.sqrt(distanceSquared);
const midDistance = (aRadius**2 - bRadius**2 + distanceSquared) / (2 * distance)
const perpDistance = Math.sqrt(aRadius**2 - midDistance**2);
return [{
x: aX + (dx * midDistance - dy * perpDistance) / distance,
y: aY + (dy * midDistance + dx * perpDistance) / distance,
}, {
x: aX + (dx * midDistance + dy * perpDistance) / distance,
y: aY + (dy * midDistance - dx * perpDistance) / distance,
}];
}
function circleAngle({x:circleX, y:circleY}, {x, y}) {
return Math.atan2(y - circleY, x - circleX);
}
function init() {
canvas.width = width;
canvas.height = height;
}
function update(timeDelta) {
for (const circle of circles) {
circle.x += circle.dx * timeDelta;
circle.y += circle.dy * timeDelta;
const x = circle.x;
const y = circle.y;
if (circle.x < 0)
circle.x *= -1;
if (circle.y < 0)
circle.y *= -1;
if (circle.x > width)
circle.x -= circle.x - width;
if (circle.y > height)
circle.y -= circle.y - height;
if (circle.x != x)
circle.dx *= -1;
if (circle.y != y)
circle.dy *= -1;
circle.intersectionAngles.length = 0;
}
for (const i of range(circles.length)) {
for (let j = i + 1; j < circles.length; ++j) {
const circleA = circles[i];
const circleB = circles[j];
for (const point of intersectionPoints(circleA, circleB)) {
circleA.intersectionAngles.push(circleAngle(circleA, point));
circleB.intersectionAngles.push(circleAngle(circleB, point));
}
}
}
}
function draw() {
context.clearRect(0, 0, width, height);
context.lineWidth = 2;
context.strokeStyle = 'blue';
context.beginPath();
for (const {x, y, radius} of circles) {
context.moveTo(x + radius, y);
context.arc(x, y, radius, 0, TAU, true);
}
context.stroke();
context.fillStyle = 'white';
context.beginPath();
for (const {x, y, radius, intersectionAngles} of circles) {
for (const angle of intersectionAngles) {
const intersectionX = x + radius * Math.cos(angle);
const intersectionY = y + radius * Math.sin(angle);
context.moveTo(intersectionX + 4, intersectionY);
context.arc(intersectionX, intersectionY, 4, 0, TAU, true);
}
}
context.fill();
}
function eachFrame(f) {
let previousTime = null;
function frame(time) {
const timeDelta = previousTime == null ? 0 : time - previousTime;
f(timeDelta / 1000);
previousTime = time;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
function main() {
init();
eachFrame(timeDelta => {
update(timeDelta);
draw();
});
}
main();
</script>
</body>
</html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment