我正在研究CSS中的offset-path,您可以在其中执行以下操作:
* {
box-sizing: border-box;
}
:root {
--delay: 0ms;
--path: path("M.4 76.8C102-24.9 266.9-24.9 368.5 76.8c81.3 81.3 81.3 213.2 0 294.5-65.1 65.1-170.6 65.1-235.6 0-52.1-52.1-52.1-136.5 0-188.5 41.6-41.6 109.2-41.6 150.8 0 33.3 33.3 33.3 87.3 0 120.6-26.7 26.7-69.9 26.7-96.5 0-21.3-21.3-21.3-55.9 0-77.2 17.1-17.1 44.7-17.1 61.8 0 13.6 13.6 13.6 35.8 0 49.4-10.9 10.9-28.6 10.9-39.5 0-8.7-8.7-8.7-22.9 0-31.6 7-7 18.3-7 25.3 0");
}
body {
margin: 0;
padding: 2rem;
min-height: 100vh;
background-color: #1b1b24;
display: flex;
justify-content: center;
align-items: center;
}
.wrapper {
position: relative;
}
.obj {
--color: salmon;
position: absolute;
top: 0;
left: 0;
offset-path: var(--path);
animation: move 4500ms infinite ease-in-out var(--delay);
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
background-color: var(--color);
opacity: 0;
transform: scale(0);
}
.obj--2 {
--delay: 1500ms;
--color: hotpink;
}
.obj--3 {
--delay: 3000ms;
--color: turquoise;
}
svg {
width: 429px;
}
@keyframes appear {
100% {
opacity: 1;
}
}
@keyframes move {
10% {
opacity: 1;
offset-distance: 0%;
transform: scale(1);
}
30% {
box-shadow: -0.5rem 0 0.3rem var(--color, white);
}
70% {
box-shadow: -0.5rem 0 0.3rem var(--color, white);
}
90% {
opacity: 1;
offset-distance: 100%;
transform: scale(0.2);
box-shadow: none;
}
100% {
opacity: 0;
offset-distance: 100%;
transform: scale(0.2);
}
}
<div class="wrapper">
<div class="obj"></div>
<div class="obj obj--2"></div>
<div class="obj obj--3"></div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 431.7 422.6"><path d="M1.1 77.8c101.7-101.7 266.5-101.7 368.2 0 81.3 81.3 81.3 213.2 0 294.5-65.1 65.1-170.6 65.1-235.6 0-52.1-52.1-52.1-136.5 0-188.5 41.6-41.6 109.2-41.6 150.8 0 33.3 33.3 33.3 87.3 0 120.6-26.7 26.7-69.9 26.7-96.5 0-21.3-21.3-21.3-55.9 0-77.2 17.1-17.1 44.7-17.1 61.8 0 13.6 13.6 13.6 35.8 0 49.4-10.9 10.9-28.6 10.9-39.5 0-8.7-8.7-8.7-22.9 0-31.6 7-7 18.3-7 25.3 0" fill="none" stroke="#5e5e7d" stroke-width="3" stroke-miterlimit="10"/></svg>
</div>
他们有这个变量:
--path: path("M.4 76.8C102-24.9 266.9-24.9 368.5 76.8c81.3 81.3 81.3 213.2 0 294.5-65.1 65.1-170.6 65.1-235.6 0-52.1-52.1-52.1-136.5 0-188.5 41.6-41.6 109.2-41.6 150.8 0 33.3 33.3 33.3 87.3 0 120.6-26.7 26.7-69.9 26.7-96.5 0-21.3-21.3-21.3-55.9 0-77.2 17.1-17.1 44.7-17.1 61.8 0 13.6 13.6 13.6 35.8 0 49.4-10.9 10.9-28.6 10.9-39.5 0-8.7-8.7-8.7-22.9 0-31.6 7-7 18.3-7 25.3 0");
不知何故,它被转换成一些底层的数据结构,然后每一帧,你都会以某种方式沿着这条“路径”移动。它是如何工作的,你如何在高层次上实现它?
基本上,我假设您已经以某种方式将SVG d
路径转换为向量(点数组?),但这似乎不正确,因为有些点不在曲线上(控制点)。然后,时钟的每一次滴答/更新,它都会移动对象的x和y位置,使其沿着路径更远。然而,我并不是在做思想上的飞跃/连接,看看如何实现这一点。有什么见解吗?
我猜问题的一部分是,path
是如何在幕后实现的?如何计算沿着路径/曲线的时钟的每个滴答声的下一个位置?CSS以某种方式将所有这些都抽象了出来,我想知道它的内部工作原理。
发布于 2021-09-08 20:27:41
This可能会有所帮助:
function setAnimationsProgress(insTime) {
let i = 0;
const animations = instance.animations;
const animationsLength = animations.length;
while (i < animationsLength) {
const anim = animations[i];
const animatable = anim.animatable;
const tweens = anim.tweens;
const tweenLength = tweens.length - 1;
let tween = tweens[tweenLength];
// Only check for keyframes if there is more than one tween
if (tweenLength) tween = filterArray(tweens, t => (insTime < t.end))[0] || tween;
const elapsed = minMax(insTime - tween.start - tween.delay, 0, tween.duration) / tween.duration;
const eased = isNaN(elapsed) ? 1 : tween.easing(elapsed);
const strings = tween.to.strings;
const round = tween.round;
const numbers = [];
const toNumbersLength = tween.to.numbers.length;
let progress;
for (let n = 0; n < toNumbersLength; n++) {
let value;
const toNumber = tween.to.numbers[n];
const fromNumber = tween.from.numbers[n] || 0;
if (!tween.isPath) {
value = fromNumber + (eased * (toNumber - fromNumber));
} else {
value = getPathProgress(tween.value, eased * toNumber, tween.isPathTargetInsideSVG);
}
if (round) {
if (!(tween.isColor && n > 2)) {
value = Math.round(value * round) / round;
}
}
numbers.push(value);
}
// Manual Array.reduce for better performances
const stringsLength = strings.length;
if (!stringsLength) {
progress = numbers[0];
} else {
progress = strings[0];
for (let s = 0; s < stringsLength; s++) {
const a = strings[s];
const b = strings[s + 1];
const n = numbers[s];
if (!isNaN(n)) {
if (!b) {
progress += n + ' ';
} else {
progress += n + b;
}
}
}
}
setProgressValue[anim.type](animatable.target, anim.property, progress, animatable.transforms);
anim.currentValue = progress;
i++;
}
}
function getPathProgress(path, progress, isPathTargetInsideSVG) {
function point(offset = 0) {
const l = progress + offset >= 1 ? progress + offset : 0;
return path.el.getPointAtLength(l);
}
const svg = getParentSvg(path.el, path.svg)
const p = point();
const p0 = point(-1);
const p1 = point(+1);
const scaleX = isPathTargetInsideSVG ? 1 : svg.w / svg.vW;
const scaleY = isPathTargetInsideSVG ? 1 : svg.h / svg.vH;
switch (path.property) {
case 'x': return (p.x - svg.x) * scaleX;
case 'y': return (p.y - svg.y) * scaleY;
case 'angle': return Math.atan2(p1.y - p0.y, p1.x - p0.x) * 180 / Math.PI;
}
}
在那里,它计算每个动画步骤的路径点。不知道该如何进一步解释。
https://stackoverflow.com/questions/69109073
复制相似问题