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

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

ただいまの
回答率

89.21%

Canvasで同じアニメーションを複数箇所に設置する方法

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 2,256

mx3

score 185

Canvasを使って同じアニメーションを同一ページ内で複数箇所に設置したくて色々試しています。
設置したいのは波のような動きをするアニメーションです。
配列を使えばできるんじゃないかというところまで行き着きましたが、どうしてもエラーが出て解決できません。

下記のエントリを参考にして、複数設置できるように修正をしようとしているところです。
Canvasで波のアニメーションを描画する

▼現在のコード

<div class="canvas-container">
    <canvas class="sineCanvas" id="sineCanvas01" width="2000" height="200"></canvas>
</div>
<div class="canvas-container">
    <canvas class="sineCanvas" id="sineCanvas02" width="2000" height="200"></canvas>
</div>
<div class="canvas-container">
    <canvas class="sineCanvas" id="sineCanvas03" width="2000" height="200"></canvas>
</div>
(function () {

var unit = 20,
    canvas, canvas2, context2,
    xAxis,
    yAxis,
    draw,
    classCount = $(".sineCanvas").length;
var context = [];
var width = [];
var height = [];
/**
 * Init function.
 * 
 * Initialize variables and begin the animation.
 */
function init() {
    canvas = document.getElementsByClassName("sineCanvas");
    for (i = 0; i < classCount; i++) {
          context[i] = canvas[i].getContext('2d');
          canvas[i].width = $('.sineCanvas').width();
          canvas[i].height = $('.sineCanvas').height();
          width[i] = canvas[i].width;
          height[i] = canvas[i].height;
    }
    xAxis = Math.floor(height[0]/2);
    yAxis = 0;
    draw();
}

/**
 * Draw animation function.
 * 
 * This function draws one frame of the animation, waits 20ms, and then calls
 * itself again.
 */
function draw() {
  for (i = 0; i < classCount; i++) {
    // キャンバスの描画をクリア
    context[i].clearRect(0, 0, width[i], height[i]);

    //波を描画
    drawWave('#EFDBAD', 0.3, 3, 0);
    drawWave('#EFDBAD', 0.3, 3, 0);
    drawWave('#EFDBAD', 0.3, 3, 0);

    // Update the time and draw again
    draw.seconds = draw.seconds + .014;
    draw.t = draw.seconds*Math.PI;
    setTimeout(draw, 35);
  }
};
draw.seconds = 0;
draw.t = 0;

/**
* 波を描画
* drawWave(色, 不透明度, 波の幅のzoom, 波の開始位置の遅れ)
*/
function drawWave(color, alpha, zoom, delay) {
  for (i = 0; i < classCount; i++) {
    context[i].fillStyle = color;
    context[i].globalAlpha = alpha;

    context[i].beginPath(); //パスの開始
    drawSine(draw.t / 0.5, zoom, delay);
    context[i].lineTo(width[i] + 10, height[i]); //パスをCanvasの右下へ
    context[i].lineTo(0, height[i]); //パスをCanvasの左下へ
    context[i].closePath() //パスを閉じる
    context[i].fill(); //塗りつぶす
  }
}


/**
 * Function to draw sine
 * 
 * The sine curve is drawn in 10px segments starting at the origin. 
 * drawSine(時間, 波の幅のzoom, 波の開始位置の遅れ)
 */
function drawSine(t, zoom, delay) {
    var x = t;
    var y = Math.sin(x)/zoom;
    for (i = 0; i < classCount; i++) {
      context[i].moveTo(yAxis, unit*y+xAxis[i]);
      for (k = yAxis; k <= width[i] + 10; k += 10) {
          x = t+(-yAxis+k)/unit/zoom;
          y = Math.sin(x - delay)/3;
          context[i].lineTo(k, unit*y+xAxis[i]);
      }
    }
}

init();

})();

▼現時点で出ているエラーはこちらです(Firebugで確認)

TypeError: context[i] is undefined
    context[i].lineTo(width[i] + 10, height[i]); //パスをCanvasの右下へ

