ไม่ว่าจะชอบหรือไม่ชอบ Parallaxing ยังคงอยู่ เมื่อใช้อย่างเหมาะสม Parallax จะเพิ่มมิติและความซับซ้อนให้กับเว็บแอปได้ แต่ปัญหาคือการใช้ Parallax ในลักษณะที่มีประสิทธิภาพนั้นอาจเป็นเรื่องยาก ในบทความนี้ เราจะพูดถึงโซลูชันที่มีประสิทธิภาพและทำงานได้กับทุกเบราว์เซอร์
สรุป
- อย่าใช้เหตุการณ์การเลื่อนหรือ
background-position
เพื่อสร้างภาพเคลื่อนไหวแบบพารัลแลกซ์ - ใช้การเปลี่ยนรูปแบบ 3 มิติของ CSS เพื่อสร้างเอฟเฟกต์พารัลแลกซ์ที่แม่นยำยิ่งขึ้น
- สำหรับ Mobile Safari ให้ใช้
position: sticky
เพื่อให้แน่ใจว่าระบบจะนำไปใช้เอฟเฟกต์พารัลแลกซ์
หากต้องการโซลูชันแบบติดตั้งใช้งานทันที ให้ไปที่ที่เก็บ GitHub ของตัวอย่างองค์ประกอบ UI แล้วดาวน์โหลด Parallax helper JS คุณดูการสาธิตการใช้งานแบบเรียลไทม์ของเครื่องมือเลื่อนแบบพารัลแลกซ์ได้ในที่เก็บ GitHub
ปัญหาเกี่ยวกับภาพพารัลแลกซ์
ก่อนอื่น มาลองดู 2 วิธีทั่วไปในการสร้างเอฟเฟกต์ภาพพารัลแลกซ์ และสาเหตุที่วิธีการเหล่านี้ไม่เหมาะกับวัตถุประสงค์ของเรา
ไม่เหมาะสม: ใช้เหตุการณ์การเลื่อน
ข้อกําหนดหลักของภาพพารัลแลกซ์คือควรมีการเชื่อมโยงกับการเลื่อนหน้าเว็บ โดยตำแหน่งขององค์ประกอบภาพพารัลแลกซ์ควรอัปเดตเมื่อมีการเปลี่ยนแปลงตำแหน่งการเลื่อนของหน้าเว็บ แม้ว่าฟังดูจะง่าย แต่กลไกที่สําคัญของเบราว์เซอร์สมัยใหม่คือความสามารถในการทํางานแบบไม่พร้อมกัน ในกรณีนี้ เหตุการณ์ดังกล่าวหมายถึงเหตุการณ์การเลื่อน ในเบราว์เซอร์ส่วนใหญ่ ระบบจะส่งเหตุการณ์การเลื่อนเป็น "พยายามอย่างดีที่สุด" และไม่มีการรับประกันว่าจะส่งในทุกเฟรมของภาพเคลื่อนไหวการเลื่อน
ข้อมูลสำคัญนี้บอกเหตุผลที่เราต้องหลีกเลี่ยงโซลูชันที่ใช้ JavaScript ซึ่งจะย้ายองค์ประกอบตามเหตุการณ์การเลื่อน นั่นคือ JavaScript ไม่รับประกันว่าภาพพารัลแลกซ์จะทำงานตามตำแหน่งการเลื่อนของหน้าเว็บ ใน Safari บนอุปกรณ์เคลื่อนที่เวอร์ชันเก่า ระบบจะส่งเหตุการณ์การเลื่อนเมื่อการเลื่อนสิ้นสุดลง ซึ่งทำให้สร้างเอฟเฟกต์การเลื่อนแบบ JavaScript ไม่ได้ เวอร์ชันที่ใหม่กว่าส่งเหตุการณ์การเลื่อนระหว่างภาพเคลื่อนไหว แต่เป็นแบบ "พยายามอย่างสุดความสามารถ" เช่นเดียวกับ Chrome หากเธรดหลักไม่ว่างทำงานอื่น ระบบจะไม่ส่งเหตุการณ์การเลื่อนทันที ซึ่งหมายความว่าเอฟเฟกต์ภาพพาโนรามาจะหายไป
ไม่ดี: กำลังอัปเดต background-position
อีกสถานการณ์ที่เราต้องการหลีกเลี่ยงคือการวาดภาพในทุกเฟรม โซลูชันจํานวนมากพยายามเปลี่ยน background-position
เพื่อให้ภาพดูมีมิติ ซึ่งทําให้เบราว์เซอร์ต้องวาดส่วนที่ได้รับผลกระทบของหน้าเว็บใหม่เมื่อเลื่อน ซึ่งอาจทําให้ภาพเคลื่อนไหวกระตุกมาก
หากต้องการนำเสนอภาพเคลื่อนไหวแบบพารัลแลกซ์ตามที่ได้สัญญาไว้ เราต้องการสิ่งที่นำไปใช้เป็นพร็อพเพอร์ตี้แบบเร่ง (ซึ่งปัจจุบันหมายถึงการยึดตามการเปลี่ยนรูปแบบและความทึบแสง) และไม่อาศัยเหตุการณ์การเลื่อน
CSS ในแบบ 3 มิติ
ทั้ง Scott Kellum และ Keith Clark ต่างก็ทํางานอย่างมีนัยสําคัญในด้านการใช้ CSS 3 มิติเพื่อให้ได้ภาพเคลื่อนไหวแบบพารัลแลกซ์ และเทคนิคที่พวกเขาใช้มีประสิทธิภาพดังนี้
- ตั้งค่าองค์ประกอบที่บรรจุเพื่อเลื่อนด้วย
overflow-y: scroll
(และอาจใช้overflow-x: hidden
) - ใช้ค่า
perspective
กับองค์ประกอบเดียวกันนั้น และตั้งค่าperspective-origin
เป็นtop left
หรือ0 0
- ใช้การแปลใน Z กับองค์ประกอบย่อยขององค์ประกอบนั้น และปรับขนาดกลับขึ้นเพื่อสร้างการเคลื่อนไหวแบบพารัลแลกซ์โดยไม่ส่งผลต่อขนาดบนหน้าจอ
CSS สำหรับแนวทางนี้มีลักษณะดังนี้
.container {
width: 100%;
height: 100%;
overflow-x: hidden;
overflow-y: scroll;
perspective: 1px;
perspective-origin: 0 0;
}
.parallax-child {
transform-origin: 0 0;
transform: translateZ(-2px) scale(3);
}
ซึ่งจะถือว่ามีข้อมูลโค้ด HTML ดังนี้
<div class="container">
<div class="parallax-child"></div>
</div>
การปรับขนาดสำหรับมุมมอง
การดันองค์ประกอบย่อยกลับจะทำให้องค์ประกอบนั้นเล็กลงตามสัดส่วนกับค่าการแสดงผลภาพเชิงลึก คุณสามารถคำนวณขนาดที่ต้องใช้ด้วยสูตรนี้ (perspective - distance) / perspective เนื่องจากเราต้องการมากที่สุดคือการให้องค์ประกอบภาพพารัลแลกซ์แสดงผลแบบพารัลแลกซ์แต่ปรากฏในขนาดที่เราเขียนขึ้น องค์ประกอบดังกล่าวจึงต้องปรับขนาดขึ้นด้วยวิธีนี้แทนที่จะปล่อยไว้ตามเดิม
ในกรณีของโค้ดด้านบน มุมมองคือ 1px และระยะทาง Z ของ parallax-child
คือ -2px ซึ่งหมายความว่าจะต้องปรับขนาดองค์ประกอบขึ้น 3 เท่า ซึ่งคุณจะเห็นค่าที่เสียบเข้ากับโค้ด scale(3)
สำหรับเนื้อหาที่ไม่มีการใช้ค่า translateZ
คุณสามารถแทนที่ด้วยค่า 0 ได้ ซึ่งหมายความว่าสเกลคือ (perspective - 0) /
perspective ซึ่งมีค่าสุทธิเท่ากับ 1 ซึ่งหมายความว่ามีการปรับขนาดขึ้นหรือลง มีประโยชน์มาก
วิธีการทํางาน
เราต้องเข้าใจสาเหตุที่กลยุทธ์นี้ได้ผล เนื่องจากเราจะนำความรู้นี้ไปใช้ในเร็วๆ นี้ การเลื่อนเป็นการเปลี่ยนรูปแบบอย่างมีประสิทธิภาพ จึงเร่งความเร็วได้ โดยส่วนใหญ่เกี่ยวข้องกับการเปลี่ยนเลเยอร์ด้วย GPU ในการเลื่อนแบบทั่วไปซึ่งไม่มีการรับรู้ถึงมุมมอง การเลื่อนจะเกิดขึ้นในลักษณะ 1:1 เมื่อเปรียบเทียบองค์ประกอบที่เลื่อนและองค์ประกอบย่อย
หากคุณเลื่อนองค์ประกอบลง 300px
ระบบจะเปลี่ยนรูปแบบองค์ประกอบย่อยขึ้น 300px
เท่า
อย่างไรก็ตาม การใช้ค่ามุมมองกับองค์ประกอบที่เลื่อนจะรบกวนกระบวนการนี้ เนื่องจากจะเปลี่ยนเมทริกซ์ที่รองรับการเปลี่ยนรูปแบบการเลื่อน
ตอนนี้การเลื่อน 300 พิกเซลอาจทำให้รายการย่อยเลื่อนไปเพียง 150 พิกเซลเท่านั้น ทั้งนี้ขึ้นอยู่กับค่า perspective
และ translateZ
ที่คุณเลือก หากองค์ประกอบมีค่า translateZ
เป็น 0 ระบบจะเลื่อนองค์ประกอบนั้นในอัตราส่วน 1:1 (ตามปกติ) แต่องค์ประกอบย่อยที่เลื่อนใน Z ออกจากจุดเริ่มต้นของมุมมองจะเลื่อนในอัตราส่วนอื่น ผลลัพธ์ที่ได้คือภาพเคลื่อนไหวแบบพารัลแลกซ์ และที่สำคัญมากคือระบบจะจัดการเรื่องนี้โดยอัตโนมัติโดยเป็นส่วนหนึ่งของกลไกการเลื่อนภายในของเบราว์เซอร์ ซึ่งหมายความว่าคุณไม่จําเป็นต้องคอยฟังเหตุการณ์ scroll
หรือเปลี่ยนแปลง background-position
ข้อเสีย: Safari บนอุปกรณ์เคลื่อนที่
การใช้เอฟเฟกต์แต่ละอย่างมีข้อควรระวัง และข้อควรระวังที่สำคัญอย่างหนึ่งสำหรับการเปลี่ยนรูปแบบคือการรักษาเอฟเฟกต์ 3 มิติในองค์ประกอบย่อย หากมีองค์ประกอบในลําดับชั้นระหว่างองค์ประกอบที่มีมุมมองกับองค์ประกอบย่อยที่มีภาพพารัลแลกซ์ ระบบจะ "ปรับให้แบน" มุมมอง 3 มิติ ซึ่งหมายความว่าเอฟเฟกต์จะหายไป
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
ใน HTML ด้านบน .parallax-container
เป็นส่วนใหม่ที่จะช่วยทำให้ค่า perspective
เป็นค่าคงที่ และเราจะสูญเสียเอฟเฟกต์ภาพพารัลแลกซ์ โซลูชันในกรณีส่วนใหญ่นั้นค่อนข้างตรงไปตรงมา คุณเพียงเพิ่ม transform-style: preserve-3d
ลงในส่วนประกอบ ซึ่งจะทำให้ส่วนประกอบดังกล่าวแสดงผลเอฟเฟกต์ 3 มิติ (เช่น ค่ามุมมอง) ที่ใช้กับองค์ประกอบที่อยู่สูงขึ้นในลําดับชั้น
.parallax-container {
transform-style: preserve-3d;
}
แต่ในกรณีของ Safari บนอุปกรณ์เคลื่อนที่ กระบวนการจะซับซ้อนกว่าเล็กน้อย
การใช้ overflow-y: scroll
กับองค์ประกอบคอนเทนเนอร์ใช้งานได้ในทางเทคนิค แต่จะทำให้ไม่สามารถปัดองค์ประกอบที่เลื่อนได้ วิธีแก้ปัญหาคือเพิ่ม-webkit-overflow-scrolling: touch
แต่วิธีนี้จะทำให้perspective
แบนลงด้วยและเราจะไม่เห็นภาพพารัลแลกซ์
จากมุมมองของการปรับปรุงแบบเป็นขั้นเป็นตอน ปัญหานี้อาจไม่ใช่เรื่องใหญ่ หากใช้ภาพพารัลแลกซ์ไม่ได้ในทุกสถานการณ์ แอปจะยังคงใช้งานได้ แต่เราขอแนะนำให้หาวิธีแก้ปัญหาชั่วคราว
position: sticky
จะช่วยคุณได้
อันที่จริงแล้ว มีความช่วยเหลือบางอย่างในรูปแบบ position: sticky
ซึ่งมีไว้เพื่ออนุญาตให้องค์ประกอบ "ติด" อยู่ที่ด้านบนของวิวพอร์ตหรือองค์ประกอบหลักที่ระบุไว้ขณะเลื่อน ข้อกำหนดนี้ค่อนข้างจะหนาเหมือนกับข้อกำหนดส่วนใหญ่ แต่ก็มีข้อมูลเล็กๆ น้อยๆ ที่เป็นประโยชน์อยู่ด้วย
ประโยคนี้อาจดูไม่สำคัญมากนักในตอนแรก แต่ประเด็นสำคัญคือเมื่อพูดถึงวิธีคำนวณการคงอยู่ขององค์ประกอบ "ระบบจะคำนวณออฟเซ็ตโดยอ้างอิงถึงบรรพบุรุษที่ใกล้ที่สุดซึ่งมีกล่องเลื่อน" กล่าวคือ ระบบจะคำนวณระยะทางที่จะย้ายองค์ประกอบที่ติดอยู่ (เพื่อให้ปรากฏติดอยู่กับองค์ประกอบอื่นหรือวิวพอร์ต) ก่อนใช้การเปลี่ยนรูปแบบอื่นๆ ไม่ใช่หลัง ซึ่งหมายความว่าหากคำนวณออฟเซตที่ 300 พิกเซล เช่นเดียวกับตัวอย่างการเลื่อนก่อนหน้านี้ คุณจะมีโอกาสใหม่ในการใช้มุมมอง (หรือการเปลี่ยนรูปแบบอื่นๆ) เพื่อจัดการค่าออฟเซต 300 พิกเซลนั้นก่อนที่จะนำไปใช้กับองค์ประกอบที่ติดอยู่
การใช้ position: -webkit-sticky
กับองค์ประกอบภาพพารัลแลกซ์ช่วยให้เรา "เปลี่ยนกลับ" ผลลัพธ์การทำให้แบนของ -webkit-overflow-scrolling:
touch
ได้อย่างมีประสิทธิภาพ วิธีนี้ช่วยให้มั่นใจได้ว่าองค์ประกอบภาพพารัลแลกซ์จะอ้างอิงถึงบรรพบุรุษที่ใกล้ที่สุดด้วยกล่องเลื่อน ซึ่งในกรณีนี้คือ .container
จากนั้น .parallax-container
จะใช้ค่า perspective
ซึ่งจะเปลี่ยนค่าออฟเซ็ตการเลื่อนที่คำนวณแล้วและสร้างเอฟเฟกต์ภาพพาโนรามา เช่นเดียวกับก่อนหน้านี้
<div class="container">
<div class="parallax-container">
<div class="parallax-child"></div>
</div>
</div>
.container {
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
}
.parallax-container {
perspective: 1px;
}
.parallax-child {
position: -webkit-sticky;
top: 0px;
transform: translate(-2px) scale(3);
}
ซึ่งจะคืนค่าเอฟเฟกต์พารัลแลกซ์สำหรับ Safari บนอุปกรณ์เคลื่อนที่ ซึ่งเป็นข่าวดีสำหรับทุกคน
ข้อควรระวังเกี่ยวกับตำแหน่งติดหนึบ
อย่างไรก็ตาม position: sticky
มีการเปลี่ยนแปลงกลไกของภาพพารัลแลกซ์ การวางตำแหน่งแบบติดจะพยายามยึดองค์ประกอบไว้กับคอนเทนเนอร์ที่เลื่อนได้ ส่วนเวอร์ชันที่ไม่ใช่แบบติดจะไม่ยึด ซึ่งหมายความว่าภาพพารัลแลกซ์แบบติดท้ายจะกลายเป็นค่ากลับกันของภาพพารัลแลกซ์แบบไม่ติดท้าย
- เมื่อใช้
position: sticky
องค์ประกอบจะเคลื่อนไหวน้อยลงเมื่ออยู่ใกล้กับ z=0 - หากไม่มี
position: sticky
องค์ประกอบจะเคลื่อนไหวมากขึ้นเมื่อเข้าใกล้ z=0
หากฟังดูเข้าใจยาก โปรดดูการสาธิตนี้โดย Robert Flack ซึ่งแสดงให้เห็นว่าองค์ประกอบมีลักษณะการทำงานอย่างไรเมื่อใช้และไม่ได้ใช้การจัดวางแบบติดหนึบ หากต้องการดูความแตกต่าง คุณต้องใช้ Chrome Canary (เวอร์ชัน 56 ณ เวลาที่เขียน) หรือ Safari
การสาธิตโดย Robert Flack ที่แสดงผลกระทบของ position: sticky
ต่อการเลื่อนแบบพารัลแลกซ์
ข้อบกพร่องและการแก้ปัญหาต่างๆ
อย่างไรก็ตาม ยังมีข้อบกพร่องที่เราต้องแก้ไขอยู่บ้าง
- การรองรับการติดอยู่ไม่สอดคล้องกัน Chrome ยังคงอยู่ระหว่างการรองรับ ส่วน Edge ไม่รองรับเลย และ Firefox มีข้อบกพร่องในการวาดภาพเมื่อใช้การติดกับองค์ประกอบอื่นๆ ร่วมกับการเปลี่ยนรูปแบบตามมุมมอง ในกรณีเช่นนี้ คุณควรเพิ่มโค้ดเพียงเล็กน้อยเพื่อเพิ่ม
position: sticky
(เวอร์ชันที่มีคำนำหน้า-webkit-
) เฉพาะเมื่อจำเป็น ซึ่งจะใช้กับ Safari บนอุปกรณ์เคลื่อนที่เท่านั้น - เอฟเฟกต์นี้ "ใช้ไม่ได้" ใน Edge Edge จะพยายามจัดการการเลื่อนในระดับระบบปฏิบัติการ ซึ่งโดยทั่วไปแล้วเป็นสิ่งที่ดี แต่ในเคสนี้กลับทำให้ Edge ตรวจจับการเปลี่ยนแปลงมุมมองขณะเลื่อนไม่ได้ หากต้องการแก้ไขปัญหานี้ ให้เพิ่มองค์ประกอบตำแหน่งคงที่ เนื่องจากดูเหมือนว่าจะเปลี่ยน Edge ไปใช้ วิธีการเลื่อนที่ไม่ใช่ระบบปฏิบัติการ และตรวจสอบว่าองค์ประกอบดังกล่าวคำนึงถึงการเปลี่ยนแปลงมุมมอง
- "เนื้อหาของหน้าเว็บมีขนาดใหญ่ขึ้นมาก" เบราว์เซอร์หลายตัวคำนึงถึงขนาดเมื่อตัดสินใจว่าเนื้อหาของหน้าเว็บควรมีขนาดใหญ่เท่าใด แต่น่าเสียดายที่ Chrome และ Safari ไม่คำนึงถึงมุมมอง ดังนั้น หากมีการใช้มาตราส่วน 3 เท่ากับองค์ประกอบ คุณอาจเห็นแถบเลื่อนและอื่นๆ แม้ว่าองค์ประกอบจะอยู่ที่ 1 เท่าหลังจากใช้
perspective
แล้วก็ตาม คุณสามารถแก้ปัญหานี้ได้โดยการปรับขนาดองค์ประกอบจากมุมขวาล่าง (ด้วยtransform-origin: bottom right
) ซึ่งได้ผลเนื่องจากจะทำให้องค์ประกอบที่มีขนาดใหญ่เกินไปขยายเข้าไปใน "ขอบเขตเชิงลบ" (โดยทั่วไปคือด้านซ้ายบน) ของพื้นที่ที่เลื่อนได้ ซึ่งขอบเขตที่เลื่อนได้จะไม่อนุญาตให้คุณเห็นหรือเลื่อนไปยังเนื้อหาในขอบเขตเชิงลบ
บทสรุป
เอฟเฟกต์พารัลแลกซ์เป็นเอฟเฟกต์สนุกๆ เมื่อใช้อย่างเหมาะสม ดังที่คุณเห็น การติดตั้งใช้งานในลักษณะที่มีประสิทธิภาพ ทำงานร่วมกับการเลื่อน และใช้งานได้ในหลายเบราว์เซอร์นั้นเป็นไปได้ เนื่องจากต้องใช้การดัดแปลงทางคณิตศาสตร์เล็กน้อยและข้อมูลโค้ดพื้นฐานจำนวนเล็กน้อยเพื่อให้ได้ผลลัพธ์ที่ต้องการ เราจึงได้รวบรวมไลบรารีตัวช่วยและตัวอย่างเล็กๆ น้อยๆ ไว้ให้แล้ว ซึ่งคุณสามารถดูได้ในตัวอย่างองค์ประกอบ UI ใน GitHub repo
ลองใช้แล้วแจ้งให้เราทราบ