การแฮ็กสถานะภาพเคลื่อนไหว CSS และเวลาเล่น PlatoBlockchain Data Intelligence ค้นหาแนวตั้ง AI.

การแฮ็กสถานะภาพเคลื่อนไหว CSS และเวลาเล่น

Wolfenstein แบบ CSS เท่านั้น เป็นโครงการเล็กๆ ที่ฉันทำเมื่อไม่กี่สัปดาห์ก่อน เป็นการทดลองกับการแปลงและภาพเคลื่อนไหว CSS 3D

แรงบันดาลใจจาก สาธิต FPS และอีก Wolfenstein รหัสปากกาฉันตัดสินใจสร้างเวอร์ชันของตัวเอง มีพื้นฐานมาจากตอนที่ 1 – ชั้น 9 ของเกม Wolfenstein 3D ดั้งเดิม

ตัวแก้ไข: เกมนี้ต้องการการตอบสนองอย่างรวดเร็วโดยเจตนาเพื่อหลีกเลี่ยงหน้าจอ Game Over

นี่คือวิดีโอการเล่นผ่าน:

[เนื้อหาฝัง]

โดยสรุป โปรเจ็กต์ของฉันไม่ได้เป็นเพียงแอนิเมชั่น CSS ยาวๆ ที่เขียนสคริปต์อย่างระมัดระวัง บวกกับบางกรณีของ ช่องทำเครื่องหมาย hack.

:checked ~ div { animation-name: spin; }

สภาพแวดล้อมประกอบด้วยใบหน้ากริด 3 มิติ และภาพเคลื่อนไหวส่วนใหญ่เป็นการแปลและหมุน 3 มิติธรรมดา ไม่มีอะไรแฟนซีจริงๆ

อย่างไรก็ตาม ปัญหาสองข้อนั้นแก้ไขได้ยากเป็นพิเศษ:

  • เล่นแอนิเมชั่น “การยิงอาวุธ” ทุกครั้งที่ผู้เล่นคลิกที่ศัตรู
  • เมื่อบอสที่เคลื่อนไหวเร็วถูกโจมตีครั้งสุดท้าย ให้เข้าสู่การเคลื่อนไหวช้าอย่างน่าทึ่ง

ในระดับเทคนิค นี่หมายถึง:

  • เล่นภาพเคลื่อนไหวซ้ำเมื่อเลือกช่องทำเครื่องหมายถัดไป
  • ทำให้ภาพเคลื่อนไหวช้าลงเมื่อเลือกช่องทำเครื่องหมาย

อันที่จริงแล้ว โปรเจ็กต์ของฉันไม่ได้รับการแก้ไขอย่างเหมาะสม! ฉันลงเอยด้วยการใช้วิธีแก้ปัญหาหรือเพียงแค่ยอมแพ้

ในทางกลับกัน หลังจากขุดค้นมาบ้าง ในที่สุดฉันก็พบกุญแจของปัญหาทั้งสอง นั่นคือ การเปลี่ยนแปลงคุณสมบัติของการรันแอนิเมชั่น CSS ในบทความนี้ เราจะสำรวจเพิ่มเติมในหัวข้อนี้:

  • ตัวอย่างแบบโต้ตอบมากมาย
  • Dissections: แต่ละตัวอย่างทำงานอย่างไร (หรือไม่ทำงาน)?
  • เบื้องหลัง: เบราว์เซอร์จัดการกับสถานะแอนิเมชั่นอย่างไร

ให้ฉัน “โยนอิฐของฉัน”.

ปัญหาที่ 1: การเล่นภาพเคลื่อนไหวซ้ำ

ตัวอย่างแรก: “แค่ช่องทำเครื่องหมายอื่น”

สัญชาตญาณแรกของฉันคือ "แค่เพิ่มช่องทำเครื่องหมายอื่น" ซึ่งใช้ไม่ได้:

ช่องทำเครื่องหมายแต่ละช่องทำงานแยกกันแต่ใช้ร่วมกันไม่ได้ หากมีการเลือกช่องทำเครื่องหมายหนึ่งช่อง ช่องอื่นจะไม่ทำงานอีกต่อไป

