この記事では、JavaScriptでcanvasを用いて、個人で作成した「クレメアメーカー」についてのコードとその時に思考していたことを書いています。作ったサイトはこちらになります。
作ろうと思ったきっかけなどはnoteに書いているので気になる方はご覧ください。
canvasの範囲指定
まず元の画像をもとにサイズを指定しました。
元画像が400×400なので同じサイズにcanvasタグを設定し表示。
<div>
<canvas class="card-img-top" id="cremea" width="400" height="400"></canvas>
</div>
// canvasの指定
var canvas = document.getElementById('cremea');
var ctx = canvas.getContext('2d');
外円の描画
次に円を描画していきます。
・・・クレメアさんのマークの円を外円と内円で分けて描画してみるかと思いつきます。
「canvas 円 描画」でググってコードを調べる。
ふむふむ・・・外円を外側と内側2つの線で描画やなと思いそうしました(書いてるときに見つけたけど、太さ指定できるの知らんかった・・・)。
座標位置・半径は元画像とにらめっこしながら、あーでもないこーでもないと言いながら指定しました。
ctx.arcの部分は(x座標, y座標, 半径, 開始角度, 終了角度, 方向)
方向は時計回り(false)か反時計回り(true)らしい(半円とかのときに役立つかもですが今回はあまり関係ないのでtrueにしてます)
塗りつぶしの色とかもここで指定。
function getCircleDraw(fColor, sColor) {
// 円の描画位置
var x = 200;
var y = 200;
// 塗りつぶし色
var fillColor = (fColor)? fColor: '#000';
// 枠線色
var strokeColor = (sColor) ? sColor: '#000';
// 1番大きい円(外側)
circleDraw(ctx, x, y, 197, fillColor, strokeColor);
// 1番大きい円(内側)
circleDraw(ctx, x, y, 165, '#fff', strokeColor);
}
// 円の描画
var circleDraw = function(ctx, x, y, radius, fillColor, strokeColor) {
ctx.beginPath();
// 塗りつぶし色
ctx.fillStyle = fillColor;
// 枠線の色
ctx.strokeStyle = strokeColor;
// 円を描画
ctx.arc(x, y, radius, 0, Math.PI*2, true);
// 塗りつぶし
ctx.fill();
// 枠線
ctx.stroke();
}
実行するとこんな感じで描画されます(わかりやすいよう、わざと塗りつぶしてません)。
よし、外の円できた!次は中の円や!
内円の描画
次に内側の円を描画。
右側に円をずらすのでx座標の位置を変更してサイズも小さくして描画します。
ここも元画像とにらめっこしながら半径を指定。
// getCircleDrawに追加
x = 300;
// 右側に入れる円(外側)
circleDraw(ctx, x, y, 93, fillColor, strokeColor);
// 右側に入れる円(内側)
circleDraw(ctx, x, y, 65, '#fff', strokeColor);
おー、なんかクレメアマークっぽいの出来てきた!
さて、次が難問の星マークの描画や!
星マークの描画
続いて星マーク(六角星)の描画。
「canvs 星 描画」で検索検索〜。
HTML5 Canvas で Star Polygon(星型多角形)描画:TM Life
とりあえずソースをコピる。
必要のない部分を除去して描画。
starDrow関数は(canvas, x座標, y座標, 半径, 塗りつぶし色, 枠線色, 辺の数, 凹ませ具合, 星の角度)です。
辺の数は6個なので6に指定。
凹ませ具合と星の角度は元画像とにらめっkry...
// 星の描画位置
var x = 120;
var y = 260;
// 塗りつぶし色
var fillColor = '#fff';
// 枠線色
var strokeColor = '#000';
// 変の数
var sides = 6;
// 凹ませ具合
var sideIndent = 0.48;
// 星の角度
var offsetAngle = 250;
// 描画
starDraw(ctx, x, y, 63, fillColor, strokeColor, sides, sideIndent, offsetAngle);
// 星の描画
var starDraw = function(ctx, x, y, radius, fillColor, strokeColor, sides, sideIndent, offsetAngle) {
ctx.beginPath();
// 内円の大きさ
var sideIndentRadius = radius * (sideIndent || 0.38);
// 描画の角度
var radOffset = (offsetAngle) ? offsetAngle*Math.PI/180: -Math.PI/2;
//
var radDiv = (Math.PI*2)/sides/2;
// 塗りつぶし色
ctx.fillStyle = fillColor;
// 枠線の色
ctx.strokeStyle = strokeColor;
// パスの開始場所を指定
ctx.moveTo(
x + Math.cos(radOffset)*radius,
y + Math.sin(radOffset)*radius
);
for (var i=1; i<=sides*2; ++i) {
var rad = radDiv*i + radOffset;
// 内円, 外円を交互にパスをセット
var len = (i%2) ? sideIndentRadius : radius;
// 座標指定してラインを描画
ctx.lineTo(
x + Math.cos(rad)*len,
y + Math.sin(rad)*len
);
}
ctx.fill();
ctx.stroke();
}
よし、基礎は出来た!
わーいわーい(`・ω・´)
星マークの数(辺)の変更
やりたかったこと①のクレメアマークの星の形の変更をします。
最初に考えたのは、radioボタンで任意の数字でやってもらうこと。
でも自由度が下がるのでやめて、ユーザーに好きな数字に変更して貰うようにinputタグを設置。
数字が変更されたら星の形を変えるよう変更。
ここで悩んだのは、inputをnumberにするかどうかでした。
numberであればpcの場合矢印キーで数字変更が容易にできるのですが、
スマホユーザーをメインターゲットにしていたので、スマホでテンキーが出るtelにしました。
途中うまく描画されなかったり、クリアしないと前の星描画残ったりとか色々試行錯誤しました。
<div>
<label>星の形</label>
<input type="tel" name="cremea-star" value="6" min="0" id="cremea-star" onchange="getDraw(this)">
</div>
// 星の描画
function getDraw(obj, fColor, sColor) {
var selectColor;
// 星の描画位置
var x = 120;
var y = 260;
// 塗りつぶし色
var fillColor = (fColor)? fColor: '#000';
// 枠線色
var strokeColor = (sColor) ? sColor: '#000';
// 変の数
var sides = (obj) ? obj.value: 6;
// 凹ませ具合
var sideIndent = 0.48;
// 星の角度
var offsetAngle = 250;
// 描画クリア
ctx.clearRect(0, 0, 400, 400);
// 円再描画
getCircleDraw(fillColor, strokeColor);
// 星の描画
starDraw(ctx, x, y, 63, fillColor, strokeColor, sides, sideIndent, offsetAngle);
}
うまく描画できた!わーいわーい(`・ω・´)
ここで力尽きて(仕事多忙になり)1ヶ月くらい放置してました←
色の設定
潜伏期間を経てモチベが戻ったので最後にやりたかったこと②の色変更を行います。
色の種類はビビットカラーとパステルカラーにしました。
カラーピッカープラグインなども使おうかと思いましたが、プラグインは増やしたくなかったので自作することに。
色を変更したらspanタグを押した色になるようにしています。
読み込み時にカラーピッカーを生成。
<div class="card-text">
色:<span id="select-color">■</span>
<div id="picker"></div>
</div>
// 生成する色配列
var colors = [
'#000000', '#808080',
'#ff0000', '#ff7f7f',
'#ff007f', '#ff7fbf',
'#ff00ff', '#ff7fff',
'#7f00ff', '#bf7fff',
'#0000ff', '#7f7fff',
'#007fff', '#7fbfff',
'#00ffff', '#7fffff',
'#00ff7f', '#7fffbf',
'#00ff00', '#7fff7f',
'#7fff00', '#bfff7f',
'#ffff00', '#ffff7f',
'#ff7f00', '#ffbf7f'
];
// 生成
colors.forEach(function(color, i) {
colorSpan = document.createElement('span');
colorSpan.innerHTML = '■';
colorSpan.classList.add('color-change');
colorSpan.style.color = color;
colorSpan.style.fontSize = '200%';
colorSpan.setAttribute('color', color);
colorFlg = (i == 0) ? true: false;
colorSpan.setAttribute('setcolor', colorFlg);
colorSpan.setAttribute('onclick', 'changeColor(this)');
picker.appendChild(colorSpan);
});
// 色変更
function changeColor(obj) {
var colorChange = document.getElementsByClassName('color-change');
for( var i = 0; i < colorChange.length; i++ ) {
// 一旦falseに
colorChange[i].setAttribute('setcolor', false);
};
cremeaColor = obj.getAttribute('color');
obj.setAttribute('setcolor', true);
cremeaStar = document.getElementById('cremea-star');
selectColor = document.getElementById('select-color');
selectColor.style.color = cremeaColor;
// 描画クリア
ctx.clearRect(0, 0, 400, 400);
// 再描画
getDraw(cremeaStar, cremeaColor, cremeaColor);
}
canvasを画像化して保存
最後仕上げとして、作ったクレメアマークを画像(png)として保存できるように機能追加。
こちらの記事を参考にcanvas画像を保存するボタンとコードを作成。
実務でも使った経験があるくらい便利なコードです。
たった8行!キャンバスをワンクリックで保存させるJavaScriptコード(IEも大丈夫)
ボタンを押すとpng画像としてダウンロードされます。
IEとEdgeにも対応しています(確認はしていません)。
iPhoneとiPadは保存ボタンを押したあとに、表示された画像長押しで保存(safariの仕様上そうなっているぽい)
<input type="submit" name="save-cremea" value="画像を保存" class="btn btn-primary" id="download-button">
<a id="download-link"></a>
// 画像の保存
var downloadLink = document.getElementById('download-link');
var filename = 'cremea-maker.png';
var button = document.getElementById('download-button');
button.addEventListener('click', function() {
if (canvas.msToBlob) {
// IE・edge
var blob = canvas.msToBlob();
window.navigator.msSaveBlob(blob, filename);
} else {
// chromeなど
downloadLink.href = canvas.toDataURL('image/png');
downloadLink.download = filename;
downloadLink.click();
}
});
最後に
主な作業時間は5日間くらいですかね(構想から実行に移すまで三ヶ月とかそのくらいですが)。
今後拡張するとしたら、カラーピッカーの色増やす・角度をずらす(全体もしくは星の部分)とかですかね。
何かこうしたら面白そうじゃない?とかあったらコメントいただけると嬉しいです。
GitHubにコード公開しているので興味ある方はみてみてくださいね。