<canvas> mouse trail
I came across an interesting interaction that reminded me of the older web the other day. As a user
moved their mouse cursor around, there would be a trail of something that followed it. I was intrigued
and wanted to see how/what it worked, so I opened up the dev console for the website
(Synchronized Digital Studio) and after poking around a bit, I realized that it wasn't
just a plain old <div>
that had some special handling. It was <canvas>
, which I had never worked
with before.
Using a combination of <canvas>
and requestAnimationFrame()
, I was able to recreate the interaction.
The idea is to attach an event listener for mouse movement to add a new Point
to an array that
holds the position of the user's movement.
const canvas = document.querySelector('canvas')const ctx = canvas.getContext('2d')class Point {constructor(x, y) {this.x = xthis.y = ythis.timestamp = 0}}const points = []const addPoint = (x, y) => {const point = new Point(x, y)points.push(point)}document.addEventListener('mousemove', ({ clientX, clientY }) => {addPoint(clientX - canvas.offsetLeft, clientY - canvas.offsetTop)})
Next, using requestAnimationFrame
, on each frame, clear the canvas, loop over each
point
and either
- Remove the point if enough time has passed (
maxAgeMS
) - Draw the point and connect it to the previous
point
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)const maxAgeMS = 160for (let i = 0; i < points.length; i++) {const currentPoint = points[i]let previousPointif (points[i - 1] !== undefined) {previousPoint = points[i - 1]} else {previousPoint = currentPoint}if (!currentPoint.timestamp) {currentPoint.timestamp = timestamp}const elapsed = timestamp - currentPoint.timestampif (elapsed > maxAgeMS) {points.shift()} else {ctx.lineJoin = 'round'ctx.lineWidth = 4ctx.strokeStyle = `rgb(152,236,60)`ctx.beginPath()ctx.moveTo(previousPoint.x, previousPoint.y)ctx.lineTo(currentPoint.x, currentPoint.y)ctx.stroke()ctx.closePath()}}
Rinse and repeat for each frame, and this is what I ended up with: