requestAnimationFrame API - มีความแม่นยำระดับวินาทีย่อย

หากเคยใช้ requestAnimationFrame คุณจะได้เห็นภาพวาดที่ซิงค์กับอัตราการรีเฟรชของหน้าจอ ซึ่งทำให้ได้ภาพเคลื่อนไหวที่สมจริงที่สุด นอกจากนี้ คุณยังช่วยลดเสียงพัดลม CPU และประหยัดแบตเตอรี่ของผู้ใช้เมื่อผู้ใช้เปลี่ยนไปใช้แท็บอื่น

อย่างไรก็ตาม เรากําลังจะมีการเปลี่ยนแปลง API บางส่วน การประทับเวลาที่ส่งไปยังฟังก์ชันการเรียกกลับจะเปลี่ยนจากการประทับเวลาแบบ Date.now() ทั่วไปเป็นการวัดความละเอียดสูงของจำนวนมิลลิวินาทีแบบทศนิยมลอยตัวนับตั้งแต่เปิดหน้าเว็บ หากใช้ค่านี้ คุณจะต้องอัปเดตโค้ดตามคำอธิบายด้านล่าง

เราขออธิบายให้ชัดเจนว่า

// assuming requestAnimationFrame method has been normalized for all vendor prefixes..
requestAnimationFrame(function(timestamp){
    // the value of timestamp is changing
});

หากคุณใช้requestAnimFrameชิมทั่วไปที่ระบุไว้ที่นี่ แสดงว่าคุณไม่ได้ใช้ค่าการประทับเวลา คุณไม่ต้องกังวล :)

ทำไม

เหตุผล rAF จะช่วยให้คุณได้รับ 60 FPS ที่ดีที่สุด ซึ่งเหมาะที่สุด และ 60 FPS หมายถึง 16.7 มิลลิวินาทีต่อเฟรม แต่การวัดด้วยจำนวนเต็มมิลลิวินาทีหมายความว่าความแม่นยำของเราคือ 1/16 สำหรับทุกสิ่งที่เราต้องการสังเกตและกำหนดเป้าหมาย

การเปรียบเทียบกราฟ 16 มิลลิวินาทีกับ 16 มิลลิวินาทีแบบจำนวนเต็ม

ดังที่เห็นด้านบน แถบสีน้ำเงินแสดงถึงเวลาสูงสุดที่คุณมีให้ทำงานทั้งหมดก่อนที่จะวาดเฟรมใหม่ (ที่ 60 fps) คุณอาจทำสิ่งต่างๆ มากกว่า 16 อย่าง แต่การใช้มิลลิวินาทีแบบจำนวนเต็มจะทำให้คุณกำหนดเวลาและวัดผลได้เฉพาะในจำนวนที่เพิ่มขึ้นแบบเป็นก้อนๆ เท่านั้น คุณภาพยังไม่ดีพอ

ตัวจับเวลาความละเอียดสูงช่วยแก้ปัญหานี้โดยแสดงตัวเลขที่แม่นยำกว่ามาก

Date.now()         //  1337376068250
performance.now()  //  20303.427000007

ปัจจุบันตัวจับเวลาความละเอียดสูงพร้อมใช้งานใน Chrome เป็น window.performance.webkitNow() และโดยทั่วไปค่านี้จะเท่ากับค่าอาร์กิวเมนต์ใหม่ที่ส่งไปยังการเรียกกลับ rAF เมื่อข้อกำหนดได้รับการพัฒนาตามมาตรฐานเพิ่มเติม วิธีการนี้จะยกเลิกคำนำหน้าและพร้อมใช้งานผ่าน performance.now()

นอกจากนี้ คุณยังจะเห็นค่า 2 ค่าข้างต้นแตกต่างกันหลายลำดับขั้น performance.now() คือการวัดค่ามิลลิวินาทีแบบทศนิยมลอยตัวนับตั้งแต่หน้าเว็บนั้นๆ เริ่มโหลด (performance.navigationStart นั่นเอง)

ใช้งานอยู่

ปัญหาหลักที่ทำให้เกิดภาพตัดคือไลบรารีภาพเคลื่อนไหวที่ใช้รูปแบบการออกแบบนี้

function MyAnimation(duration) {
    this.startTime = Date.now();
    this.duration = duration;
    requestAnimFrame(this.tick.bind(this));
}
MyAnimation.prototype.tick = function(time) {
    var now = Date.now();
    if (time > now) {
        this.dispatchEvent("ended");
        return;
    }
    ...
    requestAnimFrame(this.tick.bind(this));
}

การแก้ไขเพื่อแก้ไขปัญหานี้นั้นง่ายมาก เพียงเพิ่ม startTime และ now เพื่อใช้ window.performance.now()

this.startTime = window.performance.now ?
                    (performance.now() + performance.timing.navigationStart) :
                    Date.now();

นี่เป็นการใช้งานที่ค่อนข้างหยาบคาย ไม่ได้ใช้เมธอด now() ที่มีคำนำหน้า และถือว่ารองรับ Date.now() ซึ่งไม่มีใน IE8

การตรวจหาองค์ประกอบ

หากคุณไม่ได้ใช้รูปแบบข้างต้นและต้องการระบุประเภทของค่าการเรียกกลับที่ได้รับ ให้ใช้เทคนิคนี้

requestAnimationFrame(function(timestamp){

    if (timestamp < 1e12){
        // .. high resolution timer
    } else {
        // integer milliseconds since unix epoch
    }

    // ...

การตรวจสอบ if (timestamp < 1e12) เป็นการตรวจสอบคร่าวๆ เพื่อดูว่าจำนวนที่เรากำลังจัดการมีมากน้อยเพียงใด ในทางเทคนิคแล้ว การตรวจอาจให้ผลบวกปลอมได้ในกรณีที่หน้าเว็บเปิดอยู่อย่างต่อเนื่องเป็นเวลา 30 ปีเท่านั้น แต่เราไม่สามารถทดสอบว่าค่านี้เป็นตัวเลขทศนิยม (ไม่ใช่ปัดเศษเป็นจำนวนเต็ม) หรือไม่ หากขอตัวจับเวลาความละเอียดสูงมากพอ คุณจะได้รับค่าจำนวนเต็มในบางจุด

เราวางแผนที่จะเผยแพร่การเปลี่ยนแปลงนี้ใน Chrome 21 ดังนั้นหากคุณใช้ประโยชน์จากพารามิเตอร์การเรียกกลับนี้แล้ว โปรดอัปเดตโค้ดของคุณ