นี่คือวิธีการทำงาน (หรือ “ไม่ทำงาน”):

  1. พื้นที่ animation-name of <div> is none โดยค่าเริ่มต้น
  2. ผู้ใช้คลิกที่ช่องทำเครื่องหมายหนึ่งช่อง animation-name จะกลายเป็น spinและแอนิเมชั่นเริ่มต้นตั้งแต่ต้น
  3. หลังจากนั้นครู่หนึ่ง ผู้ใช้คลิกที่ช่องทำเครื่องหมายอื่น กฎ CSS ใหม่มีผลบังคับใช้ แต่ animation-name is ยังคง spinซึ่งหมายความว่าไม่มีการเพิ่มหรือลบภาพเคลื่อนไหว แอนิเมชั่นยังคงเล่นต่อไปราวกับว่าไม่มีอะไรเกิดขึ้น

ตัวอย่างที่สอง: “การโคลนแอนิเมชั่น”

แนวทางการทำงานวิธีหนึ่งคือการโคลนแอนิเมชัน:

#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2; }

นี่คือวิธีการทำงาน:

  1. animation-name is none ในขั้นต้น
  2. ผู้ใช้คลิกที่ “หมุน!” animation-name จะกลายเป็น spin1. แอนิเมชั่น spin1 เริ่มต้นจากจุดเริ่มต้นเพราะเพิ่งเพิ่มเข้ามา
  3. ผู้ใช้คลิกที่ "หมุนอีกครั้ง!", animation-name จะกลายเป็น spin2. แอนิเมชั่น spin2 เริ่มต้นจากจุดเริ่มต้นเพราะเพิ่งเพิ่มเข้ามา

โปรดทราบว่าในขั้นตอน #3 spin1 ถูกลบเนื่องจากลำดับของกฎ CSS จะไม่ทำงานหาก “หมุนอีกครั้ง!” จะถูกตรวจสอบก่อน

ตัวอย่างที่สาม: “ต่อท้ายแอนิเมชั่นเดียวกัน”

แนวทางการทำงานอีกวิธีหนึ่งคือ "ผนวกแอนิเมชั่นเดียวกัน":

#spin1:checked ~ div { animation-name: spin; }
#spin2:checked ~ div { animation-name: spin, spin; }

ซึ่งคล้ายกับตัวอย่างก่อนหน้านี้ คุณสามารถเข้าใจพฤติกรรมได้ด้วยวิธีนี้:

#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2, spin1; }

โปรดทราบว่าเมื่อ “หมุนอีกครั้ง!” เมื่อตรวจสอบแล้ว แอนิเมชั่นที่ทำงานอยู่แบบเก่าจะกลายเป็นแอนิเมชั่นที่สองในรายการใหม่ ซึ่งอาจใช้ไม่ได้สัญชาตญาณ ผลที่ตามมาโดยตรงคือ: เคล็ดลับจะไม่ทำงานถ้า animation-fill-mode is forwards. นี่คือตัวอย่าง:

หากคุณสงสัยว่าเหตุใดจึงเป็นเช่นนี้ นี่คือเงื่อนงำบางประการ:

  • animation-fill-mode is none โดยค่าเริ่มต้น ซึ่งหมายความว่า "ภาพเคลื่อนไหวจะไม่มีผลเลยหากไม่เล่น"
  • animation-fill-mode: forwards; หมายถึง “หลังจากเล่นแอนิเมชั่นเสร็จแล้ว อนิเมชั่นจะต้องอยู่ที่คีย์เฟรมสุดท้ายตลอดไป”
  • spin1การตัดสินใจของมักจะแทนที่ spin2เพราะ spin1 ปรากฏในรายการในภายหลัง
  • สมมติว่าผู้ใช้คลิก "หมุน!" รอจนครบหมุน จากนั้นคลิก "หมุนอีกครั้ง!" ณ ตอนนี้. spin1 เสร็จแล้วและ spin2 เพิ่งเริ่มต้น

การสนทนา

