Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
88
Help us understand the problem. What are the problem?
Organization

猫でもわかるスクロールイベントパフォーマンス改善ポイント2018

TL;DR

  • パフォーマンスの重要性をおさらい
  • Scroll Jank とは
  • 60fpsとリフレッシュレート
  • スクロールイベントを最適化する

パフォーマンスの重要性をおさらい





検索エンジンと広告収益事例

GoogleとMicrosoftの検索エンジンチームによって2009年に発表された調査

レスポンスの遅延が招く収益への影響:

  • 0.5秒遅延 → -1.2%
  • 1.0秒遅延 → -2.8%
  • 2.0秒遅延 → -4.3%

スクロールイベントは負荷が高い

フロントエンドの実装をしたことのある方は、スクロールイベントを使った実装をしたことも多いはず。

スクロールイベントは発生頻度が高いため、負荷が高くなりがちです。スクロールイベントのリスナーは適切に実装しないとScroll Jankの発生・UXの低下を招いてしまいます。

LT_TechLunch_20180720.003.jpeg

パフォーマンスを出来るだけ下げないような実装事例を紹介していきます。

Scroll Jank とは

ページのスクロールをした際に、スクロールが詰まったような遅延を
「Scroll Jank」と呼びます。

Scroll Jankが起きるパティーン

下記の2点をピックアップします。

  • スクロールイベント内でpreventDefault()が実行されている可能性がある
  • スクロールイベント内で要素のサイズや位置を取得する処理がある(Forced Synchronous Layout が発生する可能性がある)

スクロールイベント内でpreventDefault()が実行されている可能性がある

可能性がある…?🤔

例えば、スクロールイベント内でpreventDefault()が実行されたら、スクロール処理は中止されます。しかしながら、ブラウザ側はイベント内でpreventDefault()が実行されるかどうかを事前に判定できません。(そのイベントが実行が終了するまで判定ができない)
そのため、イベント内の処理が終了するのを待つ(=遅延が発生する)ことになります。

いわゆるScroll Jank 発生原因のほとんどはコレです。

スクロールイベント内で要素のサイズや位置を取得する処理がある(Forced Synchronous Layout が発生する可能性がある)

Forced Synchronous Layoutはレイアウト計算処理で発生します。(「レイアウト処理」が実行されないと期待する値が分からない系の処理)
ただ、近年、こういった処理を実施しなければならない処理は、Intersection Observer や position: sticky を使うことでスマートかつ良いパフォーマンスで実装できるため、減りつつある(はず)。

60fpsとリフレッシュレート

一般的な端末は画面を1秒間に60回リフレッシュする
→ 間隔は、1000ミリ秒 / 60 = およそ16.66ミリ秒

この間隔からズレるとフレームレートが低下して、画面上の描画がブレる(ジャンクが発生)


https://developers.google.com/web/fundamentals/performance/rendering/optimize-javascript-execution?hl=ja

スクロールイベントを最適化する

スクロールイベント内でpreventDefault()がされてない事をブラウザに教える

Event Listener Options passive

この指定で「処理がpreventDefault()を実行していない」という事が明示できるようになった
イベント内の処理が終了を待たず判定が可能になった

指定方法

addEventListenerの第三引数に{passive: true}を指定するだけ

document.addEventListener('scroll', function() {
  // SUGOI SHORI
}, {passive: true});

互換性の問題

Internet Explorer「こんにちは」
image.png
https://caniuse.com/#feat=passive-event-listener

非対応ブラウザで同処理を実行すると、元々のuseCaptureがtrueになってしまう
一応、このpassiveが使えるかどうかの検出は可能

/* "passive" が使えるかどうかを検出 */
var passiveSupported = false;

try {
  window.addEventListener("test", null, Object.defineProperty({}, "passive", { get: function() { passiveSupported = true; } }));
} catch(err) {}

/* リスナーを登録 */
var elem = document.getElementById('elem');

elem.addEventListener('touchmove', function listener() {
  /* do something */
}, passiveSupported ? { passive: true } : false);

未対応ブラウザにOpera miniがあるが、IEのみ回避すれば良いのであれば、単純に下記のように書くことができる。

var elem = document.getElementById('elem');

elem.addEventListener('touchmove', function listener() {
  /* do something */
}, !document.documentMode ? { passive: true } : false);

document.documentModeでIEかどうかを判定

jQueryでpassiveを使う方法は…?

ない

スクロールイベントの処理を間引く

setTimeout

間引き処理として、setTimeoutが広く利用されてきた。
次回の処理をスケジューリングし処理を頻繁に実行させないようにする事ができる。

  • ブラウザ側の準備に関わらず必ず実行される
  • タブが非アクティブ時でも実行される
var timer = null;
var FPS = 1000 / 60;

function func() {
    clearTimeout(timer);
    timer = setTimeout(function() {
      // YABAI SHORI
    }, FPS);
}

document.addEventListener('scroll', func, {passive: true});

※delayに渡す値を
 1000 / 60 (16.66ms)
にしている(60fps相当)

throttle

lodashUnderscore.js に throttle 関数がある

先述のsetTimeoutでの実装と目的は同じ

import { throttle } from 'lodash';
const FPS = 1000 / 60;

document.addEventListener('scroll', throttle(func, FPS), {passive: true});

requestAnimationFrame

処理を待つように時間指定するのではなく、次のフレームのレンダリングが準備が整った時に呼び出される。
ほかの処理に割り込まれてフレームのレンダリングが遅延することなく適切なタイミングで呼び出される。

  • ブラウザの画面リフレッシュと同じタイミングで呼び出される
  • 画面が非アクティブ時には実行されない
  • なにげにIE11でも使える
var ticking = false;

function func() {
  if (!ticking) {
    requestAnimationFrame(function() {
      ticking = false;
      // GREAT SHORI
    });
    ticking = true;
  }
}

document.addEventListener('scroll', func, {passive: true});

デモ

See the Pen requestAnimationFrame vs throttle vs setTimeout by kikuchi hiroyuki (@kikuchi-hiroyuki) on CodePen.

setTimeoutだけ異常にもたつく

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
88
Help us understand the problem. What are the problem?