Appearance
让枯燥的网页更有趣,新年新气象自定义你的鼠标图标,附开源代码
最近看了一篇修改鼠标图标的文章,感觉非常有意思。其实现方案也非常简单,其实就是修改了css的cursor图标的url。
但是针对自己的网页实现略显无趣,简单封装了一下做成一个chrome插件,无论浏览哪个网页都能看到新图标。
这也算是新年新气象了,提前给大家拜个年。
实现思路
- 通过css样式修改
cursor图标的url。 - 通过chrome官方提供的方式获取图片
- 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插件市场里也已经有了不少成品,但是因为网络原因恐怕比较难下载到。
后续我继续封装一下,把代码打包放在公众号上让大家下载即用,敬请期待。
