0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【CANVAS】うにょうにょ動いて色が変わる円を作ってみた

Last updated at Posted at 2022-05-29

まえがき

ある案件で動く円を背景に実装する機会があったので、
canvasでうにょうにょ動く円を作ってみることにしました。
円を動かしたり描画するだけならcssだけで実装も出来たのですが、
動かすと同時に円の色を変更したかったので、
下記コードを参考にごにょごにょして実装してみました。

準備

まず、基本的なうにょうにょ動く円のコードはこちらより拝借。
https://blog.nijibox.jp/article/canvas_wobbly_circle/

触ったところは基本設定をお好みに調整したぐらいで、
円の描画に関する処理はそのまま使わせて頂きました。
 


// 基本設定
var MAX = 10;           //点の数
var RADIUS = 100;       //描画する円の半径
var FPS = 60;           //1秒あたり何回画面を描きなおすか
var CENTER = {x:200, y:200};     //描画位置
var canvas, ctx, timer, point;
var stats;
var mouseFlag = true;


// 円の描画基本設定
var Point = function(c, r, rota)
{
  this.x, this.y;
  this.centerX = c.x;
  this.centerY = c.y;
  this.radian = rota * (Math.PI / 180); 
  this.radius = r;
  
  this.speed = Math.random() * 10 + 5;   //うにょうにょ動くスピード
  this.r = Math.random() * 2 + 1;        //うにょうにょの柔らかさ
  this.rota = 0;

~ 省略

表示されたうにょうにょ動く円

拝借したコードを動かしてみると、うにょうにょ動くかわいい円が描画されました。(fillStyleで色を変更可能)

スクリーンショット 2022-05-29 19.54.03.png

色を変更する処理を追加

■仕様
指定の時間ごとに指定の3色にふわっとカラーチェンジする

色変更の基礎となるコード

// 1.RGBのリスト
const colors = [
    {
        r: 20,
        g: 20,
        b: 20
    },
    {
        r: 40,
        g: 40,
        b: 40
    },
    {
        r: 50,
        g: 50,
        b: 50
    },
],
// 2.FPS設定
FPS = 20,

// 3.色が変化するまでのフレーム数 - 2秒ごと
duration = 2000 / FPS;


// 4.duration内の秒数カウンター、現在の目標の色の配列キー
let
    transition = [
      {
        r:0,
        g:0,
        b:0,
      }
    ], 
    color = [
      {
        r:0,
        g:0,
        b:0,
      }
    ],
    time_counter = 0,
    color_key = 0;


// 5.カラーチェンジの差分取得処理
function set_color_transition() {
    const target_color = colors[color_key];
    const basic_color = color_key !== 0 ? colors[ color_key - 1 ] : colors[colors.length - 1];
    transition = {
        r: (target_color.r - basic_color.r) / duration,
        g: (target_color.g - basic_color.g) / duration,
        b: (target_color.b - basic_color.b) / duration
    }
}

// 6.描画実行処理
function update() {
    time_counter++;

    // 色の変化が終わったら切り替える
    if ( duration === time_counter ) {
        color_key !== colors.length - 1 ? color_key++ : color_key = 0;
        set_color_transition();
        // Reset
        time_counter = 0;
    }

    color = {
        r: color.r + transition.r,
        g: color.g + transition.g,
        b: color.b + transition.b,
    }

    draw();
}
setInterval( update, ( 1000 / FPS ));



// 7.描画処理
function draw() {
    var canvas = new HTMLCanvasElement;
    var ctx = canvas.getContext('2d');

    ctx.fillStyle = 'rgba('+ color.r +','+ color.g +','+ color.b +', 1)';

    ctx.fill();
}

順番に解説

1. RGBのリスト
 変更する3色のRBG値を設定

2. FPS設定
 setIntervalで描画する際に使用、お好みで。

3.色が変化するまでのフレーム数を指定
 どれくらいの時間をかけて色が変わるかをここで指定。お好みで。

4.変数宣言
 まず、letで使用する変数を宣言。
 transition・・・RBG値の変化量のための変数
 color・・・描画時の最終的なRBG値のための変数
 time_counter・・・色が変わるタイミングを検知するためのタイマーとしての変数
 color_key・・・RBGのリストを呼び出すためのキーとして使用する変数
  ※transitonとcolorはデフォルト値で0を入れておかないとエラー出る。

5.カラーチェンジの差分取得処理
 現在のカラーと次の目標のカラーを宣言し、
 transition変数のr,b,gそれぞれに色の変化量を入れる。

6.描画実行処理
 カウンターを呼び出し、色の変化が終わるたびに次の色に変更するため
 set_color_transition関数を再度実行&カウンターをリセットする。
 現在のRBG値にset_color_transition関数で計算した変化量を足してcolorに代入し、
 draw関数を呼び出し実際に描画。

7.描画処理
 canvasを指定、ctx.fillStyleにこれまでに計算したRBG値を入れ描画させる。

8.完成
 durationで設定した間隔で、円の色がRBG値が変わることによりじんわりと変更されました。

最終的なコード

https://awrd.com/sozo-yasashii/ 
こちらの円のように、指定の色に変化したらその色を数秒間キープした後に、ふわっと色を変えたかったので、
上記のコードに加え、キープするためのカウンターをもう一つ追加し、実装しました。

また、ぬるぬる動かしたかったのでSetIntervalでの描画ではなく、
requestAnimationFrameを使って描画することにしました。

// 円の設定
var MAX = 6; //頂点
var RADIUS = 150; //円の半径
var CENTER = {x:250, y:250}; //描画位置
var canvas, ctx, timer, point;


// 円の描画設定
var Point = function(c, r, rota)
{
  this.x, this.y;
  this.centerX = c.x;
  this.centerY = c.y;
  this.radian = rota * (Math.PI / 180);
  this.radius = r;  
  this.speed = Math.random() + .5; //うにょうにょスピード 
  this.r = Math.random() * .2; //やわらかさ  
  this.rota = 0;

  this.update = function(){
    var plus = Math.cos(this.rota * (Math.PI / 180)) * this.r;    
    this.radius += plus    
    var cos = Math.cos(this.radian) * this.radius;
    var sin = Math.sin(this.radian) * this.radius;

    this.x = cos + this.centerX;
    this.y = sin + this.centerY;
    this.rota += this.speed;    
    if(this.rota > 360){ this.rota -= 360; };
  }  
}

// 初期化
function initialize(){
  var rota = 360 / MAX;
  var i;
  for(i = 0; i < MAX; i++)
  {
    point[i] = new Point(CENTER, RADIUS, rota * i); //点を設定値だけ生成
  }
}


// RGBのリスト
const colors = [
  {
    r: 176,
    g: 230,
    b: 142
  },
  {
    r: 164,
    g: 222,
    b: 222
  },
  {
    r: 133,
    g: 185,
    b: 229
  },
];
// 色が変化するまでのフレーム数 - 何秒かけて色が変わるか(ベスト10)
const duration = 35;
// 色を止める時間
const stopTime = 460;


let
  color = {
    r: 176,
    g: 230,
    b: 142
  },
  transition = {
    r: 0,
    g: 0,
    b: 0
  },
  time_counter = 0,
  time_counter2 = 0,
  color_key = 0;  


// 次の色との差分ゲット
function set_color_transition() {
  var target_color = colors[color_key]; //変わる色
  var basic_color = color_key !== 0 ? colors[ color_key - 1 ] : colors[colors.length - 1]; //現在の色
  
  //現在の色と次に変化する色の値との差  
  transition = {
    r: (target_color.r - basic_color.r) / duration,
    g: (target_color.g - basic_color.g) / duration,
    b: (target_color.b - basic_color.b) / duration 
  }

}

// 描画処理
function update(){

  //全ての点に対して描画処理を走らせる
  for(var i = 0; i < MAX; i++)
  {
    point[i].update();     
  }
  
  // duration内の秒数カウンター、現在の目標の色の配列キー        
  time_counter++;  
  time_counter2++; 

  // タイマーが15になったら色止まる
  if ( duration < time_counter) {
    console.log('色停止中');
    color = {
      r:  color.r ,
      g:  color.g ,
      b:  color.b ,
    }    
  }else{
    color = {
      r:  color.r + transition.r,
      g:  color.g + transition.g,
      b:  color.b + transition.b,
    }
  }
  // タイマーが60になったら色が変わる
  if ( stopTime == time_counter2 ) {
    console.log('色変化中');
    color_key !== colors.length - 1 ? color_key++ : color_key = 0;
    set_color_transition();
  
    // Reset
    time_counter = 0;
    time_counter2 = 0;
  }


  
  //描画実行
  draw(); 

  requestAnimationFrame(update);
}


// 描画設定
function draw(){  
  ctx.clearRect(0, 0, canvas.width, canvas.height); //初期化    

  ctx.beginPath();
  var xc1 = (point[0].x + point[MAX - 1].x) / 2;
  var yc1 = (point[0].y + point[MAX - 1].y) / 2; 
  ctx.moveTo(xc1, yc1);  
  for(var i = 0; i < MAX - 1; i++){
    var xc = (point[i].x + point[i + 1].x) / 2;
    var yc = (point[i].y + point[i + 1].y) / 2;  
    ctx.quadraticCurveTo(point[i].x, point[i].y, xc, yc)
  }
  
  ctx.quadraticCurveTo(point[i].x, point[i].y, xc1, yc1);  
  ctx.closePath();  
    
  ctx.fillStyle = 'rgba('+ color.r +','+ color.g +','+ color.b +', 1)';
  
  ctx.fill();  
}

// 実際に動かす
window.onload = function(e){
  canvas = document.getElementById("canvas"); //canvasを指定
  ctx = canvas.getContext("2d"); //canvasの初期設定
  point = [];  //円の点の配列を用意
  initialize(); //点を初期化
  update();
}

完成形

完成したうにょうにょ動いて色が変わる円がこちら

ezgif.com-gif-maker.gif

参考

https://kkoo.hateblo.jp/entry/2015/02/18/192007
https://awrd.com/sozo-yasashii/
https://developer.mozilla.org/ja/docs/Web/API/Canvas_API/Tutorial/Applying_styles_and_colors
https://jablogs.com/detail/67575

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?