tips blog

iphone(iOS)でscrollTop()の値でスクロール向きを判定すると逆になる場合がある

2019年5月8日

■症状
iphone(iOS)でscrollTop()の値がおかしくなる
scrollTop()の値が元に戻る
scrollTop()で判定するスクロールの向きが逆になる
上にスクロールすると表示。下にスクロールすると消える機能を付けるとカクカクした動作する。

■原因$(window).on(‘scroll’,function(){
$(‘body’).scrollTop();
});
$(‘body’).animate({‘scrollTop’:0});

のようにanimateやsetIntervalで画面をスクロールさせる場合に、scrollイベントでscrollTopを取得すると1~3回に1回逆向きに移動した値を取得する場合がある。
厳密に言うと元の値に戻る場合があある。
例えば、スクロール中にPCブラウザでは、$(‘body’).scrollTop()が978 , 962 , 950 , 942となってたとしてもiphone(iOS)では978 , 962 , 978 , 942のような値が取得される場合がある。てか5、6回に1回はなる。

その原因はマジ不明。
上記でも書いたがsetIntervalで $(‘body’).scrollTop(y)などで動かしても同じ。

ブラウザ画面下部のメニューバーが原因??かもと思ったが、同じ値が出てくるというのは、もっと根本的にイベントの発火と値の取得の辺りのタイミングが問題なのかも知れない。

■解決方法(2案)

□1案目
// 過去3つのscrollTopと比較し同じ値がある場合ノーカウント(差分0)とする。
// iphone iOs用対策 scrollイベント時 scrollTopを取得すると過去3つの値に戻る場合がある。

jQuery(function($){
var $base;
var checkTop=[-1,-1,-1];
$.extend({'checkScrollDiff':function(){
if(typeof $base==='undefined')$base=$.getBase();
var top=$base.scrollTop();
for(var i=0;i<3;i++)
if(checkTop[i]==top)
return 0;
var diff=checkTop[2]-top;
for(var i=0;i<2;i++)
checkTop[i]=checkTop[i+1];
checkTop[2]=top;
return diff;
}});
});
jQuery(function($){
var $base;
var checkTop=[-1,-1,-1];
$.extend({‘checkScrollDiff’:function(){
if(typeof $base===’undefined’)$base=$.getBase();
var top=$base.scrollTop();
for(var i=0;i<3;i++)
if(checkTop[i]==top)
return 0;
var diff=checkTop[2]-top;
for(var i=0;i<2;i++)
checkTop[i]=checkTop[i+1];
checkTop[2]=top;
return diff;
}});
});
□2案目
// 過去3つのscrollTopの各々の差の平均値を取り、スクロール向きを判定させる
// iphoneだけじゃなく汎用性があるように
jQuery(function($){
var $base;
var checkTop=[];
var id;
var MAX=4;
function _reset(){
for(var i=0;i<MAX;i++)
checkTop[i]=-1;
}
_reset();
$.extend({‘checkScrollDirection’:function(){
if(typeof $base===’undefined’)$base=$.getBase();
var top=$base.scrollTop();
var count=0;
var d=0;
for(var i=0;i<MAX;i++){
if(checkTop[i]>=0){
count++;
d+=checkTop[i]-checkTop[i+1];
}
}
if(checkTop[MAX]>=0){
count++;
d+=checkTop[MAX]-top;
}
for(var i=0;i<MAX;i++)
checkTop[i]=checkTop[i+1];
checkTop[MAX]=top;
// 0.5秒立った場合リセット
clearTimeout(id);
id=setTimeout(function(){
_reset();
},500);
if(count<=0)d=0;else d=d/count;
if(d==0)return 0;
else if(d<0)return -1;
else if(d>0)return 1;
}});
});
// getBase 上記共通で使用する関数
jQuery(function($){
var $base;
$.extend({‘getBase’:function(){
if(typeof $base===’undefined’){
if($(‘html’).scrollTop()>0){
$base=$(‘html’);
}else if($(‘body’).scrollTop()>0){
$base=$(‘body’);
}
}
return $base;
}});
});

前者は1~3回に同じ値になるという特性を利用して、同じ値になった場合、差を0にしてノーカウントにする。
デメリットとしては、向き判定の連続性が無くなり、ときどき0の値が出てくる。

後者は0.5秒以内の1~4回差の平均をとり、今の状態の向きを判定する。
デメリットとしては、0.5秒以内に止まった場合判定が若干遅れる。
※今回の症状で同じ値になったのか、止まったから同じ値になったのか判定できないので
(でもscrollイベントが実行されてて同じ値になる事は無いのかもしれない)

他の方法がある場合。、原因が違うや、
もっと簡単に向き判定できる!ってあれば教えてください!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です