Tip of the day
Want to find something particular about the game and don't mind spoilers? Check the Wiki!

MediaWiki:Gadget-AnimatedCursorFollower.js: Difference between revisions

From Walkscape Walkthrough
mNo edit summary
mNo edit summary
Line 1: Line 1:
/**
/**
  * Animated Cursor Follower
  * Animated Cursor Follower
  * Clones a WalkScape sprite and makes it follow the mouse.
  * WalkScape Wiki mouse-following pets.
  */
  */


Line 22: Line 22:
reqId: null
reqId: null
};
};
let mounted = false;
let centerOffset = 36;


function mountPet(sourceNode) {
function mountPet(sourceNode) {
if (mounted) return;
const pet = sourceNode.cloneNode(true);
mounted = true;


const wrapper = document.createElement('span');
const pet = sourceNode.cloneNode(true);
const styles = window.getComputedStyle(sourceNode);
const styles = window.getComputedStyle(sourceNode);
const frameSize = parseInt(styles.getPropertyValue('--frame'), 10) || 48;


centerOffset = (frameSize * CONFIG.scale) / 2;
const frameSize =
parseInt(styles.getPropertyValue('--frame') || 48, 10);


wrapper.className = 'animated-cursor-follower-wrapper';
const centerOffset =
(frameSize * CONFIG.scale) / 2;


Object.assign(wrapper.style, {
pet.style.setProperty(
'--scale',
String(CONFIG.scale)
);
 
Object.assign(pet.style, {
position: 'fixed',
position: 'fixed',
top: '0',
top: '0',
Line 50: Line 50:
});
});


// Let the existing ws-sprite CSS/JS handle sprite rendering.
document.body.appendChild(pet);
pet.removeAttribute('style');
pet.className = sourceNode.className;
pet.style.setProperty('--scale', String(CONFIG.scale));
pet.style.display = 'inline-block';


wrapper.appendChild(pet);
function render() {
document.body.appendChild(wrapper);
state.currentX +=
 
(state.targetX - state.currentX) *
document.addEventListener('mousemove', function (e) {
CONFIG.speed;
state.targetX = e.clientX + CONFIG.offsetX;
state.targetY = e.clientY + CONFIG.offsetY;
}, { passive: true });


document.addEventListener('mouseleave', function () {
state.currentY +=
if (state.reqId) {
(state.targetY - state.currentY) *
cancelAnimationFrame(state.reqId);
CONFIG.speed;
state.reqId = null;
}
});
 
document.addEventListener('mouseenter', function () {
if (!state.reqId) {
render();
}
});
 
function render() {
state.currentX += (state.targetX - state.currentX) * CONFIG.speed;
state.currentY += (state.targetY - state.currentY) * CONFIG.speed;


if (state.targetX > state.currentX + 1) {
if (state.targetX > state.currentX + 1) {
state.facingRight = true;
state.facingRight = true;
} else if (state.targetX < state.currentX - 1) {
} else if (
state.targetX < state.currentX - 1
) {
state.facingRight = false;
state.facingRight = false;
}
}


const scaleX = state.facingRight ? 1 : -1;
const scaleX =
state.facingRight ? 1 : -1;


wrapper.style.transform =
pet.style.transform =
`translate3d(${state.currentX - centerOffset}px, ` +
`translate3d(` +
`${state.currentY - centerOffset}px, 0) scaleX(${scaleX})`;
`${state.currentX - centerOffset}px, ` +
`${state.currentY - centerOffset}px, 0)` +
` scaleX(${scaleX})`;


state.reqId = requestAnimationFrame(render);
state.reqId =
requestAnimationFrame(render);
}
}
document.addEventListener(
'mousemove',
function (e) {
state.targetX =
e.clientX + CONFIG.offsetX;
state.targetY =
e.clientY + CONFIG.offsetY;
},
{ passive: true }
);
document.addEventListener(
'mouseleave',
function () {
cancelAnimationFrame(
state.reqId
);
}
);
document.addEventListener(
'mouseenter',
function () {
render();
}
);


render();
render();
Line 100: Line 114:


function init() {
function init() {
const sprite = document.querySelector(
const sprite =
'span.ws-sprite:not(.animated-cursor-follower-wrapper span.ws-sprite)'
document.querySelector(
);
'span.ws-sprite'
);


if (!sprite) {
if (!sprite) {
Line 112: Line 127:
}
}


$(init);
mw.loader.using(
['mediawiki.util'],
function () {
$(window).on(
'load',
function () {
setTimeout(init, 1000);
}
);
}
);


}());
}());

Revision as of 01:13, 23 May 2026

/**
 * Animated Cursor Follower
 * WalkScape Wiki mouse-following pets.
 */

(function () {
	'use strict';

	const CONFIG = {
		scale: 1.5,
		speed: 0.5,
		offsetX: 30,
		offsetY: 30
	};

	const state = {
		targetX: window.innerWidth / 2,
		targetY: window.innerHeight / 2,
		currentX: window.innerWidth / 2,
		currentY: window.innerHeight / 2,
		facingRight: true,
		reqId: null
	};

	function mountPet(sourceNode) {
		const pet = sourceNode.cloneNode(true);

		const styles = window.getComputedStyle(sourceNode);

		const frameSize =
			parseInt(styles.getPropertyValue('--frame') || 48, 10);

		const centerOffset =
			(frameSize * CONFIG.scale) / 2;

		pet.style.setProperty(
			'--scale',
			String(CONFIG.scale)
		);

		Object.assign(pet.style, {
			position: 'fixed',
			top: '0',
			left: '0',
			pointerEvents: 'none',
			zIndex: '99999',
			margin: '0',
			willChange: 'transform',
			transition: 'none'
		});

		document.body.appendChild(pet);

		function render() {
			state.currentX +=
				(state.targetX - state.currentX) *
				CONFIG.speed;

			state.currentY +=
				(state.targetY - state.currentY) *
				CONFIG.speed;

			if (state.targetX > state.currentX + 1) {
				state.facingRight = true;
			} else if (
				state.targetX < state.currentX - 1
			) {
				state.facingRight = false;
			}

			const scaleX =
				state.facingRight ? 1 : -1;

			pet.style.transform =
				`translate3d(` +
				`${state.currentX - centerOffset}px, ` +
				`${state.currentY - centerOffset}px, 0)` +
				` scaleX(${scaleX})`;

			state.reqId =
				requestAnimationFrame(render);
		}

		document.addEventListener(
			'mousemove',
			function (e) {
				state.targetX =
					e.clientX + CONFIG.offsetX;

				state.targetY =
					e.clientY + CONFIG.offsetY;
			},
			{ passive: true }
		);

		document.addEventListener(
			'mouseleave',
			function () {
				cancelAnimationFrame(
					state.reqId
				);
			}
		);

		document.addEventListener(
			'mouseenter',
			function () {
				render();
			}
		);

		render();
	}

	function init() {
		const sprite =
			document.querySelector(
				'span.ws-sprite'
			);

		if (!sprite) {
			setTimeout(init, 500);
			return;
		}

		mountPet(sprite);
	}

	mw.loader.using(
		['mediawiki.util'],
		function () {
			$(window).on(
				'load',
				function () {
					setTimeout(init, 1000);
				}
			);
		}
	);

}());