Skip to content

Instantly share code, notes, and snippets.

@maritrinez
Created October 1, 2018 15:20
Show Gist options
  • Save maritrinez/33daa75473f4cad6cfabe4eee6285099 to your computer and use it in GitHub Desktop.
Save maritrinez/33daa75473f4cad6cfabe4eee6285099 to your computer and use it in GitHub Desktop.
Canvas points bounded inisde / outside a circle
license: gpl-3.0

Canvas points bounded inisde / outside a circle

Based on Mike Bostock Circles. Canvas transition taken from this Bocoup's article

<!DOCTYPE html>
<meta charset="utf-8">
<style>
button {
font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
font-size: 18px;
color: black;
pointer-events: all;
}
.disabled {
color: grey;
pointer-events: none;
}
</style>
<body>
<div>
<button class="restart disabled">RESTART</button>
</div>
<script src="//d3js.org/d3.v5.min.js"></script>
<script>
// - - - - - constant values - - - - - //
const FILLIN = 'rgba(255, 3, 5, 0.8)',
FILLOUT = 'rgba(5, 3, 5, 0.5)',
STROKE = 'rgba(5, 5, 5, 0.8)',
SIZE = {w: 960, h: 400},
RADIUS = {point: {min: 1, max: 3}, circle: 120},
VELOCITY = {min: 0.15, dev: 0.15},
DENSITY = 0.0025, // particles per sq px;
DURATION = 750;
// - - - - - canvas - - - - - //
const canvas = d3.select('body').append('canvas')
.attr('width', SIZE.w + 'px')
.attr('height', SIZE.h + 'px');
const context = canvas.node().getContext('2d');
// - - - - - random circle & points - - - - - //
const circle = {
r: RADIUS.circle,
x: Math.round(Math.random() * (SIZE.w - 2 * RADIUS.circle) + RADIUS.circle),
y: Math.round(Math.random() * (SIZE.h - 2 * RADIUS.circle) + RADIUS.circle)
};
const N = Math.round(SIZE.w * SIZE.h * DENSITY);
const points = d3.range(N).map(function(d) {
var point = {};
point.r = Math.round(Math.random() * (RADIUS.point.max - RADIUS.point.min) + RADIUS.point.min),
point.x = Math.round(Math.random() * SIZE.w),
point.y = Math.round(Math.random() * SIZE.h),
point.dx = (Math.random() * VELOCITY.dev + VELOCITY.min) * (Math.round(Math.random()) * 2 - 1),
point.dy = (Math.random() * VELOCITY.dev + VELOCITY.min) * (Math.round(Math.random()) * 2 - 1),
point.inside = pointInCircle(point);
point.fill = !point.inside ? FILLOUT : FILLIN;
return point;
});
// - - - - - trigger animation - - - - - //
var timer = d3.timer(floating);
// - - - - - restart - - - - - //
d3.select('.restart').on('click', () => {
d3.select('.restart').classed('disabled', true);
timer.restart(floating)
})
// - - - - - functions - - - - - //
function floating(elapsed) {
if (elapsed > 3000) {
timer.stop();
d3.select('.restart').classed('disabled', false);
return;
}
points.forEach(d => {
if(!d.inside) {
d.x += d.dx; if (d.x > SIZE.w || d.x < 0 || pointInCircle(d)) d.dx *= -1;
d.y += d.dy; if (d.y > SIZE.h || d.y < 0 || pointInCircle(d)) d.dy *= -1;
} else {
d.x += d.dx; if (!pointInCircle(d)) d.dx *= -1;
d.y += d.dy; if (!pointInCircle(d)) d.dy *= -1;
}
});
draw();
}
function pointInCircle(point) {
return _dist() <= circle.r ? true : false;
function _dist() {
let dist = Math.sqrt(_diff('x') * _diff('x') + _diff('y') * _diff('y'));
switch (point.inside) {
case false:
return dist - point.r;
break;
case true:
return dist + point.r;
break;
default:
// update position of nodes that fall IN the circumference
if (dist > circle.r && dist - point.r < circle.r) {
point.x = point.x < circle.x ? point.x - point.r : point.x + point.r;
point.y = point.y < circle.y ? point.y - point.r : point.y + point.r;
} else if (dist <= circle.r && dist + point.r >= circle.r) {
point.x = point.x < circle.x ? point.x + point.r : point.x - point.r;
point.y = point.y < circle.y ? point.y + point.r : point.y - point.r;
}
return dist ;
}
}
function _diff(c) {
return circle[c] - point[c];
}
}
function draw() {
context.clearRect(0, 0, SIZE.w, SIZE.h)
// points
for (let i = 0; i < points.length; ++i) {
context.beginPath();
context.fillStyle = points[i].fill;
context.arc(points[i].x, points[i].y, points[i].r, 0, 2 * Math.PI);
context.fill();
}
// circle
context.beginPath();
context.fillStyle = 'rgba(0, 0, 0, 0)';
context.strokeStyle = STROKE;
context.arc(circle.x, circle.y, circle.r, 0, 2 * Math.PI);
context.fill();
context.stroke();
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment