ไม่ว่าจะชอบหรือไม่ชอบ พารัลแลกซ์ก็อยู่ที่นี่ตลอดไป เมื่อใช้อย่างรอบคอบ เพิ่มความลึกและความละเอียดให้กับเว็บแอป อย่างไรก็ตาม ปัญหาคือการใช้ พารัลแลกซ์ในวิธีที่มีประสิทธิภาพอาจเป็นเรื่องที่ท้าทาย ในบทความนี้ เราจะ พูดคุยถึงโซลูชันที่มีประสิทธิภาพ และที่สำคัญไม่แพ้กันคือ ข้ามเบราว์เซอร์
![ภาพพารัลแลกซ์](https://developer.chrome.com/static/blog/performant-parallaxing/image/parallax-illustration-330a26c14e2fc.jpg?authuser=0&hl=th)
สรุปสั้นๆ
- อย่าใช้เหตุการณ์แบบเลื่อนหรือ
background-position
เพื่อสร้างภาพเคลื่อนไหวแบบพารัลแลกซ์ - ใช้การเปลี่ยนรูปแบบ 3 มิติของ CSS เพื่อสร้างเอฟเฟกต์พารัลแลกซ์ที่แม่นยำยิ่งขึ้น
- สำหรับ Safari บนอุปกรณ์เคลื่อนที่ ให้ใช้
position: sticky
เพื่อให้มั่นใจว่าเอฟเฟกต์พารัลแลกซ์ จะถูกเผยแพร่
หากคุณต้องการโซลูชันแบบดร็อปอิน ให้ไปที่ที่เก็บตัวอย่างองค์ประกอบ UI ของ GitHub แล้วคลิก JS ผู้ช่วยพารัลแลกซ์ ดูการสาธิตแบบสดของแถบเลื่อนพารัลแลกซ์ได้ใน ที่เก็บ GitHub
พารัลแลกซ์ปัญหา
เรามาเริ่มต้นด้วยการดู 2 วิธีทั่วไปในการสร้างพารัลแลกซ์ โดยเฉพาะอย่างยิ่ง เหตุผลที่ไม่เหมาะสมต่อวัตถุประสงค์ของเรา
ไม่ถูกต้อง: การใช้เหตุการณ์การเลื่อน
ข้อกำหนดหลักของพารัลแลกซ์คือควรใช้คู่กับการเลื่อน สำหรับ ตำแหน่งการเลื่อนของหน้าเว็บ องค์ประกอบพารัลแลกซ์ ควรอัปเดต แม้ว่าจะฟังดูง่าย แต่กลไกสำคัญของ เบราว์เซอร์สมัยใหม่คือความสามารถในการทำงานไม่พร้อมกัน ซึ่งเป็นไปตามข้อกำหนด เพื่อเลื่อนเหตุการณ์ ในเบราว์เซอร์ส่วนใหญ่จะมีการส่งเหตุการณ์การเลื่อน ว่า "พยายามอย่างเต็มที่" และไม่ได้รับประกันว่าจะแสดง ในทุกเฟรมของ ภาพเคลื่อนไหวแบบเลื่อน!
ข้อมูลสำคัญนี้บอกกับเราว่า เหตุใดเราจึงต้องหลีกเลี่ยง โซลูชันแบบ JavaScript ซึ่งย้ายองค์ประกอบตามเหตุการณ์การเลื่อน JavaScript ไม่รับประกันว่าพารัลแลกซ์จะยังคงเป็น ตำแหน่งการเลื่อนของหน้า ใน Mobile Safari เวอร์ชันเก่า เหตุการณ์การเลื่อน ส่งตรงช่วงท้ายของม้วนข้อความไปแล้ว ซึ่งทำให้ เอฟเฟกต์การเลื่อนแบบ JavaScript เวอร์ชันล่าสุด จะส่งเหตุการณ์การเลื่อน ในระหว่างการแสดงภาพเคลื่อนไหว ซึ่งก็คล้ายกับ Chrome ในการ "พยายามอย่างเต็มที่" พื้นฐาน หาก เทรดหลักไม่ว่างกับงานอื่นๆ ระบบจะไม่นำส่งกิจกรรมการเลื่อน โดยทันที ซึ่งหมายความว่าเอฟเฟกต์พารัลแลกซ์จะหายไป
ไม่ถูกต้อง: กำลังอัปเดต background-position
อีกสถานการณ์หนึ่งที่เราอยากหลีกเลี่ยงคือการวาดภาพบนทุกเฟรม โซลูชันมากมาย
ลองเปลี่ยน background-position
เพื่อให้ภาพพารัลแลกซ์
ทำให้เบราว์เซอร์แสดงผลซ้ำในส่วนที่ได้รับผลกระทบของหน้าขณะเลื่อน และ
อาจมีต้นทุนสูงพอที่จะทำให้ภาพเคลื่อนไหวกระเพื่อมได้อย่างมาก
หากเราต้องการ นำเสนอภาพเคลื่อนไหวแบบพารัลแลกซ์ สามารถใช้เป็นพร็อพเพอร์ตี้แบบเร่ง (ซึ่งปัจจุบันหมายถึงการยึดติดกับ การเปลี่ยนรูปแบบและความทึบแสง) รวมถึงไม่ต้องใช้เหตุการณ์การเลื่อน
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
คือ -2 พิกเซล ซึ่งหมายความว่าองค์ประกอบจะต้อง
เพิ่มขนาดเป็น 3 เท่าซึ่งจะเห็นได้คือค่าที่ใส่ลงในโค้ด
scale(3)
สำหรับเนื้อหาที่ไม่มีค่า translateZ
คุณสามารถ
แทนค่า 0 หมายความว่ามาตราส่วนคือ (มุมมอง - 0) /
สุทธิที่ค่า 1 ซึ่งหมายความว่ามีการปรับขนาด
ไม่ขึ้นหรือลง มีประโยชน์มากจริงๆ
วิธีการทำงาน
คุณต้องชี้แจงให้ชัดเจนว่าทำไมถึงได้ผล เพราะเราจะใช้
ในไม่ช้า การเลื่อนเป็นการเปลี่ยนโฉมหน้าอย่างมีประสิทธิภาพ จึงเป็นเหตุผลที่เราสามารถ
accelerated; ส่วนใหญ่แล้วคุณจะต้องเปลี่ยนเลเยอร์ต่างๆ ด้วย GPU ใน
การเลื่อนตามปกติ ซึ่งก็คือการเลื่อนดูโดยไม่มีความคิดเรื่องมุมมองเลย
จะเกิดขึ้นในลักษณะ 1:1 เมื่อเปรียบเทียบองค์ประกอบแบบเลื่อนและองค์ประกอบย่อย
หากคุณเลื่อนองค์ประกอบลงมา 300px
องค์ประกอบย่อยขององค์ประกอบนั้นจะเปลี่ยนรูปแบบขึ้น
เป็นจำนวนเงินเดียวกัน: 300px
อย่างไรก็ตาม การนำค่ามุมมองไปใช้กับองค์ประกอบที่เลื่อนได้โดยรอบ
ด้วยกระบวนการนี้ จะเปลี่ยนเมทริกซ์ที่สนับสนุนการเปลี่ยนรูปแบบการเลื่อน
ตอนนี้การเลื่อน 300 พิกเซลอาจเลื่อน
เด็กไป 150 พิกเซลเท่านั้น โดยขึ้นอยู่กับ
ค่า perspective
และ translateZ
ที่คุณเลือก หากองค์ประกอบมี
ค่า translateZ
ของ 0 จะเลื่อนที่ 1:1 (ตามเดิม) แต่หน้าเว็บย่อย
ที่ผลักในตัว Z ออกห่างจากจุดเริ่มต้นของมุมมอง จะถูกเลื่อนไปยัง
อัตรา ผลลัพธ์สุทธิ: การเคลื่อนไหวแบบพารัลแลกซ์ และที่สำคัญอย่างยิ่งคือ เครื่องมือนี้จัดการในฐานะ
ของเครื่องเลื่อนภายในเบราว์เซอร์โดยอัตโนมัติ ซึ่งหมายความว่า
คุณไม่จำเป็นต้องฟังเหตุการณ์ scroll
หรือเปลี่ยน background-position
โลดแล่นบนขี้ผึ้ง: Mobile 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;
}
อย่างไรก็ตาม ในกรณีของ Mobile Safari สิ่งต่างๆ จะซับซ้อนขึ้นเล็กน้อย
การใช้ overflow-y: scroll
กับองค์ประกอบคอนเทนเนอร์ในทางเทคนิคใช้งานได้ แต่
ต้นทุนในการขว้างองค์ประกอบที่เลื่อนได้ วิธีแก้ไขคือเพิ่ม
-webkit-overflow-scrolling: touch
แต่จะเป็นการรวม perspective
ด้วย
เราก็จะไม่มีพารัลแลกซ์เลย
จากมุมมองของการเพิ่มประสิทธิภาพแบบต่อเนื่อง นี่น่าจะไม่ใช่ ปัญหา หากเราไม่สามารถพารัลแลกซ์ได้ในทุกสถานการณ์ แอปจะยังใช้งานได้ เราขอแนะนำให้คุณหาวิธีแก้ปัญหาเบื้องต้น
position: sticky
ช่วยคุณได้
อันที่จริงแล้ว มีการให้ความช่วยเหลือในรูปแบบของ position: sticky
ซึ่งมีขึ้นเพื่อ
อนุญาตให้องค์ประกอบ "ติด" ที่ด้านบนของวิวพอร์ตหรือองค์ประกอบระดับบนสุดที่กำหนด
ขณะเลื่อน ข้อมูลจำเพาะส่วนใหญ่ค่อนข้างหนักหน่วง แต่มี
อัญมณีเล็กๆ ที่มีประโยชน์ภายใน:
ซึ่งอาจจะไม่ได้มีความหมายดีในครั้งแรกที่เห็น แต่เป็นประเด็นสำคัญ ประโยคดังกล่าวหมายถึงเมื่อพูดถึงลักษณะของความสามารถในการดึงดูด คำนวณแล้ว: "ระบบคำนวณออฟเซ็ตโดยอ้างอิงไปยังบรรพบุรุษที่อยู่ใกล้ที่สุด ด้วยกล่องเลื่อน" กล่าวคือ ระยะทางที่จะย้ายองค์ประกอบติดหนึบ (เพื่อให้ปรากฏซึ่งแนบอยู่กับองค์ประกอบอื่นหรือวิวพอร์ต) คือ คำนวณก่อนการเปลี่ยนรูปแบบอื่นๆ จะมีผล ไม่ใช่หลัง ซึ่งหมายความว่า ซึ่งก็คล้ายกับตัวอย่างที่เลื่อนผ่านก่อนหน้านี้ ถ้าการคำนวณออฟเซ็ต ที่ขนาด 300 พิกเซล คุณจะมีโอกาสใหม่ในการใช้มุมมอง (หรือการเปลี่ยนรูปแบบอื่นๆ) เพื่อจัดการค่าออฟเซ็ต 300px ก่อนที่จะนำไปใช้กับค่าใดๆ ติดหนึบ จากองค์ประกอบเหล่านี้
เมื่อใช้ 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);
}
ซึ่งจะเป็นการคืนค่าเอฟเฟกต์พารัลแลกซ์สำหรับ Mobile Safari ซึ่งเป็นข่าวดีมาก กลม!
คำเตือนเกี่ยวกับตำแหน่งติดหนึบ
แต่มีข้อแตกต่างดังนี้ position: sticky
จะเปลี่ยนแปลง
กลไกพารัลแลกซ์ ตำแหน่งติดหนึบจะพยายามทำให้องค์ประกอบ
คอนเทนเนอร์แบบเลื่อนได้ ส่วนเวอร์ชันที่ไม่ติดหนึบนั้นทำไม่ได้ ซึ่งหมายความว่า
พารัลแลกซ์ที่มี Sticky จบลงด้วยการผกผันกับองค์ประกอบที่ไม่มีรายการต่อไปนี้
- ด้วย
position: sticky
ยิ่งองค์ประกอบอยู่ใกล้กว่า z=0 ยิ่งน้อยลง ย้ายที่ - ที่ไม่มี
position: sticky
ยิ่งองค์ประกอบอยู่ใกล้มาก z=0 ก็จะมากขึ้น ขยับได้
ถ้าทุกอย่างดูเป็นนามธรรมไปหน่อย ลองดูการสาธิตนี้ โดย Robert Flack แสดงให้เห็นว่าองค์ประกอบมีลักษณะการทำงานที่แตกต่างกันหรือไม่ เมื่อมีและไม่มี Sticky ของคุณ เพื่อให้เห็นความแตกต่าง คุณต้องใช้ Chrome Canary (ซึ่งเป็นเวอร์ชัน 56) ขณะเขียน) หรือ Safari
![ภาพหน้าจอแบบมุมมองพารัลแลกซ์](https://developer.chrome.com/static/blog/performant-parallaxing/image/parallax-perspective-scre-e5a0b46e4d2ad.jpg?authuser=0&hl=th)
การสาธิตโดย Robert Flack ที่แสดงให้เห็นว่า
position: sticky
ส่งผลต่อการเลื่อนพารัลแลกซ์
ข้อบกพร่องต่างๆ และวิธีแก้ปัญหา
แต่ก็มีก้อนและตุ่มที่จำเป็นต้องมี ปรับเรียบ:
- การรองรับแบบติดหนึบไม่สอดคล้องกัน การสนับสนุนยังคงดำเนินการใน
Chrome, Edge ไม่มีการรองรับอย่างสมบูรณ์ และ Firefox มีข้อบกพร่องเกี่ยวกับการวาดภาพเมื่อ Sticky รวมอยู่ในการแปลงมุมมอง ในกรณีดังกล่าว
คุณควรเพิ่มโค้ดเล็กๆ เพื่อเพิ่มเฉพาะ
position: sticky
(แท็ก-webkit-
เวอร์ชันนำหน้า) เมื่อจำเป็น ซึ่งมีไว้สำหรับ Mobile Safari เท่านั้น - ผลลัพธ์ไม่ได้ "แค่ใช้งานได้" ใน Edge Edge พยายามรองรับการเลื่อนที่ ระดับระบบปฏิบัติการ ซึ่งโดยทั่วไปก็เป็นสิ่งที่ดี แต่ในกรณีนี้จะเป็นการป้องกันไม่ให้เกิด จากการตรวจจับการเปลี่ยนมุมมองระหว่างการเลื่อน หากต้องการแก้ไข คุณสามารถเพิ่ม องค์ประกอบตำแหน่งคงที่ เนื่องจากดูเหมือนว่าจะเปลี่ยน Edge ไปใช้ วิธีการเลื่อนที่ไม่ใช่ระบบปฏิบัติการ และช่วยให้มั่นใจว่าจะมีการอธิบายการเปลี่ยนแปลงมุมมองด้วย
- "เนื้อหาในหน้าเว็บเพิ่มขึ้นเรื่อยๆ" เบราว์เซอร์จำนวนมากรองรับการปรับขนาด
เมื่อตัดสินใจว่าเนื้อหาในหน้าจะใหญ่แค่ไหน แต่น่าเสียดายที่ Chrome และ Safari
ไม่ได้พิจารณาถึงมุมมอง สไตรค์
หากมี - เช่น ใช้มาตราส่วน 3x กับองค์ประกอบหนึ่งๆ คุณอาจ
เห็นแถบเลื่อนและสิ่งอื่นๆ ที่คล้ายกัน แม้ว่าองค์ประกอบจะอยู่ที่ 1x หลัง
ใช้
perspective
แล้ว ปัญหานี้แก้ไขได้โดยทำดังนี้ การปรับขนาดองค์ประกอบจากมุมขวาล่าง (ที่มีtransform-origin: bottom right
) ซึ่งได้ผลเนื่องจากจะทำให้องค์ประกอบที่มีขนาดใหญ่เกินไปมีขนาดใหญ่ขึ้นเป็น "ภูมิภาคเชิงลบ" (โดยทั่วไปจะอยู่ด้านซ้ายบน) ของพื้นที่ที่เลื่อนได้ เลื่อนได้ ภูมิภาคจะไม่อนุญาตให้คุณเห็นหรือเลื่อนไปยังเนื้อหาในภูมิภาคเชิงลบ
บทสรุป
พารัลแลกซ์เป็นเอฟเฟกต์สนุกๆ เมื่อใช้อย่างรอบคอบ อย่างที่เห็น เป็นไปได้ว่า เพื่อติดตั้งใช้งานให้มีประสิทธิภาพ ทำงานแบบคู่ต่อด้าน และข้ามเบราว์เซอร์ได้ เนื่องจากต้องใช้ความพยายามทางคณิตศาสตร์เล็กน้อย และ Boilerplate เพื่อให้ได้ผลลัพธ์ที่ต้องการ เราได้รวมไลบรารีตัวช่วยเล็กๆ ไว้ และตัวอย่าง ซึ่งดูได้ในที่เก็บตัวอย่างองค์ประกอบ UI ของ GitHub
มาสนุกกันและบอกเราว่าคุณเป็นอย่างไรบ้าง