質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.30%
CSS3

CSS(Cascading Style Sheet)の第3版です。CSS3と略されることが多いです。色やデザインを柔軟に変更することが可能になります。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

3回答

1388閲覧

JS:addEventListenerのscroll処理でガタつく現象の原因と対策についてアドバイスいただきたいです

Ashi

総合スコア139

CSS3

CSS(Cascading Style Sheet)の第3版です。CSS3と略されることが多いです。色やデザインを柔軟に変更することが可能になります。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2023/08/17 02:59

編集2023/08/17 03:00

目的と課題

スクロール値の増減に併せて、特定の要素のスタイルの値も増減するアニメーションをスムーズに動作させたいです。
しかし、スクロール時にガタつきが発生してしまうのでその原因と対策を模索しています。
イメージ説明

お手数をかけますがご助言いただけますと幸いです。

具体的な問題

スクロールの増減によって画像の要素の「padding-top」を同様に増減させ、画像がその場に停滞して見えるような処理を実装しているのですが、下記URLの動画のように、主にスクロールの開始直前くらいのタイミングで要素がガタガタと揺れてしまいます。
https://vimeo.com/855227603/1b0406ea35?share=copy

スクロールイベントの多発によるブラウザへの負荷でこのような現象が起きているのではないかと予想してはいるのですが、そもそもその原因で合っているのか、合っていたとしてもどのように改善すればいいかがわからない状態です。

現在のコード(簡易版)

動画のサイトのコードをより簡略化したものが下記のコードです。HTMLやCSSが異なりますが、JSのコード自体は動画のものと全く同じです。

こちらの方が内容が軽いせいか、動画ほど頻繁にガタつきが発生しませんが、こちらのコードでもスクロール開始のタイミングでガタつきが発生してしまいます。

html

1<div id="box" class="box"> 2 <div class="box__wrapper"></div> 3</div> 4<div class="subBox"></div>

css

