A cursor trailing effect library for this Christmas
Tags: javascript, webdev, canvas
Published: 2021-12-25 00:22:54

We did a cursor trailing effect on our website. Learned how to do holiday effects the right way, experimented with Canvas and Transition, built a NPM library cursor-effect. 
Here's our story...

# How it started

I saw an ancient HTML spell the other day:

```html
<MARQUEE><BLINK>
  How would you suppose this element behave?
</BLINK></MARQUEE>
```

Surprised by the fact it still works


![Good job safari...](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kpqjxukk12qmuh1dp94h.png)
<figcaption>Good job safari</figcaption>

It reminds me of the old days where cursor trail is a sexy effect. So I went googling but most result are telling you how to set cursor trail on Windows

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8uw3bgu23bvzisg07k2.jpeg)

Luckily there's [Cursor Effects (tholman.com)](https://tholman.com/cursor-effects/) , which seem to be used on StackOverflow before. So we implemented the same effect on [our site: Yourator](https://yourator.co), with some customization tweaks. This post is what we've learned from implementing this effect, and we also published the effect as an npm library: 

[Cursor Trails: https://github.com/yourator/cursor-trails](https://github.com/yourator/cursor-trails)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4fh83ktiufv9700k3ybx.gif)
<figcaption>the effect is up on production site til this weekend</figcaption>

# Learn from the original cursor-effect library

src: [https://github.com/tholman/cursor-effects/blob/master/src/snowflakeCursor.js](https://github.com/tholman/cursor-effects/blob/master/src/snowflakeCursor.js)



![init](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b3e15679wz06ichbnrsr.png)
<figcaption>skeleton of init method</figcaption>

We only needed snowflake effect, so this is what we'll be talking about. The main flow of starts from `init`, which contains the basic working flow of this effect:

1. Initialize environment (canvas) for snowflakes
2. Draw emoji character (possibleEmoji) on canvas
3. `bindEvents` listens for mouse & touch event 
4. `loop` updates snowflake continuously 

## `bindEvents` listens for mouse & touch event 

![bindEvents](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yhrmza4tayvzq4v0smqx.png)


We're doing the same thing in `onMouseMove` and `onTouchMove` : call `addParticle` upon the event and create a snowflake at where cursor is.

`onWindowResize` is responsible for adjusting canvas size.

```
Why not use CSS width: 100%, height: 100% ?

Since canvas is a canvas with assigned resolution, CSS can only adjust its visual size. If you create a canvas with 100px x 100px and stretch it to 200px x 1000px, then you'll have a 100px x 100px canvas (and pixels in it) which it stretched 2x wide and 10x long. So we need to adjust size of canvas according to window size.
```

![loop](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ous8ix16i8oi3f9scfks.png)

The last line of `init` was a `loop()` call, to create an infinite loop using requestAnimationFrame. This loop is responsible to update snowflake position and behaviors, it will call `update` on each snowflake (particles), or clean up expiring snowflakes. This is the most CPU intensive part.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1r8hrud6qqaxpjgnattg.png)

More detail on particle update: manage its own lifespan, position, rotating and draw snowflake emoji on canvas accordingly


# Improvements

The original cursor-effect repo is a effect we need. But to be used on our site we have to add some improvements:

- Use image arrays to render customizable images 
- We want to have more control over snowflake's behavior such as: appearing frequency, it's speed and life, etc.
- Touch event on touch device will trigger mousemove and touchstart at the same time, generating two (almost overlapping) snowflakes at once.
- We want to import this library through npm for easier maintainence

# What we do

## Use image arrays to render customizable images 

Change the  `fillText` with `drawImage`, also add calculation for snowflake opacity: `globalAlpha` . Since there are several canvas context manipulation, we use `save` & `restore` to prevent polluting original context.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9qn079sm9bgqwvs805sw.png)

And since image loading is async, we need `loadImage` handles the image url array

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ue4z881n6ohpdcbrgizi.png)

With the help of promise all (or [Promise.allSettled](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled)) , to load image before `init()` call

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/czwb6veg7z01ihnafd3z.png)

## Control snowflake's behavior over initialization options

Main benefit is this boosts prototyping and discussion productivity, you can live-tweak and quickly show the result, or even hand the prototype to stakeholders and let them decide the behaviors.

This part is simple, just don't forget the option defaults

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aorr2t951ke0dxf72jqb.png)

## Touch event on touch device will trigger mousemove and touchstart

`bindEvent` method listens for `mousemove` `touchstart` `touchmove` , but Touch event on touch device will trigger mousemove and touchstart on user touch, causing excess particle creation, [you can see touch event order it on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent#event_order). To prevent this we need to detect if this device is touch device or not

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o29ztll8o1jumju1kod0.png)

## NPM-ify for easier maintainence

Uses ESM and published on NPM, use when in need.

 `npm install cursor-trails` 

# Something learned about Canvas and Transition

Which we adjust cursor-effect to deal with image loading, FPS drops significantly, thought it was because we throw too many image creating at a short time. Even rewrite one version using CSS Transition, just to find out it was because mass creating of SVG element consumes a lot of CPU.

Canvas is very effective with drawing bitmap image in a fixed space. While CSS Transition suits animating DOM elements on the page. So creating lots of image element on canvas is more smooth then creating <img> and transform it.

I must highlight chrome's devtool's **"rendering"** tab (edge has it also), especially two checked in this image


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/if3ued05j1dne73984m5.png)

it shows fps and paint areas, as gif below

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kzfpi0s7o6tekdru56ev.gif)
<figcaption>This was a version where we use CSS Transition, you can see image repaint areas</figcaption>

# Other considerations

The effect is sexy (in a retro 90s way), but we shouldn't forgot this is not the main purpose of users on our site (they are here for job searching and career development). So after some discussion we decided to let this sexy feature reside only in the main search section at home page. It's spacey, it is what users first sees, it wont interfere with other thing users want to do. Hope this gets some balance with Christmas ambient and user's opration.

We we're planning to use `prefers-reduced-motion` to deal with low-end devices, but due to time limitation, that will be put on the roadmap.

This library now only have snow falling effect, hope we can have more strategies on particle behavior. Maybe even customizable strategy, ex: fixed, floating, fadeout of cursor trailing effects.

That's about it. 
🎄❄️🧑‍🎄 Merry Christmas 🎄❄️🧑‍🎄

Here's our repo (again): [https://github.com/yourator/cursor-trails](https://github.com/yourator/cursor-trails)

# References

- stackoverflow's april fools day effect
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tgrm7sln8fwimba9v1m7.gif)
- [Mouse Trail - Noah Yamamoto (archive.org)](http://web.archive.org/web/20200805225612/https://noahyamamoto.com/blog/mousetrailanimation)
- [The technology of nostalgia (humphd.org)](https://blog.humphd.org/the-technology-of-nostalgia/)
- [When to Use SVG vs. When to Use Canvas - CSS-Tricks](https://css-tricks.com/when-to-use-svg-vs-when-to-use-canvas/)