to topto bottom

モバイルファースト時代のマルチデバイス対応を確実・簡単に

iOS6でjQueryのanimateメソッドが動作しなくなる現象が発生中

こんにちは。x-fitチーム・エバンジェリストの渡辺です。iOS6のSafariでjQueryのanimateメソッドの動きがおかしくなる現象が確認されました。Webで調べてみると、あちこちで報告されているようです。ちょっと確かめてみました。

 
 
確認されている現象は、「window操作(画面のスクロールやピンチ操作など)を行っている間にjQueryのanimateメソッドが一度でもコールされると、それ以降animateメソッドをコールしても動作しなくなる。」というものです。この現象はjQueryのサイトでも現象が報告されています
 

HTML例

 
以下のようなHTMLで確認してみます。
 
<!DOCTYPE html>
  <head>
    
    <meta charset="utf-8" />
    <title>Animate</title>
    
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
    <style>
      .editorial{ position:relative; width:100%; overflow:hidden; }
      .items{ position:relative; white-space:nowrap; }
      .item{ display:inline-block; white-space:normal; vertical-align:top; border-style : solid;}
      .item ~ .item{ margin:0 0 0 -4px;  }
    </style>
  </head>
  <body>
    <section class="container">
      <div class="content">
        
        <div class="editorial">
          <div class="items">
            
            <div class="item">
              <p>ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。ダミーテキストです。</p>
            </div>
            
            <div class="item">
              <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
            </div>
          </div>
        </div>
      </div>
      
    </section>
    <!-- JavaScript -->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
    <script>
      
      // Vars
      var start, last;
      // Start
      $('.editorial').get(0).addEventListener('touchstart',function(e){
        // Only for one finger actions
        if(e.touches.length != 1) return;
        start = { x:null, y:null };
        last = { x:null, y:null };
      },false);
      // Move
      $('.editorial').get(0).addEventListener('touchmove',function(e){
        // Only for one finger actions
        if(e.touches.length != 1) return;
        freestyle.apply($('.editorial').get(0),[e]);
      },false);
      // Move
      $('.editorial').get(0).addEventListener('touchend',function(e){
        console.log('Touch End');
        $('.editorial .items').stop(true,false).animate({ left:0 },250);
      },false);
      var freestyle = function(e){
        // Vars
        var  me = this,
          $me = $(me),
          ref = $me.find('.items').get(0),
          $ref = $(ref),
          touch = e.touches[0],
          ex = touch.pageX,
          ey = touch.pageY,
          dif = ex-last.x,
          dx = ex-start.x,
          dy = ey-start.y;
        if(start.x == null){
          start.x = ex;
          start.y = ey;
          return;
        };
        if(Math.abs(dx)/2 > Math.abs(dy)){
          e.preventDefault();
          e.stopPropagation();
        } else {
          return;
        };
        if(last.x != null) $ref.css({ left:(ref.offsetLeft+dif) });
        last.x = ex;
        last.y = ey;
      };
      
      window.scrollTo(0, 1);
    </script>
    
  </body>
</html>
 
このHTMLでは、テキストのブロックを横に並べて、左右のスワイプで動かせるようにし、指を離したときに、そのブロックを元の位置に戻すためにjQueryのanimateメソッドを使う、という例です。jQueryのバグトラッカーにコメントされている動作デモコードを一部改変しました。
 

確認結果

止まってしまったアニメーション
指を離しても元に戻らなくなった状態画面
 
animateメソッドを実行しているのは、指を離した瞬間から、ブロック位置が元に戻るところです。横にスワイプさせている間は、ブロックはもとの位置に戻りますが、少しでも縦にスワイプしてスクロールさせると、横にスワイプしても元に戻らなくなります。縦方向へのスクロールは極めて一般的な動作であるため、再現性が高く、アニメーションされないというのはWebサイトの機能的に問題になりやすいと思われます。
 

原因

 
jQueryのanimateメソッドは、内部で、JavascriptのsetIntervalなどのTimer系のメソッドを使用しています。この現象の原因にあるのは、このTimer系メソッドがiOS6できちんと動いていないためのようです。
JavascriptのsetIntervalメソッドは、コールバックメソッドが登録されると、ハンドラのIDを返します。このハンドラは正しく返されるものの、コールバックメソッドが実行されないため、jQuery内部でアニメーション処理を続行できない状態になっています。
 

対策・回避策はあるのか?

 
対策としてあげられるのは、window.requestAnimationFrameか、CSS3のtransition・animationを使って実装する、というものです。ただしこれは実装方法の変更になり、サポートされるブラウザなどが限定される可能性がありますので、100%対応できるというものではありません。
10/25現在、jQueryのバグトラッカーでは、iOS6側の問題に見えるためか、jQuery側での対処が行われるかどうかははっきりしていません。Webサイト側で取ることのできるとりあえずの回避策については、setIntervalやsetTimeoutのようなTimer系のメソッドをjavascriptで再実装し、オーバーライドする、という荒技を使うことくらいしかないようです。
jQueryのanimateメソッドをWebサイトで利用される際は、ご注意ください。
 
※本記事は、弊社開発チーム所有機での確認結果であり、全機種調査の結果ではありません。同一機種であっても、この現象が必ず発生することを保証するものではありません。これ以外の現象や例外のある可能性がありますことをご了承ください。
 
 
タグ: 
このエントリーをはてなブックマークに追加