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

สรุปสั้นๆ
- อย่าใช้เหตุการณ์การเลื่อนหรือ
background-position
เพื่อสร้างภาพเคลื่อนไหวแบบพารัลแลกซ์ - ใช้การเปลี่ยนรูปแบบ 3 มิติของ CSS เพื่อสร้างเอฟเฟกต์พารัลแลกซ์ที่แม่นยำยิ่งขึ้น
- สำหรับ Mobile Safari ให้ใช้
position: sticky
เพื่อให้แน่ใจว่าเอฟเฟกต์พารัลแลกซ์ ได้รับการเผยแพร่
หากต้องการโซลูชันแบบดรอปอิน ให้ไปที่ที่เก็บ GitHub ของตัวอย่างองค์ประกอบ UI แล้วดาวน์โหลด JS ตัวช่วย Parallax คุณดูการสาธิตการใช้งาน Parallax Scroller แบบสดๆ ได้ใน ที่เก็บ GitHub
ผู้ที่ชอบแก้ปัญหา
ก่อนอื่น มาดู 2 วิธีทั่วไปในการสร้างเอฟเฟกต์ภาพเคลื่อนไหวแบบพารัลแลกซ์ กัน และโดยเฉพาะอย่างยิ่ง เหตุผลที่วิธีเหล่านี้ไม่เหมาะกับวัตถุประสงค์ของเรา
ไม่ดี: ใช้เหตุการณ์การเลื่อน
ข้อกำหนดหลักของการเลื่อนแบบพารัลแลกซ์คือควรเชื่อมโยงกับการเลื่อน กล่าวคือ ทุกครั้งที่ตำแหน่งการเลื่อนของหน้าเว็บมีการเปลี่ยนแปลง ตำแหน่งขององค์ประกอบการเลื่อนแบบพารัลแลกซ์ควรได้รับการอัปเดต แม้จะฟังดูง่าย แต่กลไกสำคัญของเบราว์เซอร์สมัยใหม่คือความสามารถในการทำงานแบบไม่พร้อมกัน ในกรณีเฉพาะของเรา การดำเนินการนี้จะมีผลกับเหตุการณ์การเลื่อน ในเบราว์เซอร์ส่วนใหญ่ เหตุการณ์การเลื่อนจะแสดงเป็น "พยายามอย่างเต็มที่" และไม่มีการรับประกันว่าจะแสดงในทุกเฟรมของภาพเคลื่อนไหวการเลื่อน
ข้อมูลสำคัญนี้บอกให้เราทราบว่าทำไมจึงต้องหลีกเลี่ยงโซลูชันที่ใช้ JavaScript ซึ่งย้ายองค์ประกอบตามเหตุการณ์การเลื่อน JavaScript ไม่รับประกันว่าการเลื่อนแบบพารัลแลกซ์จะสอดคล้องกับ ตำแหน่งการเลื่อนของหน้าเว็บ ใน Mobile Safari เวอร์ชันเก่า เหตุการณ์การเลื่อนจะเกิดขึ้นที่ส่วนท้ายของการเลื่อนจริงๆ ซึ่งทำให้ไม่สามารถสร้างเอฟเฟกต์การเลื่อนที่ใช้ JavaScript ได้ ส่วนเวอร์ชันที่ใหม่กว่าจะส่งเหตุการณ์การเลื่อน ระหว่างภาพเคลื่อนไหว แต่จะทำในลักษณะ "พยายามอย่างเต็มที่" เช่นเดียวกับ Chrome หาก เทรดหลักทำงานอื่นอยู่ เหตุการณ์การเลื่อนจะไม่ได้รับการส่ง ทันที ซึ่งหมายความว่าเอฟเฟกต์พารัลแลกซ์จะหายไป
ไม่ดี: กำลังอัปเดต background-position
อีกสถานการณ์หนึ่งที่เราอยากหลีกเลี่ยงคือการวาดภาพในทุกเฟรม โซลูชันหลายอย่าง
พยายามเปลี่ยน background-position
เพื่อให้ดูเหมือนภาพเคลื่อนไหวแบบพารัลแลกซ์ ซึ่ง
ทำให้เบราว์เซอร์ต้องวาดส่วนที่ได้รับผลกระทบของหน้าเว็บใหม่เมื่อเลื่อน และ
อาจทำให้ภาพเคลื่อนไหวเกิดอาการกระตุกอย่างมาก
หากต้องการทำตามสัญญาเรื่องการเคลื่อนไหวแบบพารัลแลกซ์ เราต้องการสิ่งที่จะ นำไปใช้เป็นพร็อพเพอร์ตี้แบบเร่งได้ (ซึ่งในปัจจุบันหมายถึงการใช้ Transform และความทึบแสง) และไม่ต้องอาศัยเหตุการณ์การเลื่อน
CSS ใน 3 มิติ
ทั้ง Scott Kellum และ Keith Clark ได้ ทำงานอย่างมากในด้านการใช้ CSS 3D เพื่อให้ได้ภาพเคลื่อนไหวแบบพารัลแลกซ์ และเทคนิคที่ใช้ก็คือ
- ตั้งค่าองค์ประกอบที่ครอบคลุมให้เลื่อนด้วย
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>
การปรับขนาดสำหรับมุมมอง
การเลื่อนองค์ประกอบย่อยกลับจะทำให้องค์ประกอบมีขนาดเล็กลงตามสัดส่วนของ ค่าเปอร์สเปคทีฟ คุณคำนวณขนาดที่ต้องปรับเพิ่มได้โดยใช้สมการ (มุมมอง - ระยะทาง) / มุมมอง เนื่องจากเราน่าจะต้องการให้องค์ประกอบที่เลื่อนแบบพารัลแลกซ์เลื่อนแบบพารัลแลกซ์ แต่ปรากฏในขนาดที่เราสร้างขึ้น จึงต้องขยายขนาดในลักษณะนี้แทนที่จะปล่อยไว้ตามเดิม
ในกรณีของโค้ดด้านบน มุมมองคือ 1px และระยะ Z ของ
parallax-child
คือ -2px ซึ่งหมายความว่าองค์ประกอบจะต้อง
ขยายขนาดขึ้น 3 เท่า ซึ่งคุณจะเห็นว่าค่านี้เป็นค่าที่เสียบเข้าไปในโค้ด
scale(3)
สำหรับเนื้อหาที่ไม่ได้ใช้ค่า translateZ
คุณสามารถ
แทนที่ด้วยค่า 0 ซึ่งหมายความว่าสเกลคือ (มุมมอง - 0) /
มุมมอง ซึ่งจะให้ค่าเป็น 1 ซึ่งหมายความว่าไม่ได้ปรับขนาด
ขึ้นหรือลง สะดวกมากจริงๆ
วิธีการทำงานของแนวทางนี้
คุณควรทราบเหตุผลที่วิธีนี้ได้ผล เนื่องจากเราจะใช้ความรู้ดังกล่าวในเร็วๆ นี้
การเลื่อนคือการเปลี่ยนรูปแบบอย่างมีประสิทธิภาพ จึงสามารถเร่งความเร็วได้ โดยส่วนใหญ่จะเกี่ยวข้องกับการเลื่อนเลเยอร์ไปมาด้วย GPU ในการเลื่อนทั่วไปซึ่งไม่มีแนวคิดเรื่องมุมมอง การเลื่อนจะเกิดขึ้นในลักษณะ 1:1 เมื่อเปรียบเทียบองค์ประกอบการเลื่อนกับองค์ประกอบย่อย
หากคุณเลื่อนองค์ประกอบลงมาด้วย 300px
องค์ประกอบย่อยจะเปลี่ยนขึ้นไป
ในจำนวนเดียวกัน: 300px
อย่างไรก็ตาม การใช้ค่ามุมมองกับองค์ประกอบที่เลื่อนจะทำให้กระบวนการนี้ผิดเพี้ยน
เนื่องจากจะเปลี่ยนเมทริกซ์ที่เป็นพื้นฐานของการเปลี่ยนการเลื่อน
ตอนนี้การเลื่อน 300 พิกเซลอาจเลื่อนรายการย่อยเพียง 150 พิกเซลเท่านั้น โดยขึ้นอยู่กับค่า perspective
และ translateZ
ที่คุณเลือก หากองค์ประกอบมีtranslateZ
ค่าเป็น 0 ระบบจะเลื่อนองค์ประกอบที่อัตราส่วน 1:1 (เช่นเดียวกับที่เคยเป็น) แต่จะเลื่อนองค์ประกอบย่อยที่เลื่อนใน Z ออกจากต้นทางของมุมมองในอัตราที่แตกต่างกัน ผลลัพธ์สุทธิ: การเคลื่อนไหวแบบพารัลแลกซ์ และที่สำคัญอย่างยิ่งคือระบบจะจัดการเรื่องนี้เป็นส่วนหนึ่งของกลไกการเลื่อนภายในของเบราว์เซอร์โดยอัตโนมัติ ซึ่งหมายความว่าคุณไม่จำเป็นต้องฟังเหตุการณ์ scroll
หรือเปลี่ยน background-position
ข้อควรทราบ: Safari บนอุปกรณ์เคลื่อนที่
เอฟเฟกต์ทุกอย่างมีข้อควรระวัง และข้อควรระวังที่สำคัญอย่างหนึ่งสำหรับการเปลี่ยนรูปคือ การรักษาเอฟเฟกต์ 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;
}
แต่ในกรณีของ Mobile Safari นั้นจะมีความซับซ้อนกว่าเล็กน้อย
การใช้ overflow-y: scroll
กับองค์ประกอบคอนเทนเนอร์ในทางเทคนิคใช้ได้ แต่จะทำให้ไม่สามารถปัดองค์ประกอบการเลื่อนได้ วิธีแก้คือการเพิ่ม
-webkit-overflow-scrolling: touch
แต่จะทำให้ perspective
แบนราบและเราจะไม่ได้ภาพเคลื่อนไหวแบบพารัลแลกซ์
จากมุมมองของการเพิ่มประสิทธิภาพแบบก้าวหน้า นี่อาจไม่ใช่ปัญหาใหญ่ หากเราไม่สามารถใช้ภาพเคลื่อนไหวแบบ Parallax ในทุกสถานการณ์ แอปจะยังคงทำงานได้ แต่เรา ควรหาวิธีแก้ปัญหา
position: sticky
มาช่วยแล้ว
จริงๆ แล้วมีตัวช่วยในรูปแบบของ position: sticky
ซึ่งมีไว้เพื่อ
อนุญาตให้องค์ประกอบ "ติด" อยู่ที่ด้านบนของวิวพอร์ตหรือองค์ประกอบหลักที่กำหนด
ในระหว่างการเลื่อน ข้อกำหนดนี้ค่อนข้างยาวเหมือนกับข้อกำหนดอื่นๆ ส่วนใหญ่ แต่มีข้อมูลที่เป็นประโยชน์ซ่อนอยู่
แม้ว่าในตอนแรกอาจดูเหมือนไม่มีอะไรมาก แต่ประเด็นสำคัญในประโยคดังกล่าวคือการอ้างอิงถึงวิธีคำนวณความเหนียวขององค์ประกอบอย่างแม่นยำ: "ระบบจะคำนวณออฟเซ็ตโดยอ้างอิงถึงบรรพบุรุษที่ใกล้ที่สุดที่มีกล่องเลื่อน" กล่าวคือ ระยะทางที่จะย้ายองค์ประกอบแบบติดหนึบ (เพื่อให้ปรากฏแนบกับองค์ประกอบอื่นหรือวิวพอร์ต) จะได้รับการคำนวณก่อนที่จะใช้การเปลี่ยนรูปแบบอื่นๆ ไม่ใช่หลังจาก ซึ่งหมายความว่า เช่นเดียวกับตัวอย่างการเลื่อนก่อนหน้านี้ หากมีการคำนวณออฟเซ็ต ที่ 300 พิกเซล ก็จะมีโอกาสใหม่ในการใช้มุมมอง (หรือการเปลี่ยนรูปแบบอื่นๆ) เพื่อจัดการค่าออฟเซ็ต 300 พิกเซลนั้นก่อนที่จะนำไปใช้กับองค์ประกอบแบบติดหนึบ
การใช้ position: -webkit-sticky
กับองค์ประกอบที่เลื่อนแบบพารัลแลกซ์จะช่วยให้เรา "ย้อนกลับ" เอฟเฟกต์การทำให้แบนของ -webkit-overflow-scrolling:
touch
ได้อย่างมีประสิทธิภาพ ซึ่งจะช่วยให้มั่นใจได้ว่าองค์ประกอบ Parallax จะอ้างอิงบรรพบุรุษที่ใกล้ที่สุด
ที่มีกล่องเลื่อน ซึ่งในกรณีนี้คือ .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);
}
ซึ่งจะคืนค่าเอฟเฟกต์พารัลแลกซ์สำหรับ Mobile Safari ซึ่งเป็นข่าวดีสำหรับทุกฝ่าย
ข้อควรระวังเกี่ยวกับการกำหนดตำแหน่งแบบติดหนึบ
อย่างไรก็ตาม มีความแตกต่างตรงที่ position: sticky
จะเปลี่ยนกลไกการเลื่อนแบบพารัลแลกซ์ การกำหนดตำแหน่งแบบ Sticky จะพยายามตรึงองค์ประกอบไว้กับคอนเทนเนอร์ที่เลื่อนได้ ส่วนเวอร์ชันที่ไม่ใช่ Sticky จะไม่ตรึง ซึ่งหมายความว่า
ภาพเคลื่อนไหวแบบพารัลแลกซ์ที่มีปลายแบบติดหนึบจะกลายเป็นส่วนกลับของภาพเคลื่อนไหวแบบพารัลแลกซ์ที่ไม่มีปลายแบบติดหนึบ
- เมื่อใช้
position: sticky
องค์ประกอบที่อยู่ใกล้ z=0 จะเคลื่อนที่น้อยลง - หากไม่มี
position: sticky
องค์ประกอบที่อยู่ใกล้ z=0 จะเคลื่อนที่ มากขึ้น
หากทั้งหมดนี้ดูเป็นนามธรรมเล็กน้อย โปรดดูการสาธิตนี้โดย Robert Flack ซึ่งแสดงให้เห็นว่าองค์ประกอบต่างๆ ทำงานแตกต่างกันอย่างไรเมื่อใช้และไม่ใช้ การวางตำแหน่งแบบติดหนึบ หากต้องการดูความแตกต่าง คุณต้องใช้ Chrome Canary (ซึ่งเป็นเวอร์ชัน 56 ในขณะที่เขียน) หรือ Safari