กฎทั่วไป: คุณไม่สามารถ "รีสตาร์ท" ภาพเคลื่อนไหว CSS ที่มีอยู่ได้ คุณต้องการเพิ่มและเล่นแอนิเมชั่นใหม่แทน ซึ่งอาจได้รับการยืนยันโดย ข้อมูลจำเพาะของ W3C:

เมื่อแอนิเมชั่นเริ่มต้นแล้ว แอนิเมชั่นจะดำเนินต่อไปจนกว่าจะสิ้นสุดหรือลบชื่อแอนิเมชั่นออก

เมื่อเปรียบเทียบสองตัวอย่างสุดท้าย ฉันคิดว่าในทางปฏิบัติ “การโคลนแอนิเมชั่น” มักจะทำงานได้ดีขึ้น โดยเฉพาะอย่างยิ่งเมื่อมีตัวประมวลผลล่วงหน้าของ CSS

ปัญหาที่ 2: สโลว์โมชั่น

บางคนอาจคิดว่าการทำให้แอนิเมชั่นช้าลงเป็นเพียงเรื่องของการตั้งค่าให้นานขึ้น animation-duration:

div { animation-duration: 0.5s; }
#slowmo:checked ~ div { animation-duration: 1.5s; }

อันที่จริงสิ่งนี้ใช้งานได้:

… หรือเปล่า?

ด้วยการปรับแต่งเล็กน้อย คุณจะเห็นปัญหาได้ง่ายขึ้น

ใช่ ภาพเคลื่อนไหวช้าลง และไม่มันดูไม่ดี สุนัข (เกือบ) "กระโดด" เสมอเมื่อคุณสลับช่องทำเครื่องหมาย นอกจากนี้ ดูเหมือนว่าสุนัขจะกระโดดไปยังตำแหน่งสุ่มแทนที่จะเป็นตำแหน่งเริ่มต้น มาได้ยังไง?

มันจะเข้าใจได้ง่ายขึ้นถ้าเราแนะนำ "องค์ประกอบเงา" สองอย่าง:

องค์ประกอบเงาทั้งสองกำลังเรียกใช้แอนิเมชั่นเดียวกันโดยมีความแตกต่างกัน animation-duration. และจะไม่ได้รับผลกระทบจากช่องทำเครื่องหมาย

เมื่อคุณสลับช่องทำเครื่องหมาย องค์ประกอบจะสลับระหว่างสถานะขององค์ประกอบเงาสององค์ประกอบในทันที

quoting ข้อมูลจำเพาะของ W3C:

การเปลี่ยนแปลงค่าของคุณสมบัติแอนิเมชั่นในขณะที่แอนิเมชั่นกำลังทำงานจะมีผลเหมือนกับว่าแอนิเมชั่นมีค่าเหล่านั้นตั้งแต่เริ่มต้น

สิ่งนี้ตามมา ไร้สัญชาติ การออกแบบซึ่งช่วยให้เบราว์เซอร์สามารถกำหนดค่าภาพเคลื่อนไหวได้อย่างง่ายดาย มีการอธิบายการคำนวณจริง โปรดคลิกที่นี่เพื่ออ่านรายละเอียดเพิ่มเติม และ โปรดคลิกที่นี่เพื่ออ่านรายละเอียดเพิ่มเติม.

ความพยายามอีกครั้ง

แนวคิดหนึ่งคือการหยุดภาพเคลื่อนไหวปัจจุบันชั่วคราว จากนั้นจึงเพิ่มภาพเคลื่อนไหวที่ช้าลงซึ่งจะเข้ามาแทนที่:

div {
  animation-name: spin1;
  animation-duration: 2s;
}

#slowmo:checked ~ div {
  animation-name: spin1, spin2;
  animation-duration: 2s, 5s;
  animation-play-state: paused, running;
}

ดังนั้นจึงใช้งานได้:

… หรือเปล่า?

มันช้าลงเมื่อคุณคลิกที่ "Slowmo!" แต่ถ้าคุณรอจนครบวง คุณจะเห็น “กระโดด” อันที่จริงมันมักจะกระโดดไปยังตำแหน่งเมื่อ “Slowmo!” ถูกคลิกบน

