จะเกิดอะไรขึ้นหากเราบอกว่ามีวิวพอร์ตมากกว่า 1 รายการ
BRRRRAAAAAAAMMMMMMMMMM
และวิวพอร์ตที่คุณใช้อยู่ตอนนี้คือวิวพอร์ตภายในวิวพอร์ต
BRRRRAAAAAAAMMMMMMMMMM
และบางครั้งข้อมูลที่ DOM แสดงให้คุณเห็นจะอ้างอิงถึงวิวพอร์ตใดวิวพอร์ตหนึ่งเท่านั้น
BRRRRAAAAM… เดี๋ยวก่อน อะไรนะ
เป็นเรื่องจริง โปรดดูตัวอย่างต่อไปนี้
วิวพอร์ตเลย์เอาต์กับวิวพอร์ตภาพ
วิดีโอด้านบนแสดงการเลื่อนหน้าเว็บและการซูมเข้า/ออกด้วยสองนิ้ว รวมถึงแผนที่ขนาดเล็กทางด้านขวาที่แสดงตำแหน่งของวิวพอร์ตภายในหน้า
ทุกอย่างค่อนข้างตรงไปตรงมาในระหว่างการเลื่อนปกติ พื้นที่สีเขียวแสดงวิวพอร์ตเลย์เอาต์ ซึ่งรายการ position: fixed
ยึดตาม
สิ่งต่างๆ จะดูแปลกๆ เมื่อใช้การซูมด้วยการบีบและกางนิ้ว กล่องสีแดงแสดงวิวพอร์ตที่มองเห็นได้ ซึ่งเป็นส่วนของหน้าเว็บที่เรามองเห็นได้จริง วิวพอร์ตนี้จะเลื่อนไปมาได้ ขณะที่องค์ประกอบ position: fixed
จะยังคงอยู่ที่เดิมโดยยึดอยู่กับวิวพอร์ตเลย์เอาต์ หากเลื่อนที่ขอบเขตของวิวพอร์ตเลย์เอาต์ ระบบจะลากวิวพอร์ตเลย์เอาต์ไปด้วย
การปรับปรุงความเข้ากันได้
ขออภัย Web API จะใช้วิวพอร์ตใดก็ตามที่อ้างอิง และจะไม่สอดคล้องกันในแต่ละเบราว์เซอร์
เช่น element.getBoundingClientRect().y
จะแสดงผลออฟเซตภายในวิวพอร์ตเลย์เอาต์ เยี่ยม แต่บ่อยครั้งที่เราต้องการตำแหน่งภายในหน้าเว็บ เราจึงเขียนดังนี้
element.getBoundingClientRect().y + window.scrollY
อย่างไรก็ตาม เบราว์เซอร์จํานวนมากใช้วิวพอร์ตภาพสําหรับ window.scrollY
ซึ่งหมายความว่าโค้ดข้างต้นจะใช้งานไม่ได้เมื่อผู้ใช้ใช้การซูมด้วยสองนิ้ว
Chrome 61 เปลี่ยน window.scrollY
เพื่ออ้างอิงวิวพอร์ตเลย์เอาต์แทน ซึ่งหมายความว่าโค้ดข้างต้นจะใช้งานได้แม้ขณะที่ซูมด้วยสองนิ้ว อันที่จริงแล้ว เบราว์เซอร์กําลังค่อยๆ เปลี่ยนพร็อพเพอร์ตี้ตําแหน่งทั้งหมดให้อ้างอิงถึงวิวพอร์ตของเลย์เอาต์
ยกเว้นพร็อพเพอร์ตี้ใหม่ 1 แห่ง…
การแสดงวิวพอร์ตภาพต่อสคริปต์
API ใหม่จะแสดงวิวพอร์ตภาพเป็น window.visualViewport
นี่เป็นข้อกำหนดฉบับร่างที่มีการอนุมัติข้ามเบราว์เซอร์ และกำลังจะเปิดตัวใน Chrome 61
console.log(window.visualViewport.width);
window.visualViewport
แสดงข้อมูลต่อไปนี้
ที่พัก visualViewport แห่ง |
|
---|---|
offsetLeft
|
ระยะห่างระหว่างขอบด้านซ้ายของวิวพอร์ตภาพกับวิวพอร์ตเลย์เอาต์เป็นพิกเซล CSS |
offsetTop
|
ระยะห่างระหว่างขอบบนของวิวพอร์ตภาพกับวิวพอร์ตเลย์เอาต์เป็นพิกเซล CSS |
pageLeft
|
ระยะห่างระหว่างขอบด้านซ้ายของวิวพอร์ตภาพกับขอบด้านซ้ายของเอกสารเป็นพิกเซล CSS |
pageTop
|
ระยะห่างระหว่างขอบบนของวิวพอร์ตภาพกับขอบบนของเอกสารเป็นพิกเซล CSS |
width
|
ความกว้างของวิวพอร์ตภาพในพิกเซล CSS |
height
|
ความสูงของวิวพอร์ตภาพในพิกเซล CSS |
scale
|
มาตราส่วนที่เกิดจากการบีบนิ้วเพื่อซูม หากเนื้อหามีขนาดใหญ่ขึ้น 2 เท่าเนื่องจากการซูม ผลลัพธ์ที่ได้จะเป็น 2 การดำเนินการนี้ไม่ได้รับผลกระทบจาก
devicePixelRatio
|
นอกจากนี้ยังมีกิจกรรมอีก 2 รายการ ได้แก่
window.visualViewport.addEventListener('resize', listener);
visualViewport เหตุการณ์ |
|
---|---|
resize
|
เริ่มทํางานเมื่อ width , height หรือ scale มีการเปลี่ยนแปลง
|
scroll
|
เริ่มทํางานเมื่อ offsetLeft หรือ offsetTop มีการเปลี่ยนแปลง
|
สาธิต
วิดีโอตอนต้นของบทความนี้สร้างขึ้นโดยใช้ visualViewport
ลองดูใน Chrome 61 ขึ้นไป โดยจะใช้ visualViewport
เพื่อทำให้แผนที่ขนาดเล็กติดอยู่ที่ด้านขวาบนของวิดเจ็ตภาพ และจะใช้การแปลงสเกลแบบกลับกันเพื่อให้แผนที่ปรากฏในขนาดเดิมเสมอ แม้จะซูมด้วยสองนิ้ว
ข้อควรระวัง
เหตุการณ์จะทริกเกอร์ก็ต่อเมื่อวิวพอร์ตภาพมีการเปลี่ยนแปลงเท่านั้น
ดูเหมือนว่าจะเป็นเรื่องชัดเจนที่ต้องระบุ แต่ฉันก็เจอปัญหานี้เมื่อเล่นกับ visualViewport
เป็นครั้งแรก
หากวิดเจ็ตในหน้าเว็บปรับขนาด แต่วิดเจ็ตภาพไม่ปรับขนาด คุณจะไม่ได้รับการเรียกเหตุการณ์ resize
อย่างไรก็ตาม การที่วิวพอร์ตของเลย์เอาต์ปรับขนาดโดยไม่ทำให้วิวพอร์ตภาพมีการเปลี่ยนแปลงความกว้าง/ความสูงด้วยนั้นเป็นเรื่องผิดปกติ
สิ่งที่น่ารำคาญจริงๆ คือการสไลด์ หากมีการเลื่อน แต่วิวพอร์ตภาพยังคงนิ่งเมื่อเทียบกับวิวพอร์ตเลย์เอาต์ คุณจะไม่ได้รับการจัดกิจกรรม scroll
ใน visualViewport
ซึ่งกรณีนี้พบได้บ่อยมาก ในระหว่างการเลื่อนเอกสารตามปกติ วิวพอร์ตภาพจะยังคงล็อกอยู่ที่ด้านซ้ายบนของวิวพอร์ตเลย์เอาต์ ดังนั้น scroll
จะไม่ทํางานใน visualViewport
หากต้องการทราบการเปลี่ยนแปลงทั้งหมดในวิวพอร์ตภาพ ซึ่งรวมถึง pageTop
และ pageLeft
คุณจะต้องฟังเหตุการณ์การเลื่อนของหน้าต่างด้วย
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
window.addEventListener('scroll', update);
หลีกเลี่ยงการทํางานซ้ำกับผู้ฟังหลายราย
เช่นเดียวกับการฟัง scroll
และ resize
ในหน้าต่าง คุณมีแนวโน้มที่จะเรียกใช้ฟังก์ชัน "อัปเดต" บางประเภท อย่างไรก็ตาม เป็นเรื่องปกติที่เหตุการณ์เหล่านี้หลายรายการจะเกิดขึ้นพร้อมกัน หากผู้ใช้ปรับขนาดหน้าต่าง ระบบจะทริกเกอร์ resize
แต่ทริกเกอร์ scroll
บ่อยครั้งด้วย หลีกเลี่ยงการจัดการการเปลี่ยนแปลงหลายครั้งเพื่อปรับปรุงประสิทธิภาพ
// Add listeners
visualViewport.addEventListener('scroll', update);
visualViewport.addEventListener('resize', update);
addEventListener('scroll', update);
let pendingUpdate = false;
function update() {
// If we're already going to handle an update, return
if (pendingUpdate) return;
pendingUpdate = true;
// Use requestAnimationFrame so the update happens before next render
requestAnimationFrame(() => {
pendingUpdate = false;
// Handle update here
});
}
เราได้ยื่นเรื่องปัญหาเกี่ยวกับข้อกำหนดสำหรับเรื่องนี้แล้ว เนื่องจากเราคิดว่าอาจมีวิธีอื่นที่ดีกว่า เช่น เหตุการณ์ update
รายการเดียว
ตัวแฮนเดิลเหตุการณ์ไม่ทํางาน
เนื่องจากข้อบกพร่องของ Chrome การดำเนินการนี้จึงไม่ทำงาน
มีข้อบกพร่อง – ใช้เครื่องจัดการเหตุการณ์
visualViewport.onscroll = () => console.log('scroll!');
ให้ดำเนินการต่อไปนี้แทน
ใช้งานได้ – ใช้ Listener เหตุการณ์
visualViewport.addEventListener('scroll', () => console.log('scroll'));
ค่าออฟเซ็ตมีการปัดเศษ
เราคิดว่า (หวังว่า) นี่จะเป็นข้อบกพร่องอีกอย่างหนึ่งของ Chrome
offsetLeft
และ offsetTop
จะปัดเศษ ซึ่งจะค่อนข้างไม่ถูกต้องเมื่อผู้ใช้ซูมเข้า คุณจะเห็นปัญหานี้ในการสาธิต หากผู้ใช้ซูมเข้าและเลื่อนไปช้าๆ แผนที่ขนาดเล็กจะกระโดดไปมาระหว่างพิกเซลที่ไม่ได้ซูม
อัตราเหตุการณ์ช้า
เหตุการณ์เหล่านี้จะไม่ทริกเกอร์ทุกเฟรม เช่นเดียวกับเหตุการณ์ resize
และ scroll
อื่นๆ โดยเฉพาะในอุปกรณ์เคลื่อนที่ คุณจะเห็นปัญหานี้ระหว่างการสาธิต เมื่อคุณใช้สองนิ้วซูมเข้า มินิแผนที่จะมีปัญหาในการล็อกกับวิวพอร์ต
การช่วยเหลือพิเศษ
ในการสาธิต เราใช้ visualViewport
เพื่อโต้ตอบกับการซูมเข้า/ออกด้วยสองนิ้วของผู้ใช้ การดำเนินการนี้เหมาะสมกับการแสดงตัวอย่างนี้ แต่คุณควรพิจารณาอย่างรอบคอบก่อนที่จะทำสิ่งใดก็ตามที่ลบล้างความต้องการของผู้ใช้ในการซูมเข้า
visualViewport
สามารถใช้เพื่อปรับปรุงการช่วยเหลือพิเศษได้ เช่น หากผู้ใช้กำลังซูมเข้า คุณอาจเลือกซ่อนรายการposition: fixed
ตกแต่งเพื่อไม่ให้รบกวนผู้ใช้ แต่อย่าลืมตรวจสอบว่าคุณไม่ได้ซ่อนสิ่งที่ผู้ใช้พยายามดูให้ใกล้ขึ้น
คุณอาจพิจารณาโพสต์ไปยังบริการวิเคราะห์เมื่อผู้ใช้ซูมเข้า ซึ่งอาจช่วยคุณระบุหน้าเว็บที่ผู้ใช้พบปัญหาเมื่อซูมระดับเริ่มต้น
visualViewport.addEventListener('resize', () => {
if (visualViewport.scale > 1) {
// Post data to analytics service
}
});
เพียงเท่านี้ก็เรียบร้อยแล้ว visualViewport
เป็น API เล็กๆ ที่ยอดเยี่ยมซึ่งช่วยแก้ปัญหาความเข้ากันได้ได้