如何在CSS“过山车”动画中弯曲一条线?

问题描述 投票:8回答:5

我正在尝试使用CSS创建过山车风格的动画。

我想知道在循环阶段时如何弯曲“过山车”。

我正在寻找所有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>
html css css-animations css-transforms
5个回答
6
投票

您可以考虑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>

另一个crazy想法,您可以在透明弯曲路径后面设置矩形形状的动画:

.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]

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);
}

另一个像素值和CSS变量较少的版本,您可以在其中轻松调整所有内容。

在整个页面上运行摘要,并与所有杯垫玩得开心!

<div class="box"></div>

<div class="box alt"></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;
  -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 {
  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>

<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);
}

由渐变创建的路径是我们的蒙版,我们将仅看到该部分。然后,使我们的矩形遵循路径,诀窍是使用两个对称元素来创建循环效果。 hide动画将使我们只能看到其中一个,而完美的重叠将创建一个连续的动画。


这里是一个版本,如果您想为杯垫画个圆圈

<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 {
  0%,50% {
    visibility: visible;
  }
  50.2%,100% {
    visibility: hidden;
  }
}

body {
  background:linear-gradient(to right,yellow,gray);
}

3
投票

您可以尝试在CSS中使用<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>。>>

检查此示例:offset-path

https://codepen.io/michellebarker/pen/XWJyydY
Splitting({
  whitespace: true
})
@import url('https://fonts.googleapis.com/css?family=Nova+Mono&display=swap');
* {
  box-sizing: border-box;
}

body {
  min-height: 100vh;
  background-color: #18181c;
  display: flex;
  align-items: center;
  justify-content: center;
}

h1 {
  font-family: 'Nova Mono', monospace;
  font-size: 2.5rem;
  text-transform: uppercase;
  width: 1109px;
  height: 365px;
  color: turquoise;
}

.char {
  offset-path: path('M.2 219.6c247-107 233.3 91.4 406.4 138.4C659.2 426.6 750.2 6.6 552.2.6 337.7-5.9 426.9 415 696.1 359.4c256.1-52.9 182.1-217.9 413.1-163.9');
  offset-distance: calc(var(--char-index) * 1.5rem);
  position: absolute;
  animation: loop 3500ms cubic-bezier(.62, .01, .42, 1.01) infinite alternate calc(var(--char-index) * 10ms);
}

@keyframes loop {
  50% {
    offset-distance: calc((var(--char-index) * 2.5rem) + 700px);
    color: hotpink;
  }
  100% {
    offset-distance: calc((var(--char-index) * 1.5rem) + 1690px);
  }
}

2
投票

谢谢大家,尤其是Temani Afif


1
投票

看上去不是很自然,但是<div id="container"> <div id="coasterLine"></div> <div id="coasterRound"></div> <div id="whiteScreen"></div> </div>似乎是一个很好的起点:


1
投票

我认为下面的方法或多或少是合理的(尽管我是第一个同意这种匆忙的实现并不完美的人。)>

不是由<div id="container"> <div id="coaster"></div> </div>表示过山车,而是由<div>border-bottom表示。

© www.soinside.com 2019 - 2024. All rights reserved.