GameWith Developer Blog

GameWith のエンジニア、デザイナーが技術について日々発信していきます。

通知ポップアップの中身をいい感じにスクロールできるようにする

GameWith のエンジニアの @terunuma です。gamewith.jp のフロントエンドに関わる部分を主に触っています。

gamewith.jp ではゲームアプリの攻略/紹介記事を掲載している他に、会員向けのコンテンツとしてアプリのリリース通知や Q&A や他のゲームユーザーと交流できる SNS/コミュニティ機能なども提供しています。

これらの会員向けのコンテンツではアプリリリースの通知や Q&A に質問/回答がついた、SNS でいいねされた時にユーザーに対して通知が飛ぶようになっており、ログイン中であれば通知ボタンにバッジが表示されます。通知ボタンをタップで内容も表示できるようになっています。

f:id:syque:20171213194748p:plain:w300

この通知ポップアップの中にユーザーに届いた通知一覧を表示しているのですが、ポップアップの表示範囲が狭いためスクロールができるようにしています。ですがモバイルだとスクロールの動きがカクカクしており触り心地がよくないので、CSS で -webkit-overflow-scrolling: touch; のプロパティを設定してスクロールがスムーズになるようにしました。

f:id:syque:20171213194531p:plain:w300

これで完成、となればよかったのですが実際に触ってみると通知をスクロールした後に画面が固まってしまうことがありました。原因を探ってみると、どうも通知一覧の上端か下端でスクロールしようとした時に body の方をスクロールしようとしていて、目には見えないけどバウンススクロール状態になっていてそれが終わるまでタッチ処理を受け付けない状態になっていたことが分かりました。

これを解決するために div の端でスクロールしても body をスクロールしないようにできないか、と思い調べてみたところ stackoverflow に Javascript を用いて div のスクロール位置を調整することで解決する方法が紹介されていました。

stackoverflow.com

仕組みとしてはこうです:

  • 対象の、スクロール可能な要素に touchstart のイベントをバインドする
  • そのイベント処理で、現在の div のスクロール位置が上端か下端かをチェックする
  • 上端であれば +1、下端であれば -1、スクロール位置を動かす
  • この状態で端まで引っ張ると、div 内部の要素で必ずバウンススクロールするので body がスクロールされない

実装したスクリプトは以下になります:

scrollableElement.on('touchstart', function() {
    // 上端なら +1 する
    if (scrollableElement.scrollTop() === 0) {
        scrollableElement.scrollTop(1);
    }

    // 下端なら +1 する
    // 要素の高さとスクロール位置の差から、現在位置が下端かどうかを計算する
    var currentScrollTopPosition = scrollableElement.scrollTop();
    var maxScrollTopPosition = scrollableElement[0].scrollHeight - scrollableElement[0].offsetHeight;

    if (currentScrollTopPosition >= maxScrollTopPosition) {
        scrollableElement.scrollTop(currentScrollTopPosition - 1);
    }
});

f:id:syque:20171213203628p:plain:w300

要素が +1/-1 スクロールされる動きは、通知ポップアップを注意深く見ながらタッチすれば見ることができますので興味のある方はぜひ観察してみてください。 (そしてニヤッとしてください 😎 )

これにより、通知ポップアップをスクロールしても画面が固まるという問題を解決することができました。 実際のところ、こういうハックをしなくても自然な実装で作れる(ex. CSS のみで完結できる)のが一番良いのですが、ユーザー体験が良くなる手段になるのであれば今後もコード的に無理のない範囲で取り入れていければと思います。