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

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

ただいまの
回答率

88.91%

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

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 3,812

kkrg24

score 7

前提・実現したいこと

Canvasを使った波のアニメーションを実装したいと考えています。
実装することはできたのですが、これを同じページに複数設置する場合、
どう修正したら良いのかご教示いただきたいです。
(類似の質問は見つけたのですが、私には難しく…)

具体的には下記の項目を実現したいと思っています。

  • 同一アニメショーンのCanvasを同一ページに複数設置したい。
  • 色をCanvas毎に変更したい。

該当のソースコード

下記のサイトを参考にしています。
http://sterfield.co.jp/designer/canvasで波のアニメーションを描画する/
→デモ
http://sterfield.co.jp/demo/ninomiya/104/index2.html

<canvas id="sineCanvas" width="800" height="300"></canvas>
(function () {

var unit = 100,
    canvas, context, canvas2, context2,
    height, width, xAxis, yAxis,
    draw;

/**
 * Init function.
 * 
 * Initialize variables and begin the animation.
 */
function init() {

    canvas = document.getElementById("sineCanvas");

    canvas.width = document.documentElement.clientWidth; //Canvasのwidthをウィンドウの幅に合わせる
    canvas.height = 300;

    context = canvas.getContext("2d");

    height = canvas.height;
    width = canvas.width;

    xAxis = Math.floor(height/2);
    yAxis = 0;

    draw();
}

/**
 * Draw animation function.
 * 
 * This function draws one frame of the animation, waits 20ms, and then calls
 * itself again.
 */
function draw() {

    // キャンバスの描画をクリア
    context.clearRect(0, 0, width, height);

    //波を描画
    drawWave('#10c2cd', 0.3, 3, 0);
    drawWave('#43c0e4', 0.4, 2, 250);
    drawWave('#1d82b6', 0.2, 1.6, 100);

    // 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) {
    context.fillStyle = color;
    context.globalAlpha = alpha;

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

/**
 * Function to draw sine
 * 
 * The sine curve is drawn in 10px segments starting at the origin. 
 * drawSine(時間, 波の幅のzoom, 波の開始位置の遅れ)
 */
function drawSine(t, zoom, delay) {

    // Set the initial x and y, starting at 0,0 and translating to the origin on
    // the canvas.
    var x = t; //時間を横の位置とする
    var y = Math.sin(x)/zoom;
    context.moveTo(yAxis, unit*y+xAxis); //スタート位置にパスを置く

    // Loop to draw segments (横幅の分、波を描画)
    for (i = yAxis; i <= width + 10; i += 10) {
        x = t+(-yAxis+i)/unit/zoom;
        y = Math.sin(x - delay)/3;
        context.lineTo(i, unit*y+xAxis);
    }
}

init();

})();

試したこと

値を変更・削除したり、類似のものを流用してみたりしましたが、
どうも関数に弱く、丸一日作業が止まってしまいました。

丸投げに近い形で大変心苦しいのですが、
どうぞご教示のほどよろしくお願い致します。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • masaya_ohashi

    2017/04/27 09:48

    同ページに複数、というのはcanvasがたくさん並んでいる状態ですか?それとも1つのcanvasの中に沢山の波がある状態ですか?

    キャンセル

  • kkrg24

    2017/04/27 10:08 編集

    レスありがとうございます!!「canvasがたくさん並んでいる状態」となります。同じような質問で「https://teratail.com/questions/37404」があり、こちらにほぼ近い形が理想としているものです。ただ、こちらの方法では、3つ並べることには成功したものの、色を個別に指定という条件をクリアできず、また動きも理想とするものとは違ったため(動きは参考の波アニメーションと同一のものです)今回、質問させていただきました。

    キャンセル

  • kei344

    2017/04/27 10:43

    この「質問への追記・修正の依頼」の部分はデフォルトで表示されませんので、「類似の質問」に関して質問本文に追記することをお勧めします。

    キャンセル

回答 3

checkベストアンサー

+4

動作サンプル
https://jsfiddle.net/39we73t1/

まず複数のcanvasがあるので、グローバル変数で全部賄うことはかなり複雑になります。なので、基準となるCanvasを各関数に渡していくことで、「このCanvasに対して処理をする」ことを明確にします。各functionに引数の先頭にcanvasが追加されているのがわかりますね?各関数の中でも、いままでグローバルで1個しかなかったwidth等がcanvasから取られています。これで「このcanvasに対してdrawWave、drawSineの処理をするぞ」というのが分かるようになります。contextも毎回canvasに持たせたcontextCacheを使うことで、このcanvasのcontextであることが分かります。

次に、1回の更新で複数のCanvasを描画するため、「更新処理」と「描画処理」を分離します。updateとdrawという関数に処理を分離しました。時間の計算とかは複数枚ごとにやっちゃうとおかしいですよね?なので1回の処理で1回しかしないでよい処理は更新処理に、各canvasごとにやらなきゃいけない処理はdraw以下に書きます。

canvasごとに色が変えたいのであれば、その色情報を持っていないといけません。colorListにその情報が入っています。描画する際に、canvasごとの色情報を渡してやることで色を変えて処理をすることができるようになっています。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/04/27 10:49

    jsfiddleってわかりやすいですね!

    キャンセル

  • 2017/04/27 10:53

    軽い動作テストとかにも重宝してます。これでタダなんですから世の中よくなったものです。

    キャンセル

  • 2017/04/27 15:13

    動作サンプルまで作成いただき、とてもわかりやすく丁寧にまとめていただきました。これをきっかけに、もっとcanvasへの理解を深めていきたいと思います。この度はご回答いただきありがとうございました。

    キャンセル

+3

どうも関数に弱く

というよりはforループによる繰り返しや配列の利用のコードの実装イメージを持てないということかと思います。あるいはこのサンプルが何をしているかコードから読み取れていないといったところではないでしょうか。その状態ですと変更コードそのものの回答しか質問者さんにはうれしくないということになるかも知れませんが・・・

例えばdrawなら次のようになるでしょう。多少コメントは付けておきました。
まずはできる限り「コードの中で何をしているか」を読み取ってみてください。
for文や配列、オブジェクトリテラルの書き方も(もし曖昧なら)調べて理解するようにしましょう。

//それぞれのキャンバスに関する必要な情報(idや色)を配列に入れておく
var waveParameters = [
  { id: 'sineCanvas1',    // キャンバスのid
    waveColor1: '#10c2cd', // 波の色-1
    waveColor2: '#43c0e4', // 波の色-2
    waveColor3: '#1d82b6'  // 波の色-3
  },
  { id: 'sineCanvas2',    // キャンバスのid
    ...
  }];

function draw() {
  // 複数のキャンバスに対してパラメータを変えながら描画
  for (var wp in waveParameters) {
    // キャンバスと描画コンテキストを求める
    var canvas = document.getElementById(wp.id);
    var context = canvas.getContext("2d");
    // 波を描画
    drawWave(wp.waveColor1, 0.3, 3, 0);
    drawWave(wp.waveColor2, 0.4, 2, 250);
    drawWave(wp.waveColor3, 0.2, 1.6, 100);
  }
  // 次回の描画のために波の位置を変化させる
  // 35msecごとに0.014/180度だけ角度を変化させる
  draw.seconds = draw.seconds + .014;
  draw.t = draw.seconds*Math.PI;
  // 35msec後に再度この関数を起動
  setTimeout(draw, 35);
}

  

<canvas id="sineCanvas1"> ...</canvas>
<canvas id="sineCanvas2"> ...</canvas>
...

なお、実装にはバリエーションがありますので、人それぞれで違ってくると思います。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/04/27 14:59

    ご回答くださりありがとうございました。まさにコメントしていただいた通りで、お恥ずかしい限りです。また、ただコピペして記載してしまったのも、回答してくださった方に失礼でした。for文や配列など、まずは意味や役割などをしっかりと理解し、ご回答いただいた方法でも試してみたいと思います。

    キャンセル

  • 2017/04/27 15:01

    何を作るせよ、基本的なところがわかってくると世界が広がるのでそれだけコードも書きやすく、おもしろくなってきますよ!頑張ってください。

    キャンセル

+1

とりあえず変数を配列にして

var waves = [{
    id: 'sineCanvas1',
    cols: ['#10c2cd', '#43c0e4', '#1d82b6'],
    canvas: null,
    context: null,
    unit: 100,
    height: 300,
    width: document.documentElement.clientWidth,
    xAxis: 0,
    yAxis: 0,
    draw: {
        seconds: 0,
        t: 0
    }
},//...以下同様

for文で回すって感じですかね…

function init() {
    for (var i = 0; i < waves.length; i++) {
        var wave = waves[i];
        wave.canvas = document.getElementById(wave.id);
        wave.context = wave.canvas.getContext("2d");
        wave.xAxis = Math.floor(wave.height / 2);
        wave.yAxis = 0;
        switch(i){
            case 0: draw0();break;
            case 1: draw1();break;
        }
    }
}

すみません間違って途中で投稿してしまいました。
再帰は使い回しができない?ので、表示するcanvas要素の数だけdraw()を書いてます。
カッコ悪いですが、動かすだけなら動きます。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/04/27 15:08

    ゴールに辿り着くまでに様々な考え方があるのだとわかりました。「カッコ悪い」とありますが、1番シンプルな考え方なのかなと思います。まだ理解力が乏しいため、お礼のみとなりますが、改めましてご回答いただきありがとうございました。

    キャンセル

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

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

関連した質問

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