Skip to content

让枯燥的网页更有趣,新年新气象自定义你的鼠标图标,附开源代码

最近看了一篇修改鼠标图标的文章,感觉非常有意思。其实现方案也非常简单,其实就是修改了css的cursor图标的url。

但是针对自己的网页实现略显无趣,简单封装了一下做成一个chrome插件,无论浏览哪个网页都能看到新图标。

这也算是新年新气象了,提前给大家拜个年。

实现思路

  1. 通过css样式修改cursor图标的url。
  2. 通过chrome官方提供的方式获取图片
  3. css创建烟花效果,实现点击特效。

实现代码

js
(function () {
    // 获取图片
	const iconUrl = chrome.runtime.getURL('mouse.png');
	const body = document.body;

	// 注入样式
	const style = document.createElement('style');
	style.textContent = `
    * { cursor: url('${iconUrl}'), auto !important; }
    a, button, [role="button"], [onclick], [role="link"],
    input[type="submit"], input[type="button"], input[type="reset"], input[type="image"],
    select, option, label[for], .btn, .button, .clickable, .link, .pointer,
    [tabindex]:not([tabindex="-1"]), summary, details > summary,
    [data-toggle], [data-click], [data-action] { cursor: pointer !important; }
    input:not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="image"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="range"]):not([type="color"]),
    textarea, [contenteditable="true"], [contenteditable=""], .text-input, .input-field { cursor: text !important; }
    input[type="checkbox"], input[type="radio"], input[type="file"], input[type="range"], input[type="color"] { cursor: default !important; }
    [disabled], .disabled { cursor: not-allowed !important; }
    [resize], .resize { cursor: nw-resize !important; }
    .click-particle { position: fixed; border-radius: 50%; pointer-events: none; z-index: 9998; }
  `;
	(document.head || document.documentElement).appendChild(style);

	// 测试图标是否加载成功
	const testElement = document.createElement('div');
	testElement.style.cursor = `url('${iconUrl}'), auto`;
	document.body.appendChild(testElement);
	const computedStyle = window.getComputedStyle(testElement).cursor;
	if (!computedStyle.includes(iconUrl)) {
		console.warn('自定义鼠标图标未成功加载,请检查资源路径或浏览器支持情况。');
	}
	document.body.removeChild(testElement);

	// 粒子动画配置
	const PARTICLE_CONFIG = {
		count: 50,
		gravity: 0.5,
		speedRange: [100, 200],
		sizeRange: [2, 5],
		hueRange: [0, 360],
		saturationRange: [80, 100],
		lightnessRange: [50, 80],
		opacityDecay: 0.01,
	};

	// 创建单个粒子动画
	const createParticle = (x, y) => {

		const particle = document.createElement('div');
		particle.className = 'click-particle';

		// 设置粒子样式
		const size = PARTICLE_CONFIG.sizeRange[0] + Math.random() * (PARTICLE_CONFIG.sizeRange[1] - PARTICLE_CONFIG.sizeRange[0]);
		const hue = PARTICLE_CONFIG.hueRange[0] + Math.random() * (PARTICLE_CONFIG.hueRange[1] - PARTICLE_CONFIG.hueRange[0]);
		const saturation = PARTICLE_CONFIG.saturationRange[0] + Math.random() * (PARTICLE_CONFIG.saturationRange[1] - PARTICLE_CONFIG.saturationRange[0]);
		const lightness = PARTICLE_CONFIG.lightnessRange[0] + Math.random() * (PARTICLE_CONFIG.lightnessRange[1] - PARTICLE_CONFIG.lightnessRange[0]);

		particle.style.width = `${size}px`;
		particle.style.height = `${size}px`;
		particle.style.backgroundColor = `hsl(${hue}, ${saturation}%, ${lightness}%)`;
		particle.style.boxShadow = `0 0 ${size / 2}px hsl(${hue}, ${saturation}%, ${lightness}%)`;
		particle.style.transform = `translate(${x}px, ${y}px)`;
		particle.style.top = `0px`;

		body.appendChild(particle);

		// 动画逻辑
		const speed = PARTICLE_CONFIG.speedRange[0] + Math.random() * (PARTICLE_CONFIG.speedRange[1] - PARTICLE_CONFIG.speedRange[0]);
		const angle = Math.random() * Math.PI * 2;
		let vx = Math.cos(angle) * speed;
		let vy = Math.sin(angle) * speed;
		let opacity = 1;

		const animate = () => {
			const currentTransform = new DOMMatrixReadOnly(getComputedStyle(particle).transform);
			const newX = currentTransform.m41 + vx * 0.02;
			const newY = currentTransform.m42 + vy * 0.02;

			// 更新位置和透明度
			particle.style.transform = `translate(${newX}px, ${newY}px)`;
			opacity -= PARTICLE_CONFIG.opacityDecay;
			particle.style.opacity = Math.max(0, opacity);

			// 应用重力
			vy += PARTICLE_CONFIG.gravity;

			// 继续动画或结束
			if (opacity > 0) {
				requestAnimationFrame(animate);
			} else {
				particle.remove();
			}
		};

		animate();
	};

	// 节流函数
	const throttle = (func, delay) => {
		let lastCall = 0;
		return (...args) => {
			const now = Date.now();
			if (now - lastCall >= delay) {
				lastCall = now;
				func.apply(this, args);
			}
		};
	};

	// 触发烟花效果(节流)
	const throttledCreateFireworkEffect = throttle((x, y) => {
		for (let i = 0; i < PARTICLE_CONFIG.count; i++) {
			createParticle(x, y);
		}
	}, 100); // 每100ms最多触发一次

	// 事件监听
	document.addEventListener('click', (e) => throttledCreateFireworkEffect(e.clientX, e.clientY));
})();

总结

这个小项目还是非常有意思的,我看Chrome插件市场里也已经有了不少成品,但是因为网络原因恐怕比较难下载到。

后续我继续封装一下,把代码打包放在公众号上让大家下载即用,敬请期待。