这是一个很炫酷的渐变文字价值效果,这个效果的独特之处在于它结合了多层文本、渐变色和遮罩动画,创造出一种复杂而吸引人的视觉效果。通过 CSS 变量的使用,代码也具有很好的可定制性,允许轻松更改颜色、动画时间等参数。
主要特点和核心实现逻辑如下:
效果描述:
- 每个单词都有一个从右到左的渐变色擦除动画效果。
- 动画包括三层:原始文本、彩色渐变文本和模糊的彩色渐变文本。
- 每个单词使用不同的颜色渐变。
核心实现逻辑:
- HTML结构:每个单词被包裹在三个
<span>
标签中,分别对应普通文本、渐变文本和模糊渐变文本。
- CSS变量:使用 CSS 变量定义动画持续时间、缓动函数和颜色渐变。
- 渐变色背景:使用
linear-gradient
创建彩色渐变背景。通过 --colors
CSS 变量自定义每个单词的渐变色。
- 文本裁剪:使用
-webkit-background-clip: text
和 -webkit-text-fill-color: transparent
将渐变背景裁剪成文字形状。
- 遮罩动画:使用 CSS
mask
属性创建一个从右到左移动的遮罩。遮罩使用 linear-gradient
定义,创建一个渐变的透明度效果。mask-size: 300% 100%
使遮罩宽度为元素的三倍,允许完整的动画循环。
- 模糊效果:
.blur-in
类使用 filter: blur(10px)
创建模糊效果。
- 动画同步:所有三层使用相同的动画持续时间和缓动函数,确保同步移动。
源代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
transition: all 0.5s;
}
body {
background-color: #252525;
font-family: "Lato", sans-serif;
font-weight: 300;
}
.text {
position: absolute;
left: 10px;
bottom: 10px;
}
.text, a {
color: #f4f4f4;
font-weight: 100;
}
svg {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.textTime {
fill: #f4f4f4;
text-anchor: middle;
alignment-baseline: middle;
font-size: 1.5rem;
font-weight: 100;
}
.outerRing {
fill: none;
stroke: #f4f4f4;
stroke-width: 2px;
stroke-dasharray: 4px;
opacity: 0.5;
}
.primCircle {
fill: #252525;
stroke: #f4f4f4;
stroke-width: 10px;
}
.secCircle {
fill: #45d9fd;
stroke: #252525;
stroke-width: 3px;
}
.spike {
stroke: #f4f4f4;
stroke-width: 2px;
}
.triangle {
fill: #ee2560;
}
</style>
</head>
<body>
<div class="clock"></div>
</body>
<script>
const r1 = 5;
const r2 = 10;
const r3 = 15;
const width = window.innerWidth;
const height = window.innerHeight;
const minWH = Math.min(width, height);
let maxSize;
if (minWH < 430) {
maxSize = minWH - 30;
} else {
maxSize = 400;
}
const rad = (a) => (Math.PI * (a - 90)) / 180;
const rx = (r, a, c) => c + r * Math.cos(rad(a, c));
const ry = (r, a, c) => c + r * Math.sin(rad(a));
const HMS = (t) => ({
h: t.getHours(),
m: t.getMinutes(),
s: t.getSeconds()
});
const pathStringVars = (c, r, time) => {
const { h, m, s } = HMS(time);
const hAngFact = 30;
const mAngFact = 6;
const sAngFact = 6;
const hx = rx(r - 30, hAngFact * h, c);
const hy = ry(r - 30, hAngFact * h, c);
const mx = rx(r - 30, mAngFact * m, c);
const my = ry(r - 30, mAngFact * m, c);
const sx = rx(r - 30, sAngFact * s, c);
const sy = ry(r - 30, sAngFact * s, c);
return { hx, hy, mx, my, sx, sy };
};
// 辅助函数:创建SVG元素
function createSVGElement(type) {
return document.createElementNS("http://www.w3.org/2000/svg", type);
}
// 创建时钟
function createClock() {
const size = maxSize;
const viewBox = `0 0 ${size} ${size}`;
const mid = size / 2;
const paddedRadius = (size - 30) / 2;
const svg = createSVGElement("svg");
svg.setAttribute("viewBox", viewBox);
svg.setAttribute("width", size);
svg.setAttribute("height", size);
// 外圈
const outerRing = createSVGElement("circle");
outerRing.setAttribute("cx", mid);
outerRing.setAttribute("cy", mid);
outerRing.setAttribute("r", mid - 5);
outerRing.setAttribute("class", "outerRing");
svg.appendChild(outerRing);
// 内圈
const primCircle = createSVGElement("circle");
primCircle.setAttribute("cx", mid);
primCircle.setAttribute("cy", mid);
primCircle.setAttribute("r", mid - 15);
primCircle.setAttribute("class", "primCircle");
svg.appendChild(primCircle);
// 刻度
const spikesGroup = createSVGElement("g");
for (let i = 1; i < 13; i++) {
let ang = i * 30;
let spike = createSVGElement("line");
spike.setAttribute("x1", rx(paddedRadius - 5, ang, mid));
spike.setAttribute("x2", rx(paddedRadius - 10, ang, mid));
spike.setAttribute("y1", ry(paddedRadius - 5, ang, mid));
spike.setAttribute("y2", ry(paddedRadius - 10, ang, mid));
spike.setAttribute("class", "spike");
spikesGroup.appendChild(spike);
}
svg.appendChild(spikesGroup);
// 三角形路径
const triangle = createSVGElement("path");
triangle.setAttribute("class", "triangle");
svg.appendChild(triangle);
// 二级圆圈
const secCircleGroup = createSVGElement("g");
for (let i = 0; i < 3; i++) {
const circle = createSVGElement("circle");
circle.setAttribute("class", "secCircle");
circle.setAttribute("r", [r1, r2, r3][i]);
secCircleGroup.appendChild(circle);
}
svg.appendChild(secCircleGroup);
// 文本时间
const textTime = createSVGElement("text");
textTime.setAttribute("x", mid);
textTime.setAttribute("y", mid);
textTime.setAttribute("class", "textTime");
svg.appendChild(textTime);
document.querySelector(".clock").appendChild(svg);
// 更新时钟
function updateClock() {
const time = new Date();
const { hx, hy, mx, my, sx, sy } = pathStringVars(mid, paddedRadius, time);
// 更新三角形
triangle.setAttribute("d", `M${hx},${hy} L${mx},${my} L${sx},${sy} L${hx},${hy}`);
// 更新二级圆圈
const circles = secCircleGroup.children;
circles[0].setAttribute("cx", hx);
circles[0].setAttribute("cy", hy);
circles[1].setAttribute("cx", mx);
circles[1].setAttribute("cy", my);
circles[2].setAttribute("cx", sx);
circles[2].setAttribute("cy", sy);
// 更新文本时间
const timeString = time.toTimeString().slice(0, 8).replace(/:/g, " : ");
textTime.textContent = timeString;
}
// 每秒更新时钟
setInterval(updateClock, 1000);
updateClock(); // 初始更新
}
// 当DOM加载完成后创建时钟
document.addEventListener("DOMContentLoaded", createClock);
</script>
</html>