1.box { 2 width: 100%; 3 background: #555; 4} 5.box__wrapper { 6 width: 100%; 7 height: 600px; 8 background: #000; 9} 10.subBox { 11 width: 100%; 12 height: 3000px; 13 background: #eee; 14}

JavaScript

1const box = document.getElementById("box"); 2 3let ScrollCount = 0; 4function scrollCount() { 5 let ScrollCount_before = ScrollCount; 6 ScrollCount = window.scrollY; 7 let ScrollCount_diff = ScrollCount - ScrollCount_before; 8 9 requestAnimationFrame(function () { 10 if (ScrollCount_diff > 0) { 11 for (let i = 1; ScrollCount_diff >= i; i++) { 12 let countUp = ScrollCount_before + i; 13 console.log(countUp); 14 box.style.paddingTop = countUp + "px"; 15 } 16 } else { 17 for (let i = 1; ScrollCount_diff * -1 >= i; i++) { 18 let countDown = ScrollCount_before - i; 19 console.log(countDown); 20 box.style.paddingTop = countDown + "px"; 21 } 22 } 23 }); 24} 25window.addEventListener("scroll", scrollCount); 26

補足

JSのスクロールカウント処理についてですが、スクロールのスピードに関わらず1pxずつの増減を実現するために上記のようなコードにしています。

CSS側のスタイルに関しては「paddingではなくfixedにした方が良い」など、アプローチの方法はいくつかあるかと思いますが、今回についてはpaddingの値の増減で制御するという前提は変えない(変えられない)ということでご検討いただきたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2023/08/17 03:55

> 今回についてはpaddingの値の増減で制御するという前提は変えない(変えられない)ということで なぜ変え(られ)ないのですか?
Ashi

2023/08/17 04:04

ご質問ありがとうございます。 ①今回の質問の本質的な目的として、この様な現象の原因が何であるかを把握したいという意向があること ②実際のコードではその他の様々な要素や条件が絡んでおり、安易に仕様自体を変更できないこと が理由です。 ①については、別のアプローチを実行することでガタつき自体は回避することができるかもしれませんが、それだと結局何が問題だったのかがわからないまま終わってしまうと考えたからです。 ②についても、絶対に変えられないわけではないのですが、原因がわかり解決できるのであれば時間と工数をかけて仕様変更を施すリスクを負うのは避けたほうが良いと考えたからです。
think49

2023/08/17 10:11

> スクロールの増減によって画像の要素の「padding-top」を同様に増減させ、画像がその場に停滞して見えるような処理を実装しているのですが、 position: sticky を使ってみてはいかがでしょうか。 https://developer.mozilla.org/ja/docs/Web/CSS/position
guest

回答3

0

 既にご認識かと思いますが、スクロールイベントは1pxごとに呼ばれるわけではないのでどうしても飛び飛びの処理になってしまうため、ズレが生じることを回避できず、がたつきが起きます。

 なので、このズレを何とかしないとがたつきを消せないだろうと思います。

ネイティブなスクロールを諦める

 ネイティブなスクロールとイベントで呼ばれる値にズレが起きるのですから、ネイティブなスクロールを諦めればずれは起きにくくなるはずです。

 以下のコードは、ラッパー要素を用意して全画面に広げて固定配置し、transformで疑似スクロールしてます。
(とはいえ、ネイティブなスクロールと同じ位置です)

html

1<!DOCTYPE html> 2<html lang="en"> 3 4<head> 5 <meta charset="UTF-8"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Document</title> 8 <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/destyle.css@3.0.0/destyle.min.css"> 9</head> 10 11<body> 12 <div id="wrapper"> 13 <div id="box" class="box"> 14 <div class="box__wrapper"></div> 15 </div> 16 <div class="subBox"></div> 17 </div> 18 <style> 19 html { 20 height: 9999px; 21 } 22 23 .box { 24 width: 100%; 25 background: #555; 26 } 27 28 .box__wrapper { 29 width: 100%; 30 height: 600px; 31 background: #000; 32 } 33 34 .subBox { 35 width: 100%; 36 height: 3000px; 37 background: #eee; 38 } 39 40 #wrapper { 41 position: fixed; 42 width: 100%; 43 height: 100%; 44 } 45 </style> 46 <script> 47 const box = document.getElementById("box"); 48 49 function scrollCount(e) { 50 document.getElementById('wrapper').style.transform = `translateY(-${scrollY}px)`; 51 box.style.paddingTop = `${scrollY}px`; 52 } 53 window.addEventListener("scroll", scrollCount); 54 55 </script> 56</body> 57 58</html>

 なお、for文で1pxずつ変更する処理は、リフローが起きないはずなので意味がないと思いましたので、削除しました。

 言うまでもないですが、こんなことをするなら#boxにfixedを当てておいた方が100倍早いのですが、今回は学習のためとのことでしたのであえて書いてみました。

投稿2023/08/18 01:22

Lhankor_Mhy

総合スコア37445

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

別のアプローチを実行することでガタつき自体は回避することができるかもしれませんが、それだと結局何が問題だったのかがわからないまま終わってしまうと考えたからです。

ブラウザ基準での描画位置を一定にするのに、「都度位置を書き換え続ける」という方が手間で非効率な手段だと考えます。

自分からすれば、「座標をJavaScriptで変化させ続ける」という選択そのものが問題だと考えました。

投稿2023/08/17 04:17

maisumakun

総合スコア146605

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2023/08/17 04:25

ふと気になったことですが、scrollYの取得をrequestAnimationFrameのコールバック内に持っていくとどうなりますでしょうか。
Ashi

2023/08/17 04:45

ありがとうございます。ご意見とても参考になります。 少し視点や思考を切り替えて考え直してみます。
Ashi

2023/08/17 05:03

>ふと気になったことですが、scrollYの取得をrequestAnimationFrameのコールバック内に持っていくとどうなりますでしょうか。 試してみましたが、挙動(ガタつき)は得意変化ありませんでした…悲
guest

0

描画フレームが微妙にずれるからだめなものはだめでしょう
(ブラウザのレンダリングエンジンによるので最適化されているものなら気になる違いはないです)

css

1<style> 2#wrap{ 3position:absolute; 4padding-top:50px; 5top:0; 6} 7#box1{ 8position:absolute; 9height:100px; 10width:100px; 11background-Color:yellow; 12} 13#box2{ 14top:50px; 15left:150px; 16position:absolute; 17height:100px; 18width:100px; 19background-Color:lime; 20left: 21} 22#box3{ 23top:50px; 24left:300px; 25position:fixed; 26height:100px; 27width:100px; 28background-Color:aqua; 29left: 30} 31.dummy{ 32height:80%; 33} 34.dummy:nth-child(even){ 35background-Color:lightgray; 36} 37</style> 38<script> 39window.addEventListener("scroll",()=>{ 40 const y=window.scrollY; 41 wrap.style.paddingTop=(y+50)+"px"; 42 box2.style.top=(y+50)+"px"; 43}); 44</script> 45<div id="wrap"> 46<div id="box1">box1</div> 47</div> 48<div id="box2">box2</div> 49<div id="box3">box3</div> 50<div class="dummy">dummy</div> 51<div class="dummy">dummy</div> 52<div class="dummy">dummy</div> 53<div class="dummy">dummy</div> 54<div class="dummy">dummy</div>

選択肢にはいっていないとのことですが素直にfixedする(box3)のが賢明です

投稿2023/08/17 03:27

編集2023/08/17 03:29
yambejp

総合スコア117755

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Ashi

2023/08/17 04:06

ご回答ありがとうございます。 >描画フレームが微妙にずれるからだめなものはだめでしょう 知識不足で大変恐縮ですが、差し支えなければ上記についてもう少し具体的にご教示いただけますと幸いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問