这是一款使用css3、svg和js制作的炫酷水滴Loading特效。该特效通过简单的代码,实现在页面中生成不断旋转的水滴Loading效果,非常炫酷。
使用方法
使用
Html代码
p
Css代码
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--hue: 223;
--bg: hsl(var(--hue),90%,95%);
--fg: hsl(var(--hue),90%,5%);
--primary: hsl(var(--hue),90%,50%);
--trans-dur: 0.3s;
font-size: calc(16px + (24 - 16) * (100vw - 320px) / (1280 - 320));
}
body {
background-color: var(--bg);
color: var(--fg);
font: 1em/1.5 sans-serif;
height: 100vh;
display: grid;
place-items: center;
transition: background-color var(--trans-dur);
}
main {
padding: 1.5em 0;
}
.pl {
display: block;
overflow: visible;
width: 8em;
height: 8em;
}
.pl__ring {
stroke: hsla(var(--hue),90%,5%,0.1);
transition: stroke var(--trans-dur);
}
.pl__worm {
stroke: var(--primary);
transform-origin: 64px 64px;
visibility: hidden;
}
.pl__worm--moving {
animation: worm 8s linear;
visibility: visible;
}
/* Dark theme */
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(var(--hue),90%,5%);
--fg: hsl(var(--hue),90%,95%);
}
.pl__ring {
stroke: hsla(var(--hue),90%,95%,0.1);
}
}
/* Animations */
@keyframes worm {
from {
stroke-dasharray: 22 307.86 22;
transform: rotate(0);
}
to {
stroke-dasharray: 2 347.86 2;
transform: rotate(4turn);
}
}
js代码
window.addEventListener("DOMContentLoaded",() => {
const dp = new DecayingPreloader(".pl");
});
class DecayingPreloader {
particles = [];
totalParticles = 120;
replayTimeout = null;
constructor(el) {
this.el = document.querySelector(el);
this.particleGroup = this.el?.querySelector("[data-particles]");
this.worm = this.el?.querySelector("[data-worm]");
this.init();
}
init() {
this.spawnParticles(this.totalParticles);
this.worm?.addEventListener("animationend",this.replayParticles.bind(this));
}
createParticle(x,y,r,delay) {
const particle = new DecayParticle(x,y,r,delay);
this.particleGroup?.appendChild(particle.g);
// animation params
particle.gAnimation = particle.g.animate(
[
{ transform: `translate(${particle.x}px,0)` },
{ transform: `translate(${particle.x + particle.dx}px,0)` },
],
{ delay: particle.delay, duration: particle.duration, easing: "linear" }
);
particle.cAnimation = particle.c.animate(
[
{ opacity: 1, transform: `translate(0,${particle.y}px) scale(1)` },
{ opacity: 1, transform: `translate(0,${particle.y + particle.dy}px) scale(0)` },
],
{ delay: particle.delay, duration: particle.duration, easing: "ease-in" }
);
// finally create the particle
this.particles.push(particle);
}
replayParticles() {
const movingClass = "pl__worm--moving";
const timeout = 800;
// retrigger the worm animation
this.worm.classList.remove(movingClass);
clearTimeout(this.replayTimeout);
this.replayTimeout = setTimeout(() => {
this.worm.classList.add(movingClass);
// restart the particles
this.particles.forEach(particle => {
particle.gAnimation.finish();
particle.gAnimation.play();
particle.cAnimation.finish();
particle.cAnimation.play();
});
},timeout);
}
spawnParticles(count = 1) {
const centerXY = 64;
const radius = 56;
const loops = 4;
const maxDelayPerLoop = 2000;
const particlesPerLoop = Math.round(this.totalParticles / loops);
const angleOffset = -2;
const particleRadius = 7;
for (let c = 0; c < count; ++c) {
// place along the ring
const percent = Utils.easeInOutCubic(c % particlesPerLoop / particlesPerLoop);
const angle = 360 * percent + angleOffset;
const x = centerXY + radius * Math.sin(Utils.degToRad(angle));
const y = centerXY - radius * Math.cos(Utils.degToRad(angle));
const loopsCompleted = Math.floor(c / particlesPerLoop);
const delay = maxDelayPerLoop * percent + maxDelayPerLoop * loopsCompleted;
this.createParticle(x,y,particleRadius,delay);
}
}
}
class DecayParticle {
duration = 500;
dx = Utils.randomFloat(-16,16);
dy = Utils.randomFloat(32,64);
// group
gAnimation = null;
// circle
cAnimation = null;
constructor(x = 0,y = 0,r = 1,delay = 0) {
this.x = x;
this.y = y;
this.r = r;
this.delay = delay;
// namespace
const ns = "http://www.w3.org/2000/svg";
// group to move horizontally in the animation
const g = document.createElementNS(ns,"g");
g.setAttributeNS(null,"transform",`translate(${x},0)`);
// circle to move vertically in the animation
const circle = document.createElementNS(ns,"circle");
circle.setAttributeNS(null,"opacity","0");
circle.setAttributeNS(null,"r",`${this.r}`);
circle.setAttributeNS(null,"transform",`translate(0,${y})`);
circle.setAttributeNS(null,"fill","var(--primary)");
this.g = g;
this.c = circle;
this.g.appendChild(this.c);
}
}
class Utils {
static degToRad(deg) {
return deg * Math.PI / 180;
}
// ease methods from https://gist.github.com/gre/1650294
static easeInOutCubic(t) {
return t < 0.5 ? 4 * t ** 3 : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
}
static randomFloat(min = 0,max = 2**32) {
const percent = crypto.getRandomValues(new Uint32Array(1))[0] / 2**32;
const relativeValue = (max - min) * percent;
const plusMin = min + relativeValue;
return +(plusMin).toFixed(3);
}
}
codepen网址:https://codepen.io/jkantner/pen/eYjMYPo