Skip to content

Instantly share code, notes, and snippets.

@artpi
Last active April 28, 2022 01:19
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save artpi/1eed7ac11eb980d9f7837041d054ef3f to your computer and use it in GitHub Desktop.
Save artpi/1eed7ac11eb980d9f7837041d054ef3f to your computer and use it in GitHub Desktop.
This will animate an "Explosion" whenever you complete a TODO in Roam Research, so you can be productive in style.
// This will animate an "Explosion" whenever you complete a TODO in Roam Research.
// See https://deliber.at/roam for more Roam goodies.
// Put this file under {{[[roam/js]]}} block. I have a page roam/js. Under that, I have {{[[roam/js]]}} and under that the javascript block
document.addEventListener("click", function ( e ) {
const target = e.target;
if (
target.tagName === "INPUT" &&
target.parentElement.className === "check-container"
) {
if (target.type === "checkbox" && target.checked ) {
const position = target.getBoundingClientRect();
setTimeout(() => {
explode( position.x, position.y );
}, 50);
}
}
});
// The animation code below is adapted from https://codepen.io/explosion/pen/zKEovE
const requestedFrames = [];
const explode = (x, y) => {
const colors = [ '#ffc000', '#ff3b3b', '#ff8400' ];
const bubbles = 25;
let particles = [];
let ratio = window.devicePixelRatio;
let c = document.createElement('canvas');
let ctx = c.getContext('2d');
c.style.position = 'absolute';
c.style.left = (x - 100) + 'px';
c.style.top = (y - 100) + 'px';
c.style.pointerEvents = 'none';
c.style.width = 200 + 'px';
c.style.height = 200 + 'px';
c.style.zIndex = 10000;
c.width = 200 * ratio;
c.height = 200 * ratio;
document.body.appendChild(c);
for(var i = 0; i < bubbles; i++) {
particles.push({
x: c.width / 2,
y: c.height / 2,
radius: r(20, 30),
color: colors[Math.floor(Math.random() * colors.length)],
rotation: r(0, 360, true),
speed: r(8, 12),
friction: 0.9,
opacity: r(0, 0.5, true),
yVel: 0,
gravity: 0.1
});
}
render(particles, ctx, c.width, c.height);
setTimeout(() => {
document.body.removeChild(c);
requestedFrames.forEach( frame => cancelAnimationFrame( frame ) );
}, 1000);
}
const render = (particles, ctx, width, height) => {
requestedFrames.push( requestAnimationFrame(() => render(particles, ctx, width, height)) );
ctx.clearRect(0, 0, width, height);
particles.forEach((p, i) => {
p.x += p.speed * Math.cos(p.rotation * Math.PI / 180);
p.y += p.speed * Math.sin(p.rotation * Math.PI / 180);
p.opacity -= 0.01;
p.speed *= p.friction;
p.radius *= p.friction;
p.yVel += p.gravity;
p.y += p.yVel;
if(p.opacity < 0 || p.radius < 0) return;
ctx.beginPath();
ctx.globalAlpha = p.opacity;
ctx.fillStyle = p.color;
ctx.arc(p.x, p.y, p.radius, 0, 2 * Math.PI, false);
ctx.fill();
});
return ctx;
}
const r = (a, b, c) => parseFloat((Math.random() * ((a ? a : 1) - (b ? b : 0)) + (b ? b : 0)).toFixed(c ? c : 0));
@dvargas92495
Copy link

Hey @artpi, how would you feel if I merged this in with the todo-trigger extension?

@artpi
Copy link
Author

artpi commented Apr 26, 2022

@dvargas92495 , yes, please do! I find it impossible to maintain this and I bet it will be easier to keep working if part of the larger project

@dvargas92495
Copy link

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