🏠 Home 

Fancy cursor spring

Attaches a funny spring to your cursor on every website

// ==UserScript==
// @name          Fancy cursor spring
// @description   Attaches a funny spring to your cursor on every website
// @author        Konf
// @version       1.0.0
// @namespace     https://greasyfork.org/users/424058
// @icon          https://i.imgur.com/Gqf0kMZ.png
// @resource      dotImg https://i.imgur.com/Gqf0kMZ.png
// @include       *
// @compatible    Chrome
// @compatible    Opera
// @compatible    Firefox
// @run-at        document-idle
// @grant         GM_addStyle
// @grant         GM_getResourceURL
// @noframes
// ==/UserScript==
/**
* Hi! Don't change (or even resave) anything here because
* by doing this in Tampermonkey you will turn off updates
* of the script (idk about other script managers).
* This could be restored in settings but it might be hard to find,
* so better to reinstall the script if you're not sure
*/
/**
* Forked from Elastic Trail script by Philip Winston @ [email protected]
* Original script featured on dynamicdrive.com/dynamicindex13/trailer2.htm
*/
/* jshint esversion: 6 */
(function() {
'use strict';
const DELTAT = .01;
const SEGLEN = 10;
const SPRINGK = 10;
const MASS = 1;
const XGRAVITY = 0;
const YGRAVITY = 50;
const RESISTANCE = 10;
const STOPVEL = 0.1;
const STOPACC = 0.1;
const DOTSIZE = 18;
const BOUNCE = 0.75;
const tail = {
dots: [],
dotUrl: null,
dotsAmount: 5,
container: null,
};
let mX, mY, scrX, scrY;
mX = mY = scrX = scrY = 0;
// init
document.addEventListener('mousemove', (ev) => {
if (tail.dotsAmount) {
updateMouseXY(ev);
tail.dotUrl = GM_getResourceURL('dotImg');
tail.container = document.createElement('dots');
document.body.appendChild(tail.container);
// bugfix
tail.dotsAmount += 1;
for (let i = 0; i < tail.dotsAmount; i++) {
tail.dots[i] = new TailDot(tail.container);
}
GM_addStyle(`
dot.cursor-spring-dot {
width: ${DOTSIZE}px;
height: ${DOTSIZE}px;
z-index: 2147483647;
position: absolute;
user-select: none;
background-size: contain;
background-image: url(${tail.dotUrl});
}
`);
// bugfix
tail.dots[0].dom.display = 'none';
requestAnimationFrame(animate);
document.addEventListener('scroll', updateMouseXY);
document.addEventListener('mousemove', updateMouseXY);
}
}, { once: true });
function Vec(X, Y) {
this.X = X;
this.Y = Y;
}
function TailDot(container) {
const dotEl = document.createElement('dot');
this.X = mX;
this.Y = mY;
this.dx = 0;
this.dy = 0;
this.dom = dotEl.style;
dotEl.className = 'cursor-spring-dot';
container.appendChild(dotEl);
}
// just save mouse position for animate() to use
function updateMouseXY(ev) {
if (ev.type === 'scroll') {
mX += scrollX - scrX;
mY += scrollY - scrY;
scrX = scrollX;
scrY = scrollY;
} else {
mX = ev.pageX;
mY = ev.pageY;
}
}
// adds force in X and Y to spring for dot[i] on dot[j]
function springForce(i, j, spring) {
const dx = (tail.dots[i].X - tail.dots[j].X);
const dy = (tail.dots[i].Y - tail.dots[j].Y);
const len = Math.sqrt(dx * dx + dy * dy);
if (len > SEGLEN) {
const springF = SPRINGK * (len - SEGLEN);
spring.X += (dx / len) * springF;
spring.Y += (dy / len) * springF;
}
}
function animate() {
requestAnimationFrame(animate);
tail.dots[0].X = mX;
tail.dots[0].Y = mY;
for (let i = 1; i < tail.dotsAmount; i++) {
const spring = new Vec(0, 0);
if (i > 0) {
springForce(i - 1, i, spring);
}
if (i < (tail.dotsAmount - 1)) {
springForce(i + 1, i, spring);
}
// air resistance/friction
const resist = new Vec(-tail.dots[i].dx * RESISTANCE, -tail.dots[i].dy * RESISTANCE);
// compute new accel, including gravity
const accel = new Vec((spring.X + resist.X) / MASS + XGRAVITY, (spring.Y + resist.Y) / MASS + YGRAVITY);
// compute new velocity
tail.dots[i].dx += (DELTAT * accel.X);
tail.dots[i].dy += (DELTAT * accel.Y);
// stop dead so it doesn't jitter when nearly still
if (Math.abs(tail.dots[i].dx) < STOPVEL &&
Math.abs(tail.dots[i].dy) < STOPVEL &&
Math.abs(accel.X) < STOPACC &&
Math.abs(accel.Y) < STOPACC) {
tail.dots[i].dx = 0;
tail.dots[i].dy = 0;
}
// move to new position
tail.dots[i].X += tail.dots[i].dx;
tail.dots[i].Y += tail.dots[i].dy;
const height = document.body.clientHeight + document.body.scrollTop;
const width = document.body.clientWidth + document.body.scrollLeft;
// bounce off 3 walls (leave ceiling open)
if (tail.dots[i].Y >= height - DOTSIZE - 1) {
if (tail.dots[i].dy > 0) {
tail.dots[i].dy = BOUNCE * -tail.dots[i].dy;
}
tail.dots[i].Y = height - DOTSIZE - 1;
}
if (tail.dots[i].X >= width - DOTSIZE) {
if (tail.dots[i].dx > 0) {
tail.dots[i].dx = BOUNCE * -tail.dots[i].dx;
}
tail.dots[i].X = width - DOTSIZE - 1;
}
if (tail.dots[i].X < 0) {
if (tail.dots[i].dx < 0) {
tail.dots[i].dx = BOUNCE * -tail.dots[i].dx;
}
tail.dots[i].X = 0;
}
// move img to new position
const newTop = Math.round(tail.dots[i].Y);
const newLeft = Math.round(tail.dots[i].X);
tail.dots[i].dom.top = newTop + 'px';
tail.dots[i].dom.left = newLeft + 'px';
}
}
})();