初心者なので処理を順を追ってalertなどで表示しながら間違っているところを午前中からずっと探しているのですが、もう完全にお手上げ状態です...。
どこが間違っているか、そもそもこの考え方で実現可能なのか教えていただけないでしょうか??

もしくは配列を使わずにやる方法があればぜひ教えてください。
どうかよろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

0

実際コードをコピーして動かしてみました。

for文で使っているiという変数がグローバルスコープになっているため、
drawWaveから呼び出したdrawSineでiが上書きされ、訳の分からない挙動になっています。
forでカウンタを使うなら、必ずその時にローカルスコープで変数宣言しましょう。

//  ↓ この var が大事
for(var i=0;i<classCount;i++){
}

追記

以下の箇所に問題があります。

xAxis[i]


xAxisは配列ではない、ただのfloat値ですが、配列アクセスをしようとしています。
私の環境では、[i]を消せばクリーム色の波が3つ表示されました。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/06/09 21:10

    お忙しいなか再度回答ありがとうございます。
    質問にも書きましたとおり、どこで処理がおかしくなっているのか、変数の値がおかしくなっているところがないか、数行ずつ分けてalert()などでチェックした上で質問をしました。FirebugやGoogleChromeのデバッグコンソールも使いましたがまだ不慣れで...。
    そうやって最終的に、
    for (i = 0; i < classCount; i++) {
    context[i].moveTo(yAxis, unit*y+xAxis[i]);
    の二行を消すとエラーがでなくなることがわかったので、仰るとおり「xAxis[i]」に問題があるのではないかなと「xAxis[i]」周りを数時間ずっといじっていたのですが、まったくの的外れかもしれないし、かえって混乱を招くかもしれないと思い、そういった課程を一切説明せずに質問してしまい申し訳ありませんでした。テラテイルに簡単に頼るのは良くないと、普段から私も思っていますので、質問に至るまで6時間かけて最低限のことはしたつもりですが、今後は軽々しく質問しないように気をつけます。
    対応していただきありがとうございました。

    キャンセル

  • 2016/06/09 22:43 編集

    おお、そこまで頑張っておられたのですね…推測で物を言って申し訳ありませんでした。とりあえず、補足部分の回答は削除します。
    teratailに頼ることは悪いことではありません。上から目線のアドバイスで申し訳ないですが、その「この行に問題があると思うんです」と添えてくだされば、私以外からも回答が多く得られたかと思います。漠然と質問するより、自分なりの解釈も、自信がなくても書いてくれれば回答者のヒントになりますよ!頑張ってください

    キャンセル

  • 2016/06/10 14:31

    またしばらくコードを修正して、私も波を3つ表示させることができるようになりました!

    教えて頂いたとおり、下記のようにvarを付けて、

    for(var i=0;i<classCount;i++){
    }

    xAxis[i]の[i]を削除したところ、動くようになりました!

    varをつけた時にブラウザがフリーズしてしまっていた件は、Firefoxを使っていたことが原因で(負荷が大きすぎて処理できてなかったみたいです)、Chromeを使ったところエラーもなく3つとも表示されました。ただ、波がかなり高速で動いていて、透明度も3つそれぞれ違っていたので、ループの対象がおかしくなって重複してしまってるのだろうなと思います。改善試みましたがいまの私のスキルでは難しそうなので、また別の方法も探してみようと思います。

    仰るとおり、今後質問する際にはきちんと、いまわかっていることも添えようと思います。
    ありがとうございました!

    キャンセル

0

initの以下の部分で取得できるcanvasが1枚もありません。
canvasにnameがついてないからです。
canvasの配列を取るならname属性をつけるか、
jQueryのセレクタでsineCanvasクラスで指定する必要があります。

canvas = document.getElementsByClassName("sineCanvas");

追記

もう一つ、javascriptを以下のコードで囲んでいますが、これだとDOM解決前にコードが実行されます。

(function(){
})();


こっちならwindow.onloadと同じ効果を持つのでこちらにしましょう。

$(function(){
});

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/06/09 21:17

    こちらもご指摘ありがとうございます!初歩的なところからもう一度見直します。

    キャンセル

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

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

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