如何在 CSS "roller coaster" 动画中弯曲一条线?
How can I curve a line in CSS "roller coaster" animation?
我正在尝试使用 CSS 创建过山车风格的动画。
我想知道 "coaster" 在循环阶段如何弯曲。
我正在寻找一个完整的 CSS 解决方案,但如果需要一点点 JavaScript 我可以接受。
到目前为止我的代码:
#container {
width: 200px;
height: 300px;
margin-top: 50px;
position: relative;
animation: 10s infinite loop;
animation-timing-function: linear;
}
#coaster {
width: 100px;
height: 10px;
background: lightblue;
position: absolute;
bottom: 0;
left: 1px;
right: 1px;
margin: 0 auto;
}
@keyframes loop {
from {
margin-left: -200px;
}
30% {
margin-left: calc(50% - 75px);
transform: rotate(0deg);
}
60% {
margin-left: calc(50% - 125px);
transform: rotate(-360deg);
}
to {
transform: rotate(-360deg);
margin-left: 100%;
}
}
<div id="container">
<div id="coaster"></div>
</div>
可以考虑border-radius
。这是您可以改进的基本示例
.box {
height:100px;
margin-top:50px;
border:10px solid transparent;
border-bottom:10px solid red;
width:100px;
animation:
loop 5s infinite alternate linear,
radius 5s infinite alternate linear;
}
@keyframes loop{
0% {
transform:translateX(0);
}
40% {
transform:translateX(100px) rotate(0deg);
}
60% {
transform:translateX(100px) rotate(-360deg);
}
100% {
transform:translateX(200px) rotate(-360deg);
}
}
@keyframes radius{
0%,28% {
border-radius:0%
}
35%,38% {
border-bottom-right-radius:50%;
border-bottom-left-radius:0%;
}
45%,55% {
border-radius:50%
}
62%,65% {
border-bottom-right-radius:0%;
border-bottom-left-radius:50%;
}
70%,100% {
border-radius:0%
}
}
<div class="box"></div>
另一个疯狂想法,您可以在透明弯曲路径后面为矩形形状设置动画:
.box {
height: 165px;
width: 315px;
position: relative;
border-radius: 0 100px 100px 0;
animation: hide 3s infinite linear alternate;
overflow:hidden;
}
.box:after {
content: "";
display:block;
height: 100%;
background: white padding-box;
border-radius: inherit;
border: 15px solid transparent;
border-left:0;
box-sizing: border-box;
}
.alt {
margin-top: -165px;
transform: scaleX(-1);
transform-origin: 170px 0;
animation-direction: alternate-reverse;
}
.box:before {
content: "";
position: absolute;
z-index: -1;
width: 90px;
height: 90px;
background: blue;
bottom: -40px;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -20px;
}
.alt:before {
animation-direction: alternate-reverse;
}
@keyframes loop {
15% {
transform: translateX(180px) rotate(0deg);
}
40% {
transform: translateX(180px) rotate(-180deg);
}
50%,100% {
transform: translateX(125px) rotate(-180deg);
}
}
@keyframes hide {
0%,50% {
visibility: visible;
}
50.2%,100% {
visibility: hidden;
}
}
<div class="box"></div>
<div class="box alt"></div>
代码少透明的优化版本(会考虑mask
)
.box {
height: 165px;
width: 315px;
position: relative;
border-radius: 0 100px 100px 0;
animation: hide 3s infinite linear alternate;
overflow:hidden;
-webkit-mask:
linear-gradient(#fff,#fff) top left /235px 15px,
linear-gradient(#fff,#fff) bottom left/235px 15px,
radial-gradient(farthest-side at left,transparent calc(100% - 15px),#fff 0) right/100px 100%;
-webkit-mask-repeat:no-repeat;
mask:
linear-gradient(#fff,#fff) top left/235px 15px,
linear-gradient(#fff,#fff) bottom left/235px 15px,
radial-gradient(farthest-side at left,transparent calc(100% - 15px),#fff 0) right/100px 100%;
mask-repeat:no-repeat;
}
.alt {
margin-top: -165px;
transform: scaleX(-1);
transform-origin: 170px 0;
animation-direction: alternate-reverse;
}
.box:before {
content: "";
position: absolute;
width: 90px;
height: 90px;
background: blue;
bottom: -40px;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -20px;
}
.alt:before {
animation-direction: alternate-reverse;
}
@keyframes loop {
15% {
transform: translateX(180px) rotate(0deg);
}
40% {
transform: translateX(180px) rotate(-180deg);
}
50%,100% {
transform: translateX(125px) rotate(-180deg);
}
}
@keyframes hide {
0%,50% {
visibility: visible;
}
50.2%,100% {
visibility: hidden;
}
}
body {
background:linear-gradient(to right,yellow,gray);
}
<div class="box"></div>
<div class="box alt"></div>
另一个具有较少像素值和 CSS 变量的版本,您可以在其中轻松调整所有内容。
运行 完整页面上的片段,并与所有杯垫一起玩得开心!
.box {
--w:400px; /* Total width of the coaster */
--h:180px; /* Height of the coaster */
--b:90px; /* width of the small bar */
--t:15px; /* height of the small bar */
--c:blue; /* Color of the small bar */
width:var(--w);
height:var(--h);
}
.box > div {
height: 100%;
position:relative;
width: calc(50% + var(--h)/2 + var(--b)/2);
border-radius: 0 1000px 1000px 0;
animation: hide 3s infinite linear alternate;
-webkit-mask:
linear-gradient(#fff,#fff) top left /calc(100% - var(--h)/2) var(--t),
linear-gradient(#fff,#fff) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),#fff 0) right/calc(var(--h)/2) 100%;
-webkit-mask-repeat:no-repeat;
mask:
linear-gradient(#fff,#fff) top left /calc(100% - var(--h)/2) var(--t),
linear-gradient(#fff,#fff) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),#fff 0) right/calc(var(--h)/2) 100%;
mask-repeat:no-repeat;
}
.box > div:last-child {
margin-top:calc(-1*var(--h));
margin-left:auto;
transform: scaleX(-1);
animation-direction: alternate-reverse;
}
.box > div:before {
content: "";
position: absolute;
width: var(--b);
height: 50%;
background: var(--c);
bottom: -25%;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -50%;
}
.box > div:last-child:before {
animation-direction: alternate-reverse;
}
@keyframes loop {
15% {
transform: translateX(calc(var(--w)/2)) rotate(0deg);
}
40% {
transform: translateX(calc(var(--w)/2)) rotate(-180deg);
}
50%,100% {
transform: translateX(calc(var(--w)/2 - var(--b)/2)) rotate(-180deg);
}
}
@keyframes hide {
50% {
visibility: visible;
}
50.1%,100% {
visibility: hidden;
}
}
body {
background:linear-gradient(to right,yellow,gray);
}
<div class="box">
<div></div><div></div>
</div>
<div class="box" style="--w:500px;--h:80px;--b:50px;--c:red;--t:5px">
<div></div><div></div>
</div>
<div class="box" style="--w:90vw;--h:200px;--b:100px;--c:purple;--t:20px">
<div></div><div></div>
</div>
为了理解技巧,让我们移除遮罩并用简单的渐变替换它并移除隐藏动画:
.box {
--w:400px; /* Total width of the coaster */
--h:180px; /* Height of the coaster */
--b:90px; /* width of the small bar */
--t:15px; /* height of the small bar */
--c:blue; /* Color of the small bar */
width:var(--w);
height:var(--h);
}
.box > div {
height: 100%;
position:relative;
width: calc(50% + var(--h)/2 + var(--b)/2);
border-radius: 0 1000px 1000px 0;
/*animation: hide 3s infinite linear alternate;*/
background:
linear-gradient(red,red) top left /calc(100% - var(--h)/2) var(--t),
linear-gradient(green,green) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),black 0) right/calc(var(--h)/2) 100%;
background-repeat:no-repeat;
}
.box > div:last-child {
margin-top:calc(-1*var(--h));
margin-left:auto;
transform: scaleX(-1);
animation-direction: alternate-reverse;
}
.box > div:before {
content: "";
position: absolute;
width: var(--b);
height: 50%;
background: var(--c);
bottom: -25%;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -50%;
}
.box > div:last-child:before {
animation-direction: alternate-reverse;
background:purple;
}
@keyframes loop {
15% {
transform: translateX(calc(var(--w)/2)) rotate(0deg);
}
40% {
transform: translateX(calc(var(--w)/2)) rotate(-180deg);
}
50%,100% {
transform: translateX(calc(var(--w)/2 - var(--b)/2)) rotate(-180deg);
}
}
@keyframes hide {
0%,50% {
visibility: visible;
}
50.2%,100% {
visibility: hidden;
}
}
body {
background:linear-gradient(to right,yellow,gray);
}
<div class="box">
<div></div><div></div>
</div>
渐变创建的路径是我们的蒙版,我们只会看到那部分。然后我们让我们的矩形跟随路径,技巧是有两个对称元素来创建循环效果。 隐藏动画将使我们只能看到其中一个,完美重叠将创建一个连续的动画。
这是一个版本,以防你想要一个圆圈作为杯垫
.box {
--w:400px; /* Total width of the coaster */
--h:180px; /* Height of the coaster */
--b:90px; /* width of the small bar */
--t:15px; /* height of the small bar */
--c:blue; /* Color of the small bar */
width:var(--w);
height:var(--h);
}
.box > div {
height: 100%;
position:relative;
width: calc(50% + var(--h)/2);
border-radius: 0 1000px 1000px 0;
animation: hide 3s infinite linear alternate;
-webkit-mask:
radial-gradient(farthest-side at bottom right,transparent calc(100% - var(--t)),#fff 0 100%,transparent 100%) top 0 right calc(var(--h)/2)/calc(var(--h)/2) 50%,
linear-gradient(#fff,#fff) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),#fff 0) right/calc(var(--h)/2) 100%;
-webkit-mask-repeat:no-repeat;
mask:
radial-gradient(farthest-side at bottom right,transparent calc(100% - var(--t)),#fff 0 100%,transparent 100%) top 0 right calc(var(--h)/2)/calc(var(--h)/2) 50%,
linear-gradient(#fff,#fff) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),#fff 0) right/calc(var(--h)/2) 100%;
mask-repeat:no-repeat;
}
.box > div:last-child {
margin-top:calc(-1*var(--h));
margin-left:auto;
transform: scaleX(-1);
animation-direction: alternate-reverse;
}
.box > div:before {
content: "";
position: absolute;
width: var(--b);
height: 50%;
background: var(--c);
bottom: -25%;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -50%;
}
.box > div:last-child:before {
animation-direction: alternate-reverse;
}
@keyframes loop {
15% {
transform: translateX(calc(var(--w)/2 - var(--b)/2)) rotate(0deg);
}
50%,100% {
transform: translateX(calc(var(--w)/2 - var(--b)/2)) rotate(-180deg);
}
}
@keyframes hide {
50% {
visibility: visible;
}
50.1%,100% {
visibility: hidden;
}
}
body {
background:linear-gradient(to right,yellow,gray);
}
<div class="box">
<div></div><div></div>
</div>
<div class="box" style="--w:500px;--h:80px;--b:50px;--c:red;--t:5px">
<div></div><div></div>
</div>
<div class="box" style="--w:90vw;--h:200px;--b:100px;--c:purple;--t:20px">
<div></div><div></div>
</div>
看起来不太自然,但 border-radius
似乎是一个很好的开始方式:
#container {
width: 200px;
height: 300px;
margin-top: 50px;
position: relative;
animation: 10s infinite loop;
animation-timing-function: linear;
}
#coaster {
width: 100px;
height: 10px;
background: lightblue;
position: absolute;
bottom: 0;
left: 1px;
right: 1px;
margin: 0 auto;
animation: 10s infinite coasterAnimation;
}
@keyframes loop {
from {
margin-left: -200px;
}
30% {
margin-left: calc(50% - 75px);
transform: rotate(0deg);
}
60% {
margin-left: calc(50% - 125px);
transform: rotate(-360deg);
}
to {
transform: rotate(-360deg);
margin-left: 100%;
}
}
@keyframes coasterAnimation {
29% {
border-radius: 0;
}
30% {
border-radius: 0 0 50% 50%;
}
59% {
border-radius: 0 0 50% 50%;
}
60% {
border-radius: 0;
}
70% {
border-radius: 0;
}
}
<div id="container">
<div id="coaster"></div>
</div>
我认为下面的方法或多或少是合理的(尽管我是第一个同意这种匆忙实施并不完美的人)。
过山车不是由 <div>
表示,而是由 <div>
的 border-bottom
表示。
在该边框自己的同步动画中,border-bottom-left-radius
和 border-bottom-left-radius
随着时间的推移弯曲到 50%
,然后再弯曲回 0
。
工作示例:
#container {
width: 180px;
height: 180px;
position: relative;
animation: 10s loop-container linear infinite;
}
#coaster {
width: 180px;
height: 180px;
border-bottom: 10px solid lightblue;
position: absolute;
bottom: 0;
left: 1px;
right: 1px;
margin: 0 auto;
animation: 10s loop-coaster linear infinite;
}
@keyframes loop-container {
0% {
margin-left: -200px;
}
30% {
margin-left: calc(50% - 75px);
transform: rotate(0deg);
}
60% {
margin-left: calc(50% - 125px);
transform: rotate(-360deg);
}
100% {
transform: rotate(-360deg);
margin-left: 100%;
}
}
@keyframes loop-coaster {
30% {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
31% {
border-bottom-left-radius: 0;
border-bottom-right-radius: 25%;
}
35%, 55% {
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
}
59% {
border-bottom-left-radius: 25%;
border-bottom-right-radius: 0;
}
60% {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
<div id="container">
<div id="coaster"></div>
</div>
谢谢大家,尤其是 Temani Afif 的启发:]
我最终使用 border-radius
、隐藏元素和更多 HTML 结合了您的许多答案 HTML 我认为我为动画创建了一个很好的解决方案。
* {
box-sizing: border-box;
}
#container {
width: 100px;
height: 100px;
margin-top: 50px;
position: relative;
animation: 5s infinite loop linear;
}
#coasterLine {
height: 10px;
background: lightblue;
position: absolute;
z-index: 20;
bottom: 0;
animation: 5s infinite c-line linear;
}
#coasterRound {
width: 100%;
height: 100%;
border-radius: 50%;
border: solid transparent 10px;
border-bottom: solid lightblue 10px;
position: relative;
animation: 5s infinite c-round linear;
}
#whiteScreen {
width: 100%;
background: white;
position: absolute;
z-index: 10;
top: 0;
animation: 5s infinite white-screen linear;
}
@keyframes loop {
0% {
margin-left: -200px;
}
38%,
43% {
margin-left: calc(50% - 50px);
}
58%,
63% {
margin-left: calc(50% - 50px);
}
100% {
margin-left: 100%;
}
}
@keyframes c-round {
0%,
43% {
transform: rotate(-45deg);
}
58%,
100% {
transform: rotate(-315deg);
}
}
@keyframes c-line {
0%,
38% {
left: 0;
width: 60px;
}
43% {
left: 50px;
width: 0;
}
58% {
left: 40px;
width: 0;
}
63%,
100% {
left: 40px;
width: 60px;
}
}
@keyframes white-screen {
0%,
38% {
height: 100%;
transform: rotate(0deg);
}
43% {
height: 50%;
transform: rotate(0deg);
}
44%,
57% {
height: 0;
transform: rotate(0deg);
}
58% {
height: 50%;
transform: rotate(180deg);
}
63%,
100% {
height: 100%;
transform: rotate(180deg);
}
}
<div id="container">
<div id="coasterLine"></div>
<div id="coasterRound"></div>
<div id="whiteScreen"></div>
</div>
太棒了!
我正在尝试使用 CSS 创建过山车风格的动画。
我想知道 "coaster" 在循环阶段如何弯曲。
我正在寻找一个完整的 CSS 解决方案,但如果需要一点点 JavaScript 我可以接受。
到目前为止我的代码:
#container {
width: 200px;
height: 300px;
margin-top: 50px;
position: relative;
animation: 10s infinite loop;
animation-timing-function: linear;
}
#coaster {
width: 100px;
height: 10px;
background: lightblue;
position: absolute;
bottom: 0;
left: 1px;
right: 1px;
margin: 0 auto;
}
@keyframes loop {
from {
margin-left: -200px;
}
30% {
margin-left: calc(50% - 75px);
transform: rotate(0deg);
}
60% {
margin-left: calc(50% - 125px);
transform: rotate(-360deg);
}
to {
transform: rotate(-360deg);
margin-left: 100%;
}
}
<div id="container">
<div id="coaster"></div>
</div>
可以考虑border-radius
。这是您可以改进的基本示例
.box {
height:100px;
margin-top:50px;
border:10px solid transparent;
border-bottom:10px solid red;
width:100px;
animation:
loop 5s infinite alternate linear,
radius 5s infinite alternate linear;
}
@keyframes loop{
0% {
transform:translateX(0);
}
40% {
transform:translateX(100px) rotate(0deg);
}
60% {
transform:translateX(100px) rotate(-360deg);
}
100% {
transform:translateX(200px) rotate(-360deg);
}
}
@keyframes radius{
0%,28% {
border-radius:0%
}
35%,38% {
border-bottom-right-radius:50%;
border-bottom-left-radius:0%;
}
45%,55% {
border-radius:50%
}
62%,65% {
border-bottom-right-radius:0%;
border-bottom-left-radius:50%;
}
70%,100% {
border-radius:0%
}
}
<div class="box"></div>
另一个疯狂想法,您可以在透明弯曲路径后面为矩形形状设置动画:
.box {
height: 165px;
width: 315px;
position: relative;
border-radius: 0 100px 100px 0;
animation: hide 3s infinite linear alternate;
overflow:hidden;
}
.box:after {
content: "";
display:block;
height: 100%;
background: white padding-box;
border-radius: inherit;
border: 15px solid transparent;
border-left:0;
box-sizing: border-box;
}
.alt {
margin-top: -165px;
transform: scaleX(-1);
transform-origin: 170px 0;
animation-direction: alternate-reverse;
}
.box:before {
content: "";
position: absolute;
z-index: -1;
width: 90px;
height: 90px;
background: blue;
bottom: -40px;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -20px;
}
.alt:before {
animation-direction: alternate-reverse;
}
@keyframes loop {
15% {
transform: translateX(180px) rotate(0deg);
}
40% {
transform: translateX(180px) rotate(-180deg);
}
50%,100% {
transform: translateX(125px) rotate(-180deg);
}
}
@keyframes hide {
0%,50% {
visibility: visible;
}
50.2%,100% {
visibility: hidden;
}
}
<div class="box"></div>
<div class="box alt"></div>
代码少透明的优化版本(会考虑mask
)
.box {
height: 165px;
width: 315px;
position: relative;
border-radius: 0 100px 100px 0;
animation: hide 3s infinite linear alternate;
overflow:hidden;
-webkit-mask:
linear-gradient(#fff,#fff) top left /235px 15px,
linear-gradient(#fff,#fff) bottom left/235px 15px,
radial-gradient(farthest-side at left,transparent calc(100% - 15px),#fff 0) right/100px 100%;
-webkit-mask-repeat:no-repeat;
mask:
linear-gradient(#fff,#fff) top left/235px 15px,
linear-gradient(#fff,#fff) bottom left/235px 15px,
radial-gradient(farthest-side at left,transparent calc(100% - 15px),#fff 0) right/100px 100%;
mask-repeat:no-repeat;
}
.alt {
margin-top: -165px;
transform: scaleX(-1);
transform-origin: 170px 0;
animation-direction: alternate-reverse;
}
.box:before {
content: "";
position: absolute;
width: 90px;
height: 90px;
background: blue;
bottom: -40px;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -20px;
}
.alt:before {
animation-direction: alternate-reverse;
}
@keyframes loop {
15% {
transform: translateX(180px) rotate(0deg);
}
40% {
transform: translateX(180px) rotate(-180deg);
}
50%,100% {
transform: translateX(125px) rotate(-180deg);
}
}
@keyframes hide {
0%,50% {
visibility: visible;
}
50.2%,100% {
visibility: hidden;
}
}
body {
background:linear-gradient(to right,yellow,gray);
}
<div class="box"></div>
<div class="box alt"></div>
另一个具有较少像素值和 CSS 变量的版本,您可以在其中轻松调整所有内容。
运行 完整页面上的片段,并与所有杯垫一起玩得开心!
.box {
--w:400px; /* Total width of the coaster */
--h:180px; /* Height of the coaster */
--b:90px; /* width of the small bar */
--t:15px; /* height of the small bar */
--c:blue; /* Color of the small bar */
width:var(--w);
height:var(--h);
}
.box > div {
height: 100%;
position:relative;
width: calc(50% + var(--h)/2 + var(--b)/2);
border-radius: 0 1000px 1000px 0;
animation: hide 3s infinite linear alternate;
-webkit-mask:
linear-gradient(#fff,#fff) top left /calc(100% - var(--h)/2) var(--t),
linear-gradient(#fff,#fff) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),#fff 0) right/calc(var(--h)/2) 100%;
-webkit-mask-repeat:no-repeat;
mask:
linear-gradient(#fff,#fff) top left /calc(100% - var(--h)/2) var(--t),
linear-gradient(#fff,#fff) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),#fff 0) right/calc(var(--h)/2) 100%;
mask-repeat:no-repeat;
}
.box > div:last-child {
margin-top:calc(-1*var(--h));
margin-left:auto;
transform: scaleX(-1);
animation-direction: alternate-reverse;
}
.box > div:before {
content: "";
position: absolute;
width: var(--b);
height: 50%;
background: var(--c);
bottom: -25%;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -50%;
}
.box > div:last-child:before {
animation-direction: alternate-reverse;
}
@keyframes loop {
15% {
transform: translateX(calc(var(--w)/2)) rotate(0deg);
}
40% {
transform: translateX(calc(var(--w)/2)) rotate(-180deg);
}
50%,100% {
transform: translateX(calc(var(--w)/2 - var(--b)/2)) rotate(-180deg);
}
}
@keyframes hide {
50% {
visibility: visible;
}
50.1%,100% {
visibility: hidden;
}
}
body {
background:linear-gradient(to right,yellow,gray);
}
<div class="box">
<div></div><div></div>
</div>
<div class="box" style="--w:500px;--h:80px;--b:50px;--c:red;--t:5px">
<div></div><div></div>
</div>
<div class="box" style="--w:90vw;--h:200px;--b:100px;--c:purple;--t:20px">
<div></div><div></div>
</div>
为了理解技巧,让我们移除遮罩并用简单的渐变替换它并移除隐藏动画:
.box {
--w:400px; /* Total width of the coaster */
--h:180px; /* Height of the coaster */
--b:90px; /* width of the small bar */
--t:15px; /* height of the small bar */
--c:blue; /* Color of the small bar */
width:var(--w);
height:var(--h);
}
.box > div {
height: 100%;
position:relative;
width: calc(50% + var(--h)/2 + var(--b)/2);
border-radius: 0 1000px 1000px 0;
/*animation: hide 3s infinite linear alternate;*/
background:
linear-gradient(red,red) top left /calc(100% - var(--h)/2) var(--t),
linear-gradient(green,green) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),black 0) right/calc(var(--h)/2) 100%;
background-repeat:no-repeat;
}
.box > div:last-child {
margin-top:calc(-1*var(--h));
margin-left:auto;
transform: scaleX(-1);
animation-direction: alternate-reverse;
}
.box > div:before {
content: "";
position: absolute;
width: var(--b);
height: 50%;
background: var(--c);
bottom: -25%;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -50%;
}
.box > div:last-child:before {
animation-direction: alternate-reverse;
background:purple;
}
@keyframes loop {
15% {
transform: translateX(calc(var(--w)/2)) rotate(0deg);
}
40% {
transform: translateX(calc(var(--w)/2)) rotate(-180deg);
}
50%,100% {
transform: translateX(calc(var(--w)/2 - var(--b)/2)) rotate(-180deg);
}
}
@keyframes hide {
0%,50% {
visibility: visible;
}
50.2%,100% {
visibility: hidden;
}
}
body {
background:linear-gradient(to right,yellow,gray);
}
<div class="box">
<div></div><div></div>
</div>
渐变创建的路径是我们的蒙版,我们只会看到那部分。然后我们让我们的矩形跟随路径,技巧是有两个对称元素来创建循环效果。 隐藏动画将使我们只能看到其中一个,完美重叠将创建一个连续的动画。
这是一个版本,以防你想要一个圆圈作为杯垫
.box {
--w:400px; /* Total width of the coaster */
--h:180px; /* Height of the coaster */
--b:90px; /* width of the small bar */
--t:15px; /* height of the small bar */
--c:blue; /* Color of the small bar */
width:var(--w);
height:var(--h);
}
.box > div {
height: 100%;
position:relative;
width: calc(50% + var(--h)/2);
border-radius: 0 1000px 1000px 0;
animation: hide 3s infinite linear alternate;
-webkit-mask:
radial-gradient(farthest-side at bottom right,transparent calc(100% - var(--t)),#fff 0 100%,transparent 100%) top 0 right calc(var(--h)/2)/calc(var(--h)/2) 50%,
linear-gradient(#fff,#fff) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),#fff 0) right/calc(var(--h)/2) 100%;
-webkit-mask-repeat:no-repeat;
mask:
radial-gradient(farthest-side at bottom right,transparent calc(100% - var(--t)),#fff 0 100%,transparent 100%) top 0 right calc(var(--h)/2)/calc(var(--h)/2) 50%,
linear-gradient(#fff,#fff) bottom left/calc(100% - var(--h)/2) var(--t),
radial-gradient(farthest-side at left,transparent calc(100% - var(--t)),#fff 0) right/calc(var(--h)/2) 100%;
mask-repeat:no-repeat;
}
.box > div:last-child {
margin-top:calc(-1*var(--h));
margin-left:auto;
transform: scaleX(-1);
animation-direction: alternate-reverse;
}
.box > div:before {
content: "";
position: absolute;
width: var(--b);
height: 50%;
background: var(--c);
bottom: -25%;
animation: loop 3s infinite linear alternate;
transform-origin: 50% -50%;
}
.box > div:last-child:before {
animation-direction: alternate-reverse;
}
@keyframes loop {
15% {
transform: translateX(calc(var(--w)/2 - var(--b)/2)) rotate(0deg);
}
50%,100% {
transform: translateX(calc(var(--w)/2 - var(--b)/2)) rotate(-180deg);
}
}
@keyframes hide {
50% {
visibility: visible;
}
50.1%,100% {
visibility: hidden;
}
}
body {
background:linear-gradient(to right,yellow,gray);
}
<div class="box">
<div></div><div></div>
</div>
<div class="box" style="--w:500px;--h:80px;--b:50px;--c:red;--t:5px">
<div></div><div></div>
</div>
<div class="box" style="--w:90vw;--h:200px;--b:100px;--c:purple;--t:20px">
<div></div><div></div>
</div>
看起来不太自然,但 border-radius
似乎是一个很好的开始方式:
#container {
width: 200px;
height: 300px;
margin-top: 50px;
position: relative;
animation: 10s infinite loop;
animation-timing-function: linear;
}
#coaster {
width: 100px;
height: 10px;
background: lightblue;
position: absolute;
bottom: 0;
left: 1px;
right: 1px;
margin: 0 auto;
animation: 10s infinite coasterAnimation;
}
@keyframes loop {
from {
margin-left: -200px;
}
30% {
margin-left: calc(50% - 75px);
transform: rotate(0deg);
}
60% {
margin-left: calc(50% - 125px);
transform: rotate(-360deg);
}
to {
transform: rotate(-360deg);
margin-left: 100%;
}
}
@keyframes coasterAnimation {
29% {
border-radius: 0;
}
30% {
border-radius: 0 0 50% 50%;
}
59% {
border-radius: 0 0 50% 50%;
}
60% {
border-radius: 0;
}
70% {
border-radius: 0;
}
}
<div id="container">
<div id="coaster"></div>
</div>
我认为下面的方法或多或少是合理的(尽管我是第一个同意这种匆忙实施并不完美的人)。
过山车不是由 <div>
表示,而是由 <div>
的 border-bottom
表示。
在该边框自己的同步动画中,border-bottom-left-radius
和 border-bottom-left-radius
随着时间的推移弯曲到 50%
,然后再弯曲回 0
。
工作示例:
#container {
width: 180px;
height: 180px;
position: relative;
animation: 10s loop-container linear infinite;
}
#coaster {
width: 180px;
height: 180px;
border-bottom: 10px solid lightblue;
position: absolute;
bottom: 0;
left: 1px;
right: 1px;
margin: 0 auto;
animation: 10s loop-coaster linear infinite;
}
@keyframes loop-container {
0% {
margin-left: -200px;
}
30% {
margin-left: calc(50% - 75px);
transform: rotate(0deg);
}
60% {
margin-left: calc(50% - 125px);
transform: rotate(-360deg);
}
100% {
transform: rotate(-360deg);
margin-left: 100%;
}
}
@keyframes loop-coaster {
30% {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
31% {
border-bottom-left-radius: 0;
border-bottom-right-radius: 25%;
}
35%, 55% {
border-bottom-left-radius: 50%;
border-bottom-right-radius: 50%;
}
59% {
border-bottom-left-radius: 25%;
border-bottom-right-radius: 0;
}
60% {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
<div id="container">
<div id="coaster"></div>
</div>
谢谢大家,尤其是 Temani Afif 的启发:]
我最终使用 border-radius
、隐藏元素和更多 HTML 结合了您的许多答案 HTML 我认为我为动画创建了一个很好的解决方案。
* {
box-sizing: border-box;
}
#container {
width: 100px;
height: 100px;
margin-top: 50px;
position: relative;
animation: 5s infinite loop linear;
}
#coasterLine {
height: 10px;
background: lightblue;
position: absolute;
z-index: 20;
bottom: 0;
animation: 5s infinite c-line linear;
}
#coasterRound {
width: 100%;
height: 100%;
border-radius: 50%;
border: solid transparent 10px;
border-bottom: solid lightblue 10px;
position: relative;
animation: 5s infinite c-round linear;
}
#whiteScreen {
width: 100%;
background: white;
position: absolute;
z-index: 10;
top: 0;
animation: 5s infinite white-screen linear;
}
@keyframes loop {
0% {
margin-left: -200px;
}
38%,
43% {
margin-left: calc(50% - 50px);
}
58%,
63% {
margin-left: calc(50% - 50px);
}
100% {
margin-left: 100%;
}
}
@keyframes c-round {
0%,
43% {
transform: rotate(-45deg);
}
58%,
100% {
transform: rotate(-315deg);
}
}
@keyframes c-line {
0%,
38% {
left: 0;
width: 60px;
}
43% {
left: 50px;
width: 0;
}
58% {
left: 40px;
width: 0;
}
63%,
100% {
left: 40px;
width: 60px;
}
}
@keyframes white-screen {
0%,
38% {
height: 100%;
transform: rotate(0deg);
}
43% {
height: 50%;
transform: rotate(0deg);
}
44%,
57% {
height: 0;
transform: rotate(0deg);
}
58% {
height: 50%;
transform: rotate(180deg);
}
63%,
100% {
height: 100%;
transform: rotate(180deg);
}
}
<div id="container">
<div id="coasterLine"></div>
<div id="coasterRound"></div>
<div id="whiteScreen"></div>
</div>
太棒了!