ฉันจะสร้างเส้นโค้งในภาพเคลื่อนไหว CSS“ รถไฟเหาะ” ได้อย่างไร?


15

ฉันกำลังพยายามสร้างภาพเคลื่อนไหวสไตล์รถไฟเหาะด้วย 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>

คำตอบ:


12

border-radiusคุณสามารถพิจารณา นี่คือตัวอย่างพื้นฐานที่คุณสามารถปรับปรุงได้

ความคิดที่บ้าอีกอย่างหนึ่งที่คุณสามารถทำให้รูปร่างเป็นรูปสี่เหลี่ยมผืนผ้าอยู่ด้านหลังเส้นทางโค้งโปร่งใส:

รุ่นที่ได้รับการปรับปรุงให้ดีที่สุดโดยมีโค้ดน้อยและมีความโปร่งใส (จะพิจารณาmaskในเรื่องนี้)

อีกรุ่นที่มีค่าพิกเซลน้อยลงและตัวแปร 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>


เพื่อให้เข้าใจถึงเคล็ดลับลองเอามาสก์ออกและแทนที่ด้วยการไล่ระดับสีอย่างง่ายและลบภาพเคลื่อนไหว hide:

เส้นทางที่สร้างโดยการไล่ระดับสีคือหน้ากากของเราและเราจะเห็นเฉพาะส่วนนั้น จากนั้นเราทำให้สี่เหลี่ยมผืนผ้าของเราไปตามเส้นทางและเคล็ดลับคือการมีองค์ประกอบสมมาตรสององค์ประกอบเพื่อสร้างเอฟเฟกต์ลูป การซ่อนภาพเคลื่อนไหวจะทำให้เราเห็นเพียงภาพเดียวเท่านั้นและการซ้อนทับที่สมบูรณ์แบบจะสร้างภาพเคลื่อนไหวต่อเนื่อง


นี่คือรุ่นในกรณีที่คุณต้องการวงกลมสำหรับรถไฟเหาะ

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


4

ขอบคุณทุกคนโดยเฉพาะTemani Afifสำหรับแรงบันดาลใจของคุณ:]

ในที่สุดฉันก็รวมคำตอบของคุณเข้าด้วยกันโดยใช้การborder-radiusซ่อนองค์ประกอบและ 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>

มันยอดเยี่ยมมาก!


0

ดูไม่เป็นธรรมชาติจริงๆ แต่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>


0

ฉันคิดว่าวิธีการด้านล่างเป็นเสียงที่มากขึ้นหรือน้อยลง (แม้ว่าฉันจะเป็นคนแรกที่เห็นด้วยว่าการดำเนินการที่รวดเร็วนี้ไม่ได้สมบูรณ์แบบ)

แทนการรถไฟเหาะตีลังกาที่ถูกแทนด้วย<div>มันเป็นตัวแทนจากborder-bottomของ<div>ของ

ในแอนิเมชันพร้อมกันของเส้นขอบนั้น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>

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.