การสาธิตโดย Robert Flack ที่แสดงให้เห็นว่า
position: sticky
ส่งผลต่อการเลื่อนแบบพารัลแลกซ์อย่างไร
ข้อบกพร่องและวิธีแก้ปัญหาต่างๆ
อย่างไรก็ตาม เช่นเดียวกับสิ่งอื่นๆ ยังมีจุดที่ต้องปรับปรุง ให้ราบรื่นขึ้น
- การรองรับการตรึงไม่สอดคล้องกัน เรายังคงดำเนินการรองรับใน Chrome, Edge ไม่รองรับเลย และ Firefox มีข้อบกพร่องในการวาดภาพเมื่อรวม sticky กับการเปลี่ยนมุมมอง ในกรณีเช่นนี้
คุณควรเพิ่มโค้ดเล็กน้อยเพื่อเพิ่มเฉพาะ
position: sticky
(เวอร์ชันที่มีคำนำหน้า-webkit-
) เมื่อจำเป็น ซึ่งก็คือสำหรับ Mobile Safari เท่านั้น - เอฟเฟกต์ไม่ได้ "ใช้งานได้เลย" ใน Edge Edge พยายามจัดการการเลื่อนที่ระดับระบบปฏิบัติการ ซึ่งโดยทั่วไปถือเป็นเรื่องดี แต่ในกรณีนี้ การดำเนินการดังกล่าวทำให้ Edge ตรวจไม่พบการเปลี่ยนแปลงมุมมองระหว่างการเลื่อน หากต้องการแก้ไขปัญหานี้ คุณสามารถเพิ่ม องค์ประกอบที่มีตำแหน่งคงที่ เนื่องจากดูเหมือนว่าวิธีนี้จะเปลี่ยน Edge ไปใช้ วิธีการเลื่อนที่ไม่ใช่ของระบบปฏิบัติการ และช่วยให้มั่นใจได้ว่าระบบจะพิจารณาการเปลี่ยนแปลงมุมมอง
- "เนื้อหาของหน้าเว็บนี้เพิ่งขยายใหญ่ขึ้น" เบราว์เซอร์หลายตัวจะพิจารณาสเกล
เมื่อตัดสินใจว่าเนื้อหาของหน้าเว็บควรมีขนาดเท่าใด แต่ Chrome และ Safari
ไม่พิจารณามุมมอง ดังนั้น
หากมีการใช้สเกล 3x กับองค์ประกอบ คุณอาจเห็นแถบเลื่อนและอื่นๆ
แม้ว่าองค์ประกอบจะมีขนาด 1x หลังจากใช้
perspective
แล้วก็ตาม คุณสามารถหลีกเลี่ยงปัญหานี้ได้โดย การปรับขนาดองค์ประกอบจากมุมขวาล่าง (ด้วยtransform-origin: bottom right
) ซึ่งจะใช้ได้เนื่องจากจะทำให้องค์ประกอบที่มีขนาดใหญ่เกินไปขยายไปที่ "พื้นที่ติดลบ" (โดยปกติคือด้านซ้ายบน) ของพื้นที่ที่เลื่อนได้ พื้นที่ที่เลื่อนได้จะไม่แสดงหรือให้คุณเลื่อนไปยังเนื้อหาในพื้นที่ติดลบ
บทสรุป
การเลื่อนแบบพารัลแลกซ์เป็นเอฟเฟกต์ที่สนุกเมื่อใช้อย่างรอบคอบ ดังที่เห็น คุณสามารถ ติดตั้งใช้งานในลักษณะที่มีประสิทธิภาพ เลื่อนตาม และใช้ได้กับทุกเบราว์เซอร์ เนื่องจากต้องใช้การคำนวณทางคณิตศาสตร์เล็กน้อยและต้องมีโค้ดมาตรฐานจำนวนเล็กน้อยเพื่อให้ได้เอฟเฟกต์ที่ต้องการ เราจึงได้รวบรวมไลบรารีตัวช่วยขนาดเล็กและตัวอย่างไว้ให้แล้ว ซึ่งคุณสามารถดูได้ในที่เก็บตัวอย่างองค์ประกอบ UI ใน GitHub
ลองใช้ฟีเจอร์นี้ แล้วบอกให้เราทราบว่าคุณคิดเห็นอย่างไร