{
const width = 600,
height = 200,
particles = [],
repeller = {
location: cm.vec(width / 2, height - 50),
power: 150
},
applyRepeller = createRepel(repeller);
function update(app) {
app.append(cm.clear, { fill: cm.rgb(255) });
app.datum(repeller).append(cm.circle, {
x: (d) => d.location.x,
y: (d) => d.location.y,
r: 16,
stroke: "black",
fill: cm.rgb(127)
});
app
.data(particles)
.process(
cm.push,
object({
location: cm.vec(app.prop("width") / 2, 50),
velocity: cm.vec(cm.random(-1, 1), cm.random(-2, 0)),
lifespan: 255
})
)
.process(cm.eachRight, fadeOut)
.process(cm.each, applyRepeller)
.process(cm.each, applyGravity)
.process(cm.each, move)
.append(cm.circle, {
x: (d) => d.location.x,
y: (d) => d.location.y,
r: 5,
fill: cm.rgb(0),
stroke: cm.rgb(0),
fillOpacity: (d) => d.lifespan,
strokeOpacity: (d) => d.lifespan
})
.transform(cm.mapAttrs, {
fillOpacity: { domain: [0, 255], range: [0, 0.6] },
strokeOpacity: { domain: [0, 255], range: [0, 1] }
});
}
function dispose(app) {
invalidation.then(() => app.dispose());
}
return cm
.app({ width, height })
.on("update", update)
.call(dispose)
.start()
.node();
}