#前言
部分博客想要一个良好的前端互动体验,比如鼠标拖尾,点击等等交互有一些好玩的特效,这里给出一个好玩的特效
#展示
#文件结构
index.html
script.js
style.css
#下载
#代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>鼠标跟随 </title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<pointer-particles></pointer-particles>
<!-- partial -->
<script src="./script.js"></script>
</body>
</html>
class PointerParticle {
constructor(spread, speed, component) {
const { ctx, pointer, hue } = component;
this.ctx = ctx;
this.x = pointer.x;
this.y = pointer.y;
this.mx = pointer.mx * 0.1;
this.my = pointer.my * 0.1;
this.size = Math.random() + 1;
this.decay = 0.01;
this.speed = speed * 0.08;
this.spread = spread * this.speed;
this.spreadX = (Math.random() - 0.5) * this.spread - this.mx;
this.spreadY = (Math.random() - 0.5) * this.spread - this.my;
this.color = `hsl(${hue}deg 90% 60%)`;
}
draw() {
this.ctx.fillStyle = this.color;
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
this.ctx.fill();
}
collapse() {
this.size -= this.decay;
}
trail() {
this.x += this.spreadX * this.size;
this.y += this.spreadY * this.size;
}
update() {
this.draw();
this.trail();
this.collapse();
}
}
class PointerParticles extends HTMLElement {
static register(tag = "pointer-particles") {
if ("customElements" in window) {
customElements.define(tag, this);
}
}
static css = `
:host {
display: grid;
width: 100%;
height: 100%;
pointer-events: none;
}
`;
constructor() {
super();
this.canvas;
this.ctx;
this.fps = 60;
this.msPerFrame = 1000 / this.fps;
this.timePrevious;
this.particles = [];
this.pointer = {
x: 0,
y: 0,
mx: 0,
my: 0
};
this.hue = 0;
}
connectedCallback() {
const canvas = document.createElement("canvas");
const sheet = new CSSStyleSheet();
this.shadowroot = this.attachShadow({ mode: "open" });
sheet.replaceSync(PointerParticles.css);
this.shadowroot.adoptedStyleSheets = [sheet];
this.shadowroot.append(canvas);
this.canvas = this.shadowroot.querySelector("canvas");
this.ctx = this.canvas.getContext("2d");
this.setCanvasDimensions();
this.setupEvents();
this.timePrevious = performance.now();
this.animateParticles();
}
createParticles(event, { count, speed, spread }) {
this.setPointerValues(event);
for (let i = 0; i < count; i++) {
this.particles.push(new PointerParticle(spread, speed, this));
}
}
setPointerValues(event) {
this.pointer.x = event.x - this.offsetLeft;
this.pointer.y = event.y - this.offsetTop;
this.pointer.mx = event.movementX;
this.pointer.my = event.movementY;
}
setupEvents() {
const parent = this.parentNode;
parent.addEventListener("click", (event) => {
this.createParticles(event, {
count: 300,
speed: Math.random() + 1,
spread: Math.random() + 50
});
});
parent.addEventListener("pointermove", (event) => {
this.createParticles(event, {
count: 20,
speed: this.getPointerVelocity(event),
spread: 1
});
});
window.addEventListener("resize", () => this.setCanvasDimensions());
}
getPointerVelocity(event) {
const a = event.movementX;
const b = event.movementY;
const c = Math.floor(Math.sqrt(a * a + b * b));
return c;
}
handleParticles() {
for (let i = 0; i < this.particles.length; i++) {
this.particles[i].update();
if (this.particles[i].size <= 0.1) {
this.particles.splice(i, 1);
i--;
}
}
}
setCanvasDimensions() {
const rect = this.parentNode.getBoundingClientRect();
this.canvas.width = rect.width;
this.canvas.height = rect.height;
}
animateParticles() {
requestAnimationFrame(() => this.animateParticles());
const timeNow = performance.now();
const timePassed = timeNow - this.timePrevious;
if (timePassed < this.msPerFrame) return;
const excessTime = timePassed % this.msPerFrame;
this.timePrevious = timeNow - excessTime;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.hue = this.hue > 360 ? 0 : (this.hue += 3);
this.handleParticles();
}
}
PointerParticles.register();
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
}
body {
overflow: hidden;
display: grid;
color: white;
background: black;
}
Comments NOTHING