เหตุผลก็คือเราไม่มี from คีย์เฟรมที่กำหนดไว้ – และเราไม่ควร เมื่อผู้ใช้คลิกที่ “Slowmo!” spin1 ถูกหยุดที่บางตำแหน่ง และ spin2 เริ่มต้นที่ตำแหน่งเดียวกันทุกประการ เราไม่สามารถคาดเดาตำแหน่งนั้นล่วงหน้า … หรือเราทำได้?

โซลูชันการทำงาน

เราทำได้! ด้วยการใช้คุณสมบัติแบบกำหนดเอง เราสามารถจับมุมในแอนิเมชั่นแรก แล้วส่งต่อไปยังแอนิเมชั่นที่สอง:

div {
  transform: rotate(var(--angle1));
  animation-name: spin1;
  animation-duration: 2s;
}

#slowmo:checked ~ div {
  transform: rotate(var(--angle2));
  animation-name: spin1, spin2;
  animation-duration: 2s, 5s;
  animation-play-state: paused, running;
}

@keyframes spin1 {
  to {
    --angle1: 360deg;
  }
}

@keyframes spin2 {
  from {
    --angle2: var(--angle1);
  }
  to {
    --angle2: calc(var(--angle1) + 360deg);
  }
}

หมายเหตุ @property ใช้ในตัวอย่างนี้ ซึ่งก็คือ ไม่รองรับทุกบราวเซอร์.

ทางออกที่ “สมบูรณ์แบบ”

วิธีแก้ปัญหาก่อนหน้านี้มีข้อแม้อยู่: "การออกจาก slowmo" ทำงานได้ไม่ดี

นี่เป็นทางออกที่ดีกว่า:

ในเวอร์ชันนี้ สโลว์โมชั่นสามารถเข้าหรือออกได้อย่างราบรื่น ไม่มีการใช้คุณลักษณะทดลองอย่างใดอย่างหนึ่ง จึงเป็นทางออกที่สมบูรณ์แบบหรือไม่? ใช่และไม่.

วิธีแก้ปัญหานี้ทำงานเหมือน "เปลี่ยน" "เกียร์":

  • เกียร์: มีสอง <div>ส. หนึ่งคือผู้ปกครองของอีกคนหนึ่ง ทั้งสองมี spin แอนิเมชั่นแต่มีความแตกต่าง animation-duration. สถานะสุดท้ายขององค์ประกอบคือการสะสมของภาพเคลื่อนไหวทั้งสอง
  • ขยับ: ที่จุดเริ่มต้นเพียงคนเดียว <div> มีแอนิเมชั่นทำงานอยู่ อื่น ๆ หยุดชั่วคราว เมื่อช่องทำเครื่องหมายถูกสลับ ภาพเคลื่อนไหวทั้งสองจะสลับสถานะ

แม้ว่าฉันจะชอบผลลัพธ์นี้มาก แต่ก็มีปัญหาอยู่อย่างหนึ่ง: มันเป็นการใช้ประโยชน์จาก spin แอนิเมชั่น ซึ่งใช้ไม่ได้กับแอนิเมชั่นประเภทอื่นโดยทั่วไป

แนวทางปฏิบัติ (ด้วย JS)

สำหรับแอนิเมชั่นทั่วไป เป็นไปได้ที่จะใช้ฟังก์ชันสโลว์โมชั่นด้วย JavaScript เล็กน้อย:

คำอธิบายสั้น ๆ :

  • คุณสมบัติที่กำหนดเองใช้เพื่อติดตามความคืบหน้าของภาพเคลื่อนไหว
  • ภาพเคลื่อนไหว "เริ่มต้นใหม่" เมื่อช่องทำเครื่องหมายถูกสลับ
  • รหัส JS คำนวณที่ถูกต้อง animation-delay เพื่อให้แน่ใจว่าการเปลี่ยนแปลงจะราบรื่น ฉันแนะนำ บทความนี้ ถ้าคุณไม่คุ้นเคยกับค่าลบของ animation-delay.

คุณสามารถดูโซลูชันนี้เป็นลูกผสมของ "แอนิเมชั่นการรีสตาร์ท" และแนวทาง "การเปลี่ยนเกียร์"

