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 the first WalkScape sprite on the page and makes it follow the cursor.
  * Clones a WalkScape sprite and makes it follow the mouse.
  */
  */
(function () {
(function () {
'use strict';
'use strict';
console.log('AnimatedCursorFollower gadget loaded');
 
console.log('Sprites found:', document.querySelectorAll('span.ws-sprite').length);
const CONFIG = {
const CONFIG = {
scale: 1.5,
scale: 1.5,
Line 20: Line 20:
currentY: window.innerHeight / 2,
currentY: window.innerHeight / 2,
facingRight: true,
facingRight: true,
reqId: null,
reqId: null
running: false
};
};


let pet = null;
let mounted = false;
let centerOffset = 36;
let centerOffset = 36;
function render() {
if (!pet || !state.running) return;
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);
}
function start() {
if (state.running) return;
state.running = true;
state.reqId = requestAnimationFrame(render);
}
function stop() {
state.running = false;
if (state.reqId) {
cancelAnimationFrame(state.reqId);
state.reqId = null;
}
}


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


pet = sourceNode.cloneNode(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;
 
const frameSize =
parseInt(styles.getPropertyValue('--frame'), 10) || 48;


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


pet.classList.add('animated-cursor-follower');
wrapper.className = 'animated-cursor-follower-wrapper';
pet.style.setProperty('--scale', CONFIG.scale);


Object.assign(pet.style, {
Object.assign(wrapper.style, {
position: 'fixed',
position: 'fixed',
top: '0',
top: '0',
Line 85: Line 53:
});
});


document.body.appendChild(pet);
// Preserve sprite appearance
Object.assign(pet.style, {
display: 'inline-block',
backgroundImage: styles.backgroundImage,
backgroundSize: styles.backgroundSize,
backgroundPosition: styles.backgroundPosition,
backgroundRepeat: styles.backgroundRepeat,
width: styles.width,
height: styles.height
});
 
pet.style.setProperty('--scale', String(CONFIG.scale));
 
wrapper.appendChild(pet);
document.body.appendChild(wrapper);


document.addEventListener('mousemove', function (e) {
document.addEventListener('mousemove', function (e) {
Line 92: Line 74:
}, { passive: true });
}, { passive: true });


document.addEventListener('mouseleave', stop);
document.addEventListener('mouseleave', function () {
document.addEventListener('mouseenter', start);
if (state.reqId) {
cancelAnimationFrame(state.reqId);
state.reqId = null;
}
});
 
document.addEventListener('mouseenter', function () {
if (!state.reqId) {
render();
}
});


start();
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;
 
wrapper.style.transform =
`translate3d(` +
`${state.currentX - centerOffset}px, ` +
`${state.currentY - centerOffset}px, 0)` +
` scaleX(${scaleX})`;
 
state.reqId = requestAnimationFrame(render);
}
 
render();
}
}


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


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


mountPet(sprite);
mountPet(sprite);
Line 107: Line 128:


$(init);
$(init);
}());
}());

Revision as of 01:06, 23 May 2026

/**
 * Animated Cursor Follower
 * Clones a WalkScape sprite and makes it follow the mouse.
 */

(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
	};

	let mounted = false;
	let centerOffset = 36;

	function mountPet(sourceNode) {
		if (mounted) return;
		mounted = true;

		const wrapper = document.createElement('span');
		const pet = sourceNode.cloneNode(true);

		const styles = window.getComputedStyle(sourceNode);

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

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

		wrapper.className = 'animated-cursor-follower-wrapper';

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

		// Preserve sprite appearance
		Object.assign(pet.style, {
			display: 'inline-block',
			backgroundImage: styles.backgroundImage,
			backgroundSize: styles.backgroundSize,
			backgroundPosition: styles.backgroundPosition,
			backgroundRepeat: styles.backgroundRepeat,
			width: styles.width,
			height: styles.height
		});

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

		wrapper.appendChild(pet);
		document.body.appendChild(wrapper);

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

		document.addEventListener('mouseleave', function () {
			if (state.reqId) {
				cancelAnimationFrame(state.reqId);
				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) {
				state.facingRight = true;
			} else if (state.targetX < state.currentX - 1) {
				state.facingRight = false;
			}

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

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

			state.reqId = requestAnimationFrame(render);
		}

		render();
	}

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

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

		mountPet(sprite);
	}

	$(init);

}());