Wolfenstein แบบ CSS เท่านั้น เป็นโครงการเล็กๆ ที่ฉันทำเมื่อไม่กี่สัปดาห์ก่อน เป็นการทดลองกับการแปลงและภาพเคลื่อนไหว CSS 3D
แรงบันดาลใจจาก สาธิต FPS และอีก Wolfenstein รหัสปากกาฉันตัดสินใจสร้างเวอร์ชันของตัวเอง มีพื้นฐานมาจากตอนที่ 1 – ชั้น 9 ของเกม Wolfenstein 3D ดั้งเดิม
ตัวแก้ไข: เกมนี้ต้องการการตอบสนองอย่างรวดเร็วโดยเจตนาเพื่อหลีกเลี่ยงหน้าจอ Game Over
นี่คือวิดีโอการเล่นผ่าน:
โดยสรุป โปรเจ็กต์ของฉันไม่ได้เป็นเพียงแอนิเมชั่น CSS ยาวๆ ที่เขียนสคริปต์อย่างระมัดระวัง บวกกับบางกรณีของ ช่องทำเครื่องหมาย hack.
:checked ~ div { animation-name: spin; }
สภาพแวดล้อมประกอบด้วยใบหน้ากริด 3 มิติ และภาพเคลื่อนไหวส่วนใหญ่เป็นการแปลและหมุน 3 มิติธรรมดา ไม่มีอะไรแฟนซีจริงๆ
อย่างไรก็ตาม ปัญหาสองข้อนั้นแก้ไขได้ยากเป็นพิเศษ:
- เล่นแอนิเมชั่น “การยิงอาวุธ” ทุกครั้งที่ผู้เล่นคลิกที่ศัตรู
- เมื่อบอสที่เคลื่อนไหวเร็วถูกโจมตีครั้งสุดท้าย ให้เข้าสู่การเคลื่อนไหวช้าอย่างน่าทึ่ง
ในระดับเทคนิค นี่หมายถึง:
- เล่นภาพเคลื่อนไหวซ้ำเมื่อเลือกช่องทำเครื่องหมายถัดไป
- ทำให้ภาพเคลื่อนไหวช้าลงเมื่อเลือกช่องทำเครื่องหมาย
อันที่จริงแล้ว โปรเจ็กต์ของฉันไม่ได้รับการแก้ไขอย่างเหมาะสม! ฉันลงเอยด้วยการใช้วิธีแก้ปัญหาหรือเพียงแค่ยอมแพ้
ในทางกลับกัน หลังจากขุดค้นมาบ้าง ในที่สุดฉันก็พบกุญแจของปัญหาทั้งสอง นั่นคือ การเปลี่ยนแปลงคุณสมบัติของการรันแอนิเมชั่น CSS ในบทความนี้ เราจะสำรวจเพิ่มเติมในหัวข้อนี้:
- ตัวอย่างแบบโต้ตอบมากมาย
- Dissections: แต่ละตัวอย่างทำงานอย่างไร (หรือไม่ทำงาน)?
- เบื้องหลัง: เบราว์เซอร์จัดการกับสถานะแอนิเมชั่นอย่างไร
ให้ฉัน “โยนอิฐของฉัน”.
ปัญหาที่ 1: การเล่นภาพเคลื่อนไหวซ้ำ
ตัวอย่างแรก: “แค่ช่องทำเครื่องหมายอื่น”
สัญชาตญาณแรกของฉันคือ "แค่เพิ่มช่องทำเครื่องหมายอื่น" ซึ่งใช้ไม่ได้:
ช่องทำเครื่องหมายแต่ละช่องทำงานแยกกันแต่ใช้ร่วมกันไม่ได้ หากมีการเลือกช่องทำเครื่องหมายหนึ่งช่อง ช่องอื่นจะไม่ทำงานอีกต่อไป
นี่คือวิธีการทำงาน (หรือ “ไม่ทำงาน”):
- พื้นที่
animation-name
of<div>
isnone
โดยค่าเริ่มต้น - ผู้ใช้คลิกที่ช่องทำเครื่องหมายหนึ่งช่อง
animation-name
จะกลายเป็นspin
และแอนิเมชั่นเริ่มต้นตั้งแต่ต้น - หลังจากนั้นครู่หนึ่ง ผู้ใช้คลิกที่ช่องทำเครื่องหมายอื่น กฎ CSS ใหม่มีผลบังคับใช้ แต่
animation-name
is ยังคงspin
ซึ่งหมายความว่าไม่มีการเพิ่มหรือลบภาพเคลื่อนไหว แอนิเมชั่นยังคงเล่นต่อไปราวกับว่าไม่มีอะไรเกิดขึ้น
ตัวอย่างที่สอง: “การโคลนแอนิเมชั่น”
แนวทางการทำงานวิธีหนึ่งคือการโคลนแอนิเมชัน:
#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2; }
นี่คือวิธีการทำงาน:
animation-name
isnone
ในขั้นต้น- ผู้ใช้คลิกที่ “หมุน!”
animation-name
จะกลายเป็นspin1
. แอนิเมชั่นspin1
เริ่มต้นจากจุดเริ่มต้นเพราะเพิ่งเพิ่มเข้ามา - ผู้ใช้คลิกที่ "หมุนอีกครั้ง!",
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
isnone
โดยค่าเริ่มต้น ซึ่งหมายความว่า "ภาพเคลื่อนไหวจะไม่มีผลเลยหากไม่เล่น"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"
ฉันหวังว่าบทความนี้จะน่าสนใจ โปรดแจ้งให้เราทราบความคิดของคุณ!