Bagaimana saya bisa melengkung garis dalam animasi "roller coaster" CSS?

15

Saya mencoba membuat animasi gaya roller coaster dengan CSS.

Saya ingin tahu bagaimana cara melengkung "coaster" ketika itu dalam tahap loop.

Saya sedang mencari solusi semua CSS tetapi jika perlu sedikit JavaScript saya setuju dengan itu.

kode saya sejauh ini:

#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>

Matan Sanbira
sumber

Jawaban:

12

Anda bisa mempertimbangkannya border-radius. Ini adalah contoh dasar yang bisa Anda tingkatkan

Gagasan gila lain di mana Anda dapat menghidupkan bentuk persegi panjang di belakang jalur melengkung transparan:

Versi yang dioptimalkan dengan lebih sedikit kode dan dengan transparansi (akan mempertimbangkan maskini)

Versi lain dengan nilai piksel dan variabel CSS yang lebih sedikit di mana Anda dapat dengan mudah mengatur semuanya.

Jalankan cuplikan di halaman penuh dan bersenang-senang dengan semua coaster!

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


Untuk memahami triknya, mari kita hapus topeng dan ganti dengan gradien sederhana dan hapus animasi sembunyikan:

Path yang dibuat oleh gradient adalah mask kita dan kita hanya akan melihat bagian itu. Kemudian kita membuat persegi panjang kita untuk mengikuti jalan dan triknya adalah memiliki dua elemen simetris untuk membuat efek loop. The menyembunyikan animasi akan memungkinkan kita untuk melihat hanya salah satu dari mereka dan tumpang tindih yang sempurna akan membuat animasi terus menerus.


Ini adalah versi jika Anda ingin lingkaran untuk coaster

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

Temani Afif
sumber
4

terima kasih semua, dan terutama Temani Afif atas ilham Anda:]

Saya akhirnya menggabungkan banyak jawaban Anda menggunakan border-radius, menyembunyikan elemen dan sedikit lebih banyak HTML. Saya pikir saya menciptakan solusi hebat untuk animasi.

* {
  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>

ini sangat bagus!

Matan Sanbira
sumber
0

Tidak terlihat alami tetapi border-radiustampaknya cara yang baik untuk memulai:

#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>

Johannchopin
sumber
0

Saya pikir pendekatan di bawah ini lebih atau kurang masuk akal (meskipun saya akan menjadi yang pertama untuk setuju bahwa implementasi terburu-buru ini tidak sempurna).

Alih-alih rollercoaster diwakili oleh <div>itu diwakili oleh border-bottoma <div>.

Dalam animasi simultan perbatasan itu sendiri, the border-bottom-left-radiusdan border-bottom-left-radiustikungan ke 50%waktu, sebelum membungkuk ke 0.

Contoh kerja:

#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>

Rounin
sumber