我如何 "perfectly" 在 Anime.js 中按照 SVG 路径使对象居中?
How do I "perfectly" center an object following an SVG path in Anime.js?
我正在 a circle path in SVG, using Anime.js 周围移动一个 20px 的正方形。然而,正方形并没有完全位于其圆形路径的中心:
anime({
targets: '.square',
translateX: path('x'),
translateY: path('y'),
rotate: path('angle'),
easing: 'linear',
duration: 10000,
loop: true,
});
https://codepen.io/gremo/pen/wvKjrMW
我找到了类似的示例,但是我无法理解数学 来完成这项工作:
发生这种情况是因为您的对象(正方形)确实在沿着路径行驶。但是,对象是根据左上角值的第一个像素进行平移的(因此,如果制作 1px x 1px
正方形,您应该会看到该正方形很好地遵循路径)。这就是为什么对象的左上角总是粘在直线上的原因。你想要的是物体的中间部分粘在线上。
使用静态和手动计算的值(盒子原始宽度和高度的 50%)是合理的(例如,将 -10px
赋予 top
和 left
)。但是,当有许多对象被动画化时,您可能不想这样做(因为发生更改时您需要更新所有 CSS 代码)。相反,我们可以在 .square
上使用伪元素 ::after
并将其宽度的 50% 向左移动,将其高度的 50% 移动到顶部。这样,物体的附着点就是正方形的中心部分。现在,当您更新原始 .square
元素的宽度和高度时,您无需更新 top
和 left
值。您不能简单地将 transform: translate(-50%, -50%)
值添加到原始 .square
div,因为它将使用 Anime.js 进行动画处理,并且初始变换值将会丢失。从 JS 获取计算的初始变换值是棘手的。
document.addEventListener('DOMContentLoaded', function() {
const path = anime.path('.circle path');
anime({
targets: '.square',
translateX: path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
});
body {
background-color: #001f3f;
}
.container {
position: relative;
margin: 0 auto;
max-width: 500px;
}
.circle {
fill: none;
stroke: #b10dc9;
}
.square {
position: absolute;
top: 0;
left: 0;
display: inline-block;
width: 20px;
height: 20px;
}
.square::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
transform: translate(-50%, -50%);
background-color: #f012be;
}
<script src="https://unpkg.com/animejs@3.2.0/lib/anime.min.js"></script>
<div class="container">
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<path d="M400,250c0,82.84-67.16,150-150,150s-150-67.16-150-150s67.16-150,150-150S400,167.16,400,250z" />
</svg>
<span class="square"></span>
</div>
要理解逻辑,请添加另一个具有不同 width/height 的元素以查看每个对象的引用:
document.addEventListener('DOMContentLoaded', function() {
const path = anime.path('.circle path');
anime({
targets: '.square',
translateX:path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
anime({
targets: '.square2',
translateX: path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
});
body {
background-color: #001f3f;
}
.container {
position: relative;
margin: 0 auto;
max-width: 500px;
}
.circle {
fill: none;
stroke: #b10dc9;
}
.square {
position: absolute;
top: 0;
left: 0;
display: inline-block;
width: 20px;
height: 20px;
background-color: #f012be;
}
.square2 {
position: absolute;
top: 0;
left: 0;
display: inline-block;
width: 40px;
height: 40px;
background-color: blue;
}
<script src="https://unpkg.com/animejs@3.2.0/lib/anime.min.js"></script>
<div class="container">
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<path d="M400,250c0,82.84-67.16,150-150,150s-150-67.16-150-150s67.16-150,150-150S400,167.16,400,250z" />
</svg>
<span class="square2"></span>
<span class="square"></span>
</div>
如您所见,top/left 角沿着路径。为避免这种情况,您可以使用负 left/top 来偏移元素并使其居中,或者使用不同的 CSS 来制作通用的东西。
我用 0 维制作元素并考虑 box-shadw
创建正方形:
document.addEventListener('DOMContentLoaded', function() {
const path = anime.path('.circle path');
anime({
targets: '.square',
translateX:path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
anime({
targets: '.square2',
translateX: path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
});
body {
background-color: #001f3f;
}
.container {
position: relative;
margin: 0 auto;
max-width: 500px;
}
.circle {
fill: none;
stroke: #b10dc9;
}
.square {
content:"";
position: absolute;
left:0;
right:0;
width: 0;
height: 0;
box-shadow:0 0 0 10px #f012be;
}
.square2 {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
box-shadow:0 0 0 20px blue;
}
<script src="https://unpkg.com/animejs@3.2.0/lib/anime.min.js"></script>
<div class="container">
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<path d="M400,250c0,82.84-67.16,150-150,150s-150-67.16-150-150s67.16-150,150-150S400,167.16,400,250z" />
</svg>
<span class="square2"></span>
<span class="square"></span>
</div>
我正在 a circle path in SVG, using Anime.js 周围移动一个 20px 的正方形。然而,正方形并没有完全位于其圆形路径的中心:
anime({
targets: '.square',
translateX: path('x'),
translateY: path('y'),
rotate: path('angle'),
easing: 'linear',
duration: 10000,
loop: true,
});
https://codepen.io/gremo/pen/wvKjrMW
我找到了类似的示例,但是我无法理解数学 来完成这项工作:
发生这种情况是因为您的对象(正方形)确实在沿着路径行驶。但是,对象是根据左上角值的第一个像素进行平移的(因此,如果制作 1px x 1px
正方形,您应该会看到该正方形很好地遵循路径)。这就是为什么对象的左上角总是粘在直线上的原因。你想要的是物体的中间部分粘在线上。
使用静态和手动计算的值(盒子原始宽度和高度的 50%)是合理的(例如,将 -10px
赋予 top
和 left
)。但是,当有许多对象被动画化时,您可能不想这样做(因为发生更改时您需要更新所有 CSS 代码)。相反,我们可以在 .square
上使用伪元素 ::after
并将其宽度的 50% 向左移动,将其高度的 50% 移动到顶部。这样,物体的附着点就是正方形的中心部分。现在,当您更新原始 .square
元素的宽度和高度时,您无需更新 top
和 left
值。您不能简单地将 transform: translate(-50%, -50%)
值添加到原始 .square
div,因为它将使用 Anime.js 进行动画处理,并且初始变换值将会丢失。从 JS 获取计算的初始变换值是棘手的。
document.addEventListener('DOMContentLoaded', function() {
const path = anime.path('.circle path');
anime({
targets: '.square',
translateX: path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
});
body {
background-color: #001f3f;
}
.container {
position: relative;
margin: 0 auto;
max-width: 500px;
}
.circle {
fill: none;
stroke: #b10dc9;
}
.square {
position: absolute;
top: 0;
left: 0;
display: inline-block;
width: 20px;
height: 20px;
}
.square::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
transform: translate(-50%, -50%);
background-color: #f012be;
}
<script src="https://unpkg.com/animejs@3.2.0/lib/anime.min.js"></script>
<div class="container">
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<path d="M400,250c0,82.84-67.16,150-150,150s-150-67.16-150-150s67.16-150,150-150S400,167.16,400,250z" />
</svg>
<span class="square"></span>
</div>
要理解逻辑,请添加另一个具有不同 width/height 的元素以查看每个对象的引用:
document.addEventListener('DOMContentLoaded', function() {
const path = anime.path('.circle path');
anime({
targets: '.square',
translateX:path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
anime({
targets: '.square2',
translateX: path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
});
body {
background-color: #001f3f;
}
.container {
position: relative;
margin: 0 auto;
max-width: 500px;
}
.circle {
fill: none;
stroke: #b10dc9;
}
.square {
position: absolute;
top: 0;
left: 0;
display: inline-block;
width: 20px;
height: 20px;
background-color: #f012be;
}
.square2 {
position: absolute;
top: 0;
left: 0;
display: inline-block;
width: 40px;
height: 40px;
background-color: blue;
}
<script src="https://unpkg.com/animejs@3.2.0/lib/anime.min.js"></script>
<div class="container">
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<path d="M400,250c0,82.84-67.16,150-150,150s-150-67.16-150-150s67.16-150,150-150S400,167.16,400,250z" />
</svg>
<span class="square2"></span>
<span class="square"></span>
</div>
如您所见,top/left 角沿着路径。为避免这种情况,您可以使用负 left/top 来偏移元素并使其居中,或者使用不同的 CSS 来制作通用的东西。
我用 0 维制作元素并考虑 box-shadw
创建正方形:
document.addEventListener('DOMContentLoaded', function() {
const path = anime.path('.circle path');
anime({
targets: '.square',
translateX:path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
anime({
targets: '.square2',
translateX: path('x'),
translateY: path('y'),
easing: 'linear',
duration: 10000,
loop: true,
});
});
body {
background-color: #001f3f;
}
.container {
position: relative;
margin: 0 auto;
max-width: 500px;
}
.circle {
fill: none;
stroke: #b10dc9;
}
.square {
content:"";
position: absolute;
left:0;
right:0;
width: 0;
height: 0;
box-shadow:0 0 0 10px #f012be;
}
.square2 {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
box-shadow:0 0 0 20px blue;
}
<script src="https://unpkg.com/animejs@3.2.0/lib/anime.min.js"></script>
<div class="container">
<svg class="circle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<path d="M400,250c0,82.84-67.16,150-150,150s-150-67.16-150-150s67.16-150,150-150S400,167.16,400,250z" />
</svg>
<span class="square2"></span>
<span class="square"></span>
</div>