HTML
JavaScript
canvas

Canvasで漫画にあるような集中線を書いてみた

More than 3 years have passed since last update.


はじめに

集中線GIFメーカーという面白いサービスにインスパイアを受け、Canvasで真似してみました。本家様のコードは一切見ずに作成したため、書き方やアニメーションの動作はまた違ったものになっているかと思います。結構良い感じにできたので公開してみます。

何かの参考にでもなれば幸いです。

[動作イメージ]

line_anim1.gif

line_anim2.gif


なお実際の動作は以下で確認することができます

http://nekoneko-wanwan.github.io/demo/canvas/effect/focus_line/

http://codepen.io/nekoneko-wanwan/pen/xwRjbq



特長

なるべく拡張性・汎用性が高くなるように作成しました。


  • Canvasサイズに依存しない

  • 集中線の数が変更できる

  • 集中線の色と太さが変更できる

  • 集中線の中心部の大きさと位置が変更できる(位置については少しスクリプトの修正が必要)


ソースコード


index.html

<canvas id="myCanvas" width="500" height="500"></canvas>



canvas.js

/**

* 集中線メーカー
* @param {obj} canvas object
* @param {number} centralX: 集中線を配置するx座標
* @param {number} centralY: 集中線を配置するy座標
* @param {number} lineWidth: 線の太さ(ランダムの上限)
* @param {number} lineNum: 線の数
* @param {number} circleRadiusMax: 集中線の円形の半径上限
* @param {number} circleRadiusMin: 集中線の円形の半径下限
* @param {string} lineColor: 集中線の色
*/

var focusLine = function(cs, centralX, centralY, lineWidth, lineNum, circleRadiusMax, circleRadiusMin, lineColor) {

var ctx = cs.getContext('2d');
var lines = [];

// canvasの中心から角までの斜辺距離を円の半径とする
// 中心をズラす場合は、もっと大きな値を設定してやる(引数でできるようにすべきか)
var csRadius = Math.sqrt(Math.pow(cs.width / 2, 2) + Math.pow(cs.height / 2, 2)) | 0;

/**
* ランダムな整数を返す
* @param max 最大値
* @param min 最小値
* @return min ~ max
*/

var getRandomInt = function(max, min) {
return Math.floor(Math.random() * (max - min)) + min;
};

/**
* 円周上の座標を返す
* @param d 角度
* @param r 半径
* @param cx, cy 中心座標
*/

var getCircumPos = {
// x座標
x: function(d, r, cx) {
return Math.cos(Math.PI / 180 * d) * r + cx;
},
// y座標
y: function(d, r, cy) {
return Math.sin(Math.PI / 180 * d) * r + cy;
}
};

/**
* @constructor
*/

var Liner = function() {
this.initialize();
};
Liner.prototype = {
/* initialize()内の設定をsetPos()内へ移すと、アニメーションの動きが変わる */
initialize: function() {
this.deg = getRandomInt(360, 0);
},
setPos: function() {
this.moveDeg = this.deg + (getRandomInt(lineWidth, 1) / 10);
this.endRadius = getRandomInt(circleRadiusMax, circleRadiusMin);

// 開始座標
this.startPos = {
x: getCircumPos.x(this.deg, csRadius, centralX),
y: getCircumPos.y(this.deg, csRadius, centralY)
};

// 移動座標
this.movePos = {
x: getCircumPos.x(this.moveDeg, csRadius, centralX),
y: getCircumPos.y(this.moveDeg, csRadius, centralY)
};

// 終了座標
this.endPos = {
x: getCircumPos.x(this.moveDeg, this.endRadius, centralX),
y: getCircumPos.y(this.moveDeg, this.endRadius, centralY)
};
},
update: function() {
this.setPos();
},
draw: function() {
ctx.beginPath();
ctx.lineWidth = 1;
ctx.fillStyle = lineColor;
ctx.moveTo(this.startPos.x, this.startPos.y);
ctx.lineTo(this.movePos.x, this.movePos.y);
ctx.lineTo(this.endPos.x, this.endPos.y);
ctx.fill();
ctx.closePath();
},
render: function() {
this.update();
this.draw();
}
};

/**
* 線インスタンスの作成
* @return lines[instance, instance...];
*/

function createLines(num) {
var i = 0;
for (; i < num; i++) {
lines[lines.length] = new Liner();
}
}

/**
* 描画
*/

function render() {
var i = 0;
var l = lines.length;
ctx.clearRect(0, 0, cs.width, cs.height);
for (; i < l; i++) {
lines[i].render();
}
setTimeout(function() {
render();
}, 100);
}

createLines(lineNum);
render();
};

/**
* focusLine()に渡す引数の設定
*/

var cs = document.getElementById('myCanvas');
var conf = {
cx: cs.width / 2,
cy: cs.height / 2,
lineWidth: 30,
lineNum: 200,
crMax: 200, // 集中線の円形の半径上限
crMin: 150, // 集中線の円形の半径下限
color: 'black'
};

focusLine(cs, conf.cx, conf.cy, conf.lineWidth, conf.lineNum, conf.crMax, conf.crMin, conf.color);



作り方・考え方


  1. canvas要素の外側に点Aをランダムに配置

  2. 点Aから少し角度をズラして、近くに点Bを配置

  3. canvasの中心部Cに向けて線を引き、細長い三角形を作成

  4. 1-3を沢山繰り返すことで、四方八方から中央に向かって線が伸びていく


canvas要素の外側に点Aを配置

線が見切れてしまうことを防ぎます。


集中線を中央以外に配置する場合は、外側の円周 csRadius をもっと大きくします。(書いている途中で気づいたので、スクリプトは調整していません。。。)


[イメージ]

sample1.png


点Aから少し角度をズラして、近くに点Bを配置

角度を変えて結ぶことで線の太さを表現します。角度の増減をランダムにすることで自然な集中線を表現します

[イメージ]

sample2.png


canvasの中心部Cに向けて線を引き、細長い三角形を作成

点Aと点B、終点Cを結びます。ただし完全な中心点だと中央の余白が作れないため、中に小さな円を想定し、その円周上を終点Cとします。実際には三角形ごとに中の小円半径をランダムに変え、ギザギザの余白ができるようにします。

[イメージ]

sample3.png

sample4.png


終わりに

作り方は自分で考えてみたので、非効率な可能性があります。もっと良い方法があればご教授くださいませ。

[おまけ]

other1.gif

other2.gif