這是我參與8月更文挑戰的第7天,活動詳情查看:8月更文挑戰css
瀏覽器經過對不一樣事件的處理來知足各類交互需求,這一部分咱們一塊兒看看從瀏覽器的視角,事件是什麼,在此咱們先主要考慮鼠標事件。web
在瀏覽器的看來,用戶的全部手勢都是輸入,鼠標滾動,懸置,點擊等等都是。瀏覽器
當用戶在屏幕上觸發諸如 touch 等手勢時,首先收到手勢信息的是 Browser process, 不過 Browser process 只會感知到在哪裏發生了手勢,對 tab 內內容的處理是仍是由渲染進程控制的。markdown
事件發生時,瀏覽器進程會發送事件類型及相應的座標給渲染進程,渲染進程隨後找到事件對象並執行全部綁定在其上的相關事件處理函數。app
前文中,咱們提到過合成器能夠獨立於主線程以外經過合成柵格化層平滑的處理滾動。若是頁面中沒有綁定相關事件,組合器線程能夠獨立於主線程建立組合幀。若是頁面綁定了相關事件處理器,主線程就不得不出來工做了。這時候合成器線程會怎麼處理呢?函數
這裏涉及到一個專業名詞「理解非快速滾動區域(non-fast scrollable region)」因爲執行 JS 是主線程的工做,當頁面合成時,合成器線程會標記頁面中綁定有事件處理器的區域爲 non-fast scrollable region ,若是存在這個標註,合成器線程會把發生在此處的事件發送給主線程,若是事件不是發生在這些區域,合成器線程則會直接合成新的幀而不用等到主線程的響應。post
web 開發中經常使用的事件處理模式是事件委託,基於事件冒泡,咱們經常在最頂層綁定事件:性能
document.body.addEventListener('touchstart',
event => {
if (event.target === area) {
event.preventDefault();
}
}
);
複製代碼
上述作法很常見,可是若是從瀏覽器的角度看,整個頁面都成了 non-fast scrollable region 了。測試
這意味着即便操做的是頁面無綁定事件處理器的區域,每次輸入時,合成器線程也須要和主線程通訊並等待反饋,流暢的合成器獨立處理合成幀的模式就失效了。優化
因爲事件綁定在最頂部,整個頁面都成爲了 non-fast scrollable region
爲了防止這種狀況,咱們能夠爲事件處理器傳遞 passive: true
作爲參數,這樣寫就能讓瀏覽器即監聽相關事件,又讓組合器線程在等等主線程響應前構建新的組合幀。
document.body.addEventListener('touchstart',
event => {
if (event.target === area) {
event.preventDefault()
}
}, {passive: true}
);
複製代碼
不過上述寫法可能又會帶來另一個問題,假設某個區域你只想要水平滾動,使用 passive: true
能夠實現平滑滾動,可是垂直方向的滾動可能會先於event.preventDefault()
發生,此時能夠經過 event.cancelable
來防止這種狀況。
document.body.addEventListener('pointermove', event => {
if (event.cancelable) {
event.preventDefault(); // block the native scroll
/*
* do what you want the application to do here
*/
}
}, {passive: true});
複製代碼
也可使用css屬性 touch-action
來徹底消除事件處理器的影響,如:
#area {
touch-action: pan-x;
}
複製代碼
查找到事件對象
當組合器線程發送輸入事件給主線程時,主線程首先會進行命中測試(hit test)來查找對應的事件目標,命中測試會基於渲染過程當中生成的繪製記錄( paint records )查找事件發生座標下存在的元素。
事件的優化
通常咱們屏幕的刷新速率爲 60fps,可是某些事件的觸發量會不止這個值,出於優化的目的,Chrome 會合並連續的事件(如 wheel, mousewheel, mousemove, pointermove, touchmove ),並延遲到下一幀渲染時候執行 。
而如 keydown, keyup, mouseup, mousedown, touchstart, 和 touchend 等非連續性事件則會當即被觸發。
合併事件雖然能提示性能,可是若是你的應用是繪畫等,則很難繪製一條平滑的曲線了,此時可使用 getCoalescedEvents
API 來獲取組合的事件。示例代碼以下:
window.addEventListener('pointermove', event => {
const events = event.getCoalescedEvents();
for (let event of events) {
const x = event.pageX;
const y = event.pageY;
// draw a line using x and y coordinates.
}
});
複製代碼
花了很久來整理上面的內容,整理的過程收穫還挺大的,也但願這篇筆記能對你有所啓發,若是有任何疑問,歡迎一塊兒來討論。