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

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

ただいまの
回答率

90.76%

  • JavaScript

    15329questions

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

  • jQuery

    6336questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

  • HTML5

    3734questions

    HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

  • CSS3

    1920questions

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

javascriptでsetIntervalを使うたびに移動速度が上がってしまう

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 465

owl

score 33

前提

前回の質問でdivのboxをjavascriptのみでアニメーション移動させることについて質問しましたが、あれから試行錯誤を重ねて成功しました。

現状としましてはクリックした箇所にboxがイージングアニメーションをしながら移動します。

発生している問題

一つだけ問題なのが、最初にクリックした時のboxの移動速度と10回ほどクリックした時の移動速度が変わってしまうことです。
クリックしてboxを移動させる度に移動速度が速くなってしまいます。
setIntervalの使い方が間違っているのか、それとも原因が他にあるのか手詰まり状態です。

ご回答どうぞよろしくお願いします。
該当のソースコードをjsfiddleにアップしていますので、ご活用ください。
https://jsfiddle.net/ululami/c29aLprv/

該当のソースコード

var box = document.getElementById('box');
var clientRect;
//boxのx軸とy軸
var boxX;
var boxY;
//クリックした箇所のx軸とy軸
var clickX = 0;
var clickY = 0;

document.addEventListener('click', function(event){
  clientRect = box.getBoundingClientRect();

  boxX = clientRect.left;
  boxY = clientRect.top;

  clickX = event.clientX;
  clickY = event.clientY;

  setInterval(function(){
    box.style.left = boxX + 'px';
    box.style.top = boxY + 'px';

    if(boxX < clickX || boxY < clickY){
      boxX = boxX + (clickX - boxX) / 50;
      boxY = boxY + (clickY - boxY) / 50;
    } else if(boxX  > clickX || boxY > clickY){
      boxX = boxX - (boxX - clickX) / 50;
      boxY = boxY - (boxY - clickY) / 50; 
    }
  }, 30)
});
<div id="box"></div>
*{
  margin: 0;
  padding: 0;
}

#box{
  width: 50px;
  height: 50px;
  background: red;
  position: relative;
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+5

どんどん速くなるのはedo_m18さんのおっしゃるとおり、setIntervalを何回も実行しているからですね。止めるには、setIntervalが返すinterval IDを変数に保存しておき、止めたい時にclearIntervalにそのIDを渡します。

var intervalID = setInterval( ... );

// stop the interval
clearInterval(intervalID);


以下は気になった点です。

次のboxの座標を次のように計算していますが、

    if(boxX < clickX || boxY < clickY){
      boxX = boxX + (clickX - boxX) / 50;
      boxY = boxY + (clickY - boxY) / 50;
    } else if(boxX  > clickX || boxY > clickY){
      boxX = boxX - (boxX - clickX) / 50;
      boxY = boxY - (boxY - clickY) / 50; 
    }

どちらの場合も同じ結果になるので、場合分けする必要はないと思います。

    boxX = boxX + (clickX - boxX) / 50;
    boxY = boxY + (clickY - boxY) / 50;

質問の趣旨から外れてしまいますが、setIntervalではなくwindow.requestAnimationFrame()を使うと滑らかなアニメーションができます。

DEMO

var box = document.getElementById('box');
var clientRect = box.getBoundingClientRect();
var boxX = clientRect.left;
var boxY = clientRect.top;

var clickX = 0;
var clickY = 0;

document.addEventListener('click', function(event){
  clickX = event.clientX;
  clickY = event.clientY;
});

window.requestAnimationFrame(step);

var lastTime;

function step(timestamp) {
  if (!lastTime) lastTime = timestamp;
  var deltaTime = timestamp - lastTime;    // 前フレームからの経過時間
  move(deltaTime);
  window.requestAnimationFrame(step);
  lastTime = timestamp;
}

