TL;DR
- Chrome 60 จะลดความกระตุกโดยการลดความถี่ของเหตุการณ์ ซึ่งจะช่วยเพิ่มความสอดคล้องของเวลาเฟรม
- เมธอด
getCoalescedEvents()
ที่เปิดตัวใน Chrome 58 มีข้อมูลเหตุการณ์มากมายเช่นเดียวกับที่คุณเคยได้รับมาตลอด
เว็บต้องมอบประสบการณ์การใช้งานที่ราบรื่น ระยะเวลาระหว่างการรับเหตุการณ์อินพุตกับเวลาที่ภาพอัปเดตจริงนั้นสำคัญ และโดยทั่วไปแล้วการทํางานน้อยลงก็สำคัญเช่นกัน ในช่วงไม่กี่เวอร์ชันที่ผ่านมาของ Chrome เราได้ลดเวลาในการตอบสนองของอินพุตในอุปกรณ์เหล่านี้
ใน Chrome 60 เราจะทำการเปลี่ยนแปลงที่ทำให้เหตุการณ์เหล่านี้เกิดขึ้นน้อยลง แต่เพิ่มความละเอียดของข้อมูลที่ให้ไว้เพื่อประสิทธิภาพและความราบรื่น เช่นเดียวกับตอนที่เปิดตัว Jelly Bean และเปิดตัว Choreographer ซึ่งจัดแนวอินพุตใน Android เรากำลังนำอินพุตที่ปรับแนวเฟรมมาสู่เว็บในแพลตฟอร์มทั้งหมด
แต่บางครั้งคุณก็อาจต้องการเหตุการณ์เพิ่มเติม ดังนั้นใน Chrome 58 เราจึงติดตั้งใช้งานเมธอดที่ชื่อ getCoalescedEvents()
ซึ่งช่วยให้แอปพลิเคชันของคุณเรียกข้อมูลเส้นทางแบบเต็มของเคอร์เซอร์ได้แม้ว่าจะได้รับเหตุการณ์น้อยลง
มาพูดถึงความถี่ของเหตุการณ์กันก่อน
การลดความถี่ของเหตุการณ์
มาทําความเข้าใจข้อมูลเบื้องต้นกัน หน้าจอสัมผัสจะส่งอินพุตที่ 60-120Hz และโดยทั่วไปเมาส์จะส่งอินพุตที่ 100Hz (แต่อาจสูงถึง 2,000Hz) แต่อัตราการรีเฟรชทั่วไปของจอภาพคือ 60 Hz ข้อมูลนี้หมายความว่าอย่างไร ซึ่งหมายความว่าเราได้รับข้อมูลในอัตราที่สูงกว่าที่เราอัปเดตการแสดงผล มาดูไทม์ไลน์ประสิทธิภาพจากเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์สําหรับภาพพิมพ์แคนวาสแบบง่ายๆ ของแอปวาดภาพกัน
ในรูปภาพด้านล่าง เมื่อปิดใช้อินพุตที่สอดคล้องกับ requestAnimationFrame()
คุณจะเห็นบล็อกการประมวลผลหลายบล็อกต่อเฟรมที่มีเวลาเฟรมไม่สอดคล้องกัน
บล็อกสีเหลืองเล็กๆ บ่งบอกการทดสอบ Hit สำหรับสิ่งต่างๆ เช่น เป้าหมายของเหตุการณ์ DOM, การส่งเหตุการณ์, เรียกใช้ JavaScript, อัปเดตโหนดที่วางเมาส์เหนือ และอาจคํานวณเลย์เอาต์และสไตล์อีกครั้ง
แล้วทําไมเราถึงต้องทํางานเพิ่มที่ไม่ก่อให้เกิดการอัปเดตภาพ เราไม่อยากทํางานที่ไม่เป็นประโยชน์ต่อผู้ใช้ในท้ายที่สุด ตั้งแต่ Chrome 60 เป็นต้นไปไป ไปป์ไลน์อินพุตจะเลื่อนเวลาการส่งเหตุการณ์ต่อเนื่อง (wheel
,
mousewheel
,
touchmove
,
pointermove
,
mousemove
) และส่งเหตุการณ์เหล่านั้นทันทีก่อนที่มีการเรียกกลับ requestAnimationFrame()
เกิดขึ้น ในรูปภาพด้านล่าง (ที่เปิดใช้ฟีเจอร์) คุณจะเห็นเวลาเฟรมที่สอดคล้องกันมากขึ้นและใช้เวลาประมวลผลเหตุการณ์น้อยลง
เราได้ทำการทดสอบโดยเปิดใช้ฟีเจอร์นี้ใน Canary และ Dev Channel และพบว่าเราทำการทดสอบ Hit น้อยลง 35% ซึ่งทำให้เธรดหลักพร้อมทำงานบ่อยขึ้น
หมายเหตุสําคัญที่นักพัฒนาเว็บควรทราบคือ ระบบจะส่งเหตุการณ์แบบไม่ต่อเนื่อง (เช่น keydown
,
keyup
,
mouseup
,
mousedown
,
touchstart
,
touchend
) ที่จะเกิดขึ้นทันทีพร้อมกับเหตุการณ์ที่รอดําเนินการอยู่ โดยยังคงลําดับสัมพัทธ์ไว้ เมื่อเปิดใช้ฟีเจอร์นี้ ระบบจะปรับปรุงงานจำนวนมากให้มีประสิทธิภาพมากขึ้นในลูปเหตุการณ์ตามปกติ ซึ่งจะให้ช่วงเวลาอินพุตที่สอดคล้องกัน ซึ่งจะช่วยให้เหตุการณ์ต่อเนื่องทำงานได้สอดคล้องกันกับเหตุการณ์ scroll
และ resize
ที่ปรับให้เข้ากับขั้นตอนการวนซ้ำของเหตุการณ์ใน Chrome อยู่แล้ว
เราพบว่าแอปพลิเคชันส่วนใหญ่ที่ใช้เหตุการณ์ดังกล่าวไม่มีการใช้ความถี่ที่สูงขึ้น Android ได้ปรับเหตุการณ์มาหลายปีแล้ว จึงไม่มีอะไรใหม่ แต่เว็บไซต์อาจเห็นเหตุการณ์ที่ละเอียดน้อยลงในแพลตฟอร์มเดสก์ท็อป ปัญหาเกี่ยวกับเธรดหลักที่ทำงานไม่ราบรื่นซึ่งทำให้อินพุตทำงานไม่ราบรื่นนั้นเกิดขึ้นมาตลอด ซึ่งหมายความว่าคุณอาจเห็นการกระโดดของตำแหน่งทุกครั้งที่แอปพลิเคชันทำงานอยู่ ทำให้ไม่สามารถทราบได้ว่าเคอร์เซอร์ย้ายจากจุดหนึ่งไปยังอีกจุดหนึ่งได้อย่างไร
getCoalescedEvents()
วิธี
ดังที่ได้กล่าวไปแล้ว สถานการณ์ที่แอปพลิเคชันต้องการทราบเส้นทางแบบเต็มของเคอร์เซอร์นั้นเกิดขึ้นไม่บ่อยนัก ดังนั้น ในการแก้ไขปัญหาที่คุณเห็นการเพิ่มขึ้นอย่างมากและเหตุการณ์ที่ลดลง ใน Chrome 58 เราได้เปิดตัวส่วนขยายสำหรับเหตุการณ์เคอร์เซอร์ที่เรียกว่า getCoalescedEvents()
และด้านล่างนี้คือตัวอย่างวิธีที่ระบบซ่อนความกระตุกในเธรดหลักจากแอปพลิเคชันหากคุณใช้ API นี้
คุณจะเข้าถึงอาร์เรย์ของเหตุการณ์ที่ผ่านมาซึ่งทําให้เกิดเหตุการณ์ได้แทนที่จะได้รับเหตุการณ์เดียว Android, iOS และ Windows ล้วนมี API ที่คล้ายกันมากใน SDK เนทีฟ และเรากำลังแสดง API ที่คล้ายกันในเว็บ
โดยปกติแล้ว แอปวาดภาพอาจวาดจุดโดยดูที่ออฟเซ็ตในเหตุการณ์ต่อไปนี้
window.addEventListener("pointermove", function(event) {
drawPoint(event.pageX, event.pageY);
});
โค้ดนี้เปลี่ยนให้ใช้อาร์เรย์ของเหตุการณ์ได้ง่ายๆ ดังนี้
window.addEventListener("pointermove", function(event) {
var events = 'getCoalescedEvents' in event ? event.getCoalescedEvents() : [event];
for (let e of events) {
drawPoint(e.pageX, e.pageY);
}
});
โปรดทราบว่าระบบอาจไม่ป้อนข้อมูลพร็อพเพอร์ตี้บางรายการในเหตุการณ์ที่ผสาน เนื่องจากเหตุการณ์ที่รวมกันไม่ได้ส่งจริง แต่เป็นเพียงเหตุการณ์ที่เกิดขึ้นพร้อมกัน ระบบจึงไม่ทดสอบ Hit กับเหตุการณ์เหล่านี้ ฟิลด์บางฟิลด์ เช่น currentTarget
และ eventPhase
จะมีค่าเริ่มต้น การเรียกใช้เมธอดที่เกี่ยวข้องกับการส่ง เช่น stopPropagation()
หรือ preventDefault()
จะไม่มีผลกับเหตุการณ์หลัก