นี่เป็นสิ่งสำคัญในการติดตามความคืบหน้าของแอนิเมชั่นอย่างถูกต้อง วิธีแก้ปัญหาเป็นไปได้ถ้า @property ไม่สามารถใช้งานได้. ตัวอย่างเช่น เวอร์ชันนี้ใช้ z-index เพื่อติดตามความคืบหน้า:

หมายเหตุด้านข้าง: เดิมที ฉันพยายามสร้างเวอร์ชัน CSS เท่านั้น แต่ไม่สำเร็จ แม้จะไม่แน่ใจ 100% แต่ฉันคิดว่าเป็นเพราะ animation-delay is ไม่เคลื่อนไหว.

นี่คือเวอร์ชันที่มี JavaScript น้อยที่สุด ใช้งานได้เฉพาะ "เข้าสู่ slowmo"

โปรดแจ้งให้เราทราบหากคุณสร้างเวอร์ชัน CSS เท่านั้นที่ใช้งานได้!

สโลว์โมชั่นใดๆ แอนิเมชั่น (พร้อม JS)

สุดท้ายนี้ ฉันต้องการแบ่งปันวิธีแก้ปัญหาที่ใช้ได้กับ (เกือบ) แอนิเมชั่นใดๆ ก็ตาม แม้จะมีความซับซ้อนหลายอย่างก็ตาม @keyframes:

โดยพื้นฐานแล้ว คุณต้องเพิ่มตัวติดตามความคืบหน้าของแอนิเมชัน จากนั้นคำนวณอย่างระมัดระวัง animation-delay สำหรับแอนิเมชั่นใหม่ อย่างไรก็ตาม บางครั้งอาจเป็นเรื่องยาก (แต่เป็นไปได้) ในการรับค่าที่ถูกต้อง

ตัวอย่างเช่น:

  • animation-timing-function ไม่ใช่ linear.
  • animation-direction ไม่ใช่ normal.
  • หลายค่าใน animation-name กับที่แตกต่างกัน animation-duration's และ animation-delayเอส

มีการอธิบายวิธีการนี้ด้วย โปรดคลิกที่นี่เพื่ออ่านรายละเอียดเพิ่มเติม สำหรับ เว็บแอนิเมชั่น API.

กิตติกรรมประกาศ

ฉันเริ่มเส้นทางนี้หลังจากพบโปรเจ็กต์ CSS เท่านั้น บางคนเป็น งานศิลปะที่ละเอียดอ่อนและบางส่วนเป็น การคุมกำเนิดที่ซับซ้อน. รายการโปรดของฉันคือวัตถุที่เกี่ยวข้องกับวัตถุ 3 มิติ ตัวอย่างเช่น สิ่งนี้ ลูกกระดอน และนี้ ก้อนบรรจุ.

ในตอนแรกฉันไม่รู้ว่าสิ่งเหล่านี้ถูกสร้างขึ้นมาอย่างไร ต่อมาฉันอ่านและเรียนรู้มากมายจาก บทช่วยสอนที่ดีนี้โดย Ana Tudor.

เมื่อมันปรากฏออกมา การสร้างและทำให้วัตถุ 3 มิติเคลื่อนไหวด้วย CSS นั้นไม่แตกต่างจากการทำด้วย . มากนัก เครื่องปั่นด้วยรสชาติที่แตกต่างกันเล็กน้อย

สรุป

ในบทความนี้ เราได้ตรวจสอบพฤติกรรมของอนิเมชั่น CSS เมื่อ animate-* ทรัพย์สินมีการเปลี่ยนแปลง โดยเฉพาะอย่างยิ่ง เราได้หาวิธีแก้ปัญหาสำหรับ "การเล่นภาพเคลื่อนไหวซ้ำ" และ "ภาพเคลื่อนไหวช้า-mo"

ฉันหวังว่าบทความนี้จะน่าสนใจ โปรดแจ้งให้เราทราบความคิดของคุณ!

ประทับเวลา:

เพิ่มเติมจาก เคล็ดลับ CSS