function move(delta) {
  boxX += (clickX - boxX) / 200 * delta;
  boxY += (clickY - boxY) / 200 * delta;
  box.style.left = boxX + 'px';
  box.style.top = boxY + 'px';
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/22 16:13

    返信遅れて申し訳ありません。
    requestAnimationFrameを使うことを考えていなかったので、
    これを使用したらスムーズにアニメーションし、大変満足しています。
    本当にありがとうございます。

    キャンセル

+4

多分、クリックするたびにsetIntervalで呼ばれる関数が重複して呼ばれるからじゃないかと思います。

クリックごとに前のsetIntervalを止めるなどして処理が重複しないようにしたら速度が変化することがなくなると思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/22 16:12

    返信遅れて申し訳ありません。
    まさにsetIntervalの部分クリックするたびに重複していることが原因でした。
    大変勉強になりました。
    本当にありがとうございます。

    キャンセル

checkベストアンサー

+2

setTimeoutを使うという手もあるでしょう。目的地にたどり着いていなければ再帰的に呼び出すという方法です。

が、たどり着いているかを判断する上で、もっと重大な問題が存在しています。

結論としては今回のプログラムとは全く関係無いのですが、わかりやすい例として、
アキレスと亀のパラドックスをご存知でしょうか?

詳しくはここでは述べませんが、アキレスがいくら速くても、微小時間で見ていくとアキレスは移動する亀には追いつけないというものです。

実は質問者様のプログラムは似たような状況になっているんです。
「残りの距離の1/2だけ進む」というのは良いアイデアだったと思いますが、
これは目的地には極限を取ると着きますが、実際には限りなく近づくだけでたどり着けないんです。

これはconsole.log("boxX : "+boxX+" clickX : "+clickX);のようなコードで確認していただけると思います。

ですから人の目では到着しているように見えても簡単な記述では止めることができません。setTimeoutに変えるだけでも確かに処理は重ならなくなるはずですが、(ほぼ)到着しても処理は止まりません。これは意図した動作では無いはずです。

ですからMath.round()等の関数を使用して区切りのいい所で止まるようにしましょう。

以下コードです。1つめは大体の位置が同じなら何もせず、違っていれば再帰処理させるというものです。(1つめの方はkaramarimo様の回答を参考に場合分けは消しました。)

var box = document.getElementById('box');
var clientRect;
//boxのx軸とy軸
var boxX;
var boxY;
//クリックした箇所のx軸とy軸
var clickX = 0;
var clickY = 0;
// 終了判定用
var preX = 0;
var preY = 0;

document.addEventListener('click', function(event){
  clientRect = box.getBoundingClientRect();

  boxX = clientRect.left;
  boxY = clientRect.top;

  clickX = event.clientX;
  clickY = event.clientY;

  var move = function(){
    box.style.left = boxX + 'px';
    box.style.top = boxY + 'px';

    preX = Math.round(boxX*100);
    preY = Math.round(boxY*100);

    boxX += (clickX - boxX) / 50;
    boxY += (clickY - boxY) / 50;

    // 小数第3位を四捨五入して小数第2位で比較
    if(Math.round(boxX*100)!=preX || Math.round(boxY*100)!=preY) setTimeout(move,30);
    //console.log("boxX : "+boxX+" clickX : "+clickX);
    //console.log("boxY : "+boxY+" clickY : "+clickY);
  };
  setTimeout(move,30);
});


jsfiddle

または比較部分で丸めるのもありです。

// 省略
      if(Math.round(boxX*10) < clickX*10 || Math.round(boxY*10) < clickY*10){
        boxX = boxX + (clickX - boxX) / 50;
        boxY = boxY + (clickY - boxY) / 50;
        setTimeout(move,30);
      } else if(Math.round(boxX*10) > clickX*10 || Math.round(boxY*10) > clickY*10){
        boxX = boxX - (boxX - clickX) / 50;
        boxY = boxY - (boxY - clickY) / 50;
        setTimeout(move,30);
      }
      //console.log("boxX : "+boxX+" clickX : "+clickX);
      //console.log("boxY : "+boxY+" clickY : "+clickY);
    };
    setTimeout(move,30);
// 省略


(長文失礼しました。m(_ _)m)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/22 16:11

    返信遅れて申し訳ありません。
    とても詳しく回答していただき大変勉強になりました。
    特に到達地点に限りなく近づくかゴールに到達できない部分が大変納得しました。
    本当にありがとうございます。

    キャンセル

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

  • ただいまの回答率 90.76%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    15329questions

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

  • jQuery

    6336questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

  • HTML5

    3734questions

    HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

  • CSS3

    1920questions

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