Webフロントエンドに従事するお前らはいい加減高頻度イベントとレイアウトとスタイリングの付き合い方を考えろ

  • 1275
    いいね
  • 3
    コメント
この記事は最終更新日から1年以上が経過しています。

もうなんかこの際マジで言わせていただくんですけど、知ってるか知らないか分かりませんが世の中にはすごい頻度で呼ばれうるDOMイベントって言うのがいくつかあるわけですよ

例えば

  • scroll
  • mousemove, touchmove
  • devicemotion

辺りですよ。

で、高頻度で呼ばれるって言うことは必然的に処理量が増えるって分かりますよね?????while(1) {}じゃないとはいえUIスレッドに十分影響を与えうる頻度で呼ばれる訳です。分かりますよね????????

そうなると当然そのイベント内で重い処理を行えば人間が認識できるレベルでのレスポンス遅延が起きるっていうのはご理解できますよね?

重い処理っていうのはまぁ想像出来るとは思うんですが例えばよくあるのが

  • DOMのレイアウトプロパティへのアクセス
    • offsetTop、offsetLeft、offsetWidth、offsetHeight
    • scrollTop、scrollLeft、scrollWidth、scrollHeight
    • clientTop、clientLeft、clientWidth、clientHeight
    • getComputedStyle()
  • DOMのスタイルプロパティへのアクセスや変更全般
  • DOMツリーへのアクセスや操作

辺りですよ。もちろんこれはjQueryを使っても同じですからね??????????

そういう訳で以下のようなコードは完全にクソってことになるのはお分かりいただけますよね??????

// 実装内容については完全に適当だが察しろ

document.addEventListener('scroll', function(ev) {
    $('.some-element').css({
        top: $(this).scrollTop() + 'px'
    });
});

document.addEventListener('touchmove', function(ev) {
    $('.some-element').css({
        left: (parseInt($('.some-element').attr('x-offset')) + ev.changedTouches[0].pageX) + 'px'    
    });    
});

window.addEventListener('devicemotion', function(ev) {
    $('.some-element').css({
        left: ev.gamma + 'px'
    });    
});

もう目に見えてゴミですよね???????????お分かりになります????????????????

iPhoneでは動くから????????????は??????????Androidは??????????????????????????

高頻度イベントのデータを使う場合は描画ロジックを分けろ

もう書くのもめんどくさいのでこないだ書いた奴をそのまま貼りますけどね?

例えばタッチしはじめてそのまま指の動きに要素をつけたいとかそういう実装で考えると

//// touchmoveごとにスタイルを変更したい(適当バージョン)
var fps = 30; // 30fpsに制限
var frameTime = 1000 / fps;
var isAnimated = false;
var lastTouchInfo = null;
var element = document.getElementById('some-element');

document.addEventListener('touchstart', function(ev) {
    // 最初のデータを入れてアニメーションを開始
    lastTouchInfo = ev.changedTouches[0];
    isAnimated = true;
    animation();
});

document.addEventListener('touchmove', function(ev) {
    // touchmoveイベントの中ではデータの更新だけする
    lastTouchInfo = ev.changedTouches[0];
});

document.addEventListener('touchend', function() {
    // アニメーションの終了
    isAnimated = false;
    lastTouchInfo = null;
});

document.addEventListener('touchcancel', function() {
    // アニメーションの終了
    isAnimated = false;
    lastTouchInfo = null;
});

function animation() {
    // isAnimatedフラグが立ってなかったら終了
    if (!isAnimated) {
        return; 
    }

    // 最後のタッチイベントからデータを取得
    var x = lastTouchInfo.pageX;
    var y = lastTouchInfo.pageY;

    // 何かのスタイルを変更したりする
    element.style.top = y + 'px';
    element.style.left = x + 'px';

    // 次のanimationを登録
    setTimeout(animation, frameTime);
}

こんな感じに イベントのデータを何らかの変数にキャッシュして、描画ロジックでそれを参照する という風に書ける訳です。大体絶対 60fps を出さなきゃいけないなんていうシビアな状況はさほどないわけですよ。

何故こんなことをするかというと、特にスマートフォン、特にAndroidでは簡単にパフォーマンスが悪くなるんですよ、上の上で書いた悪いようなことをしてると!!!!!!!!!!!!!発熱も!!!!!バッテリーも!!!!!!ユーザ体感を損ないますよね!!!!!!!!!

iPhoneで動きゃ良いって訳じゃねぇんだからな!!!!!!!!!!!!!!!!!!

見た目だけで出来たって言う奴は何をやっても駄目

マジで今年はこんなのばっかり相手にして自分の仕事がまったく出来なくなるので今日は本当に頭にきたので書きました。ナメた実装で心が汚れる前にQiitaで発散させていただきます。

クソお世話になりました!!!!!!!!!!本当にありがとうございました!!!!!!!!!!!