javascriptとcanvasでクレメアメーカーをどのようにして作ったかとその時に思っていたこと

この記事では、JavaScriptでcanvasを用いて、個人で作成した「クレメアメーカー」についてのコードとその時に思考していたことを書いています。作ったサイトはこちらになります。

[クレメアメーカー](https://mac-like.com/cremea-maker/)

作ろうと思ったきっかけなどはnoteに書いているので気になる方はご覧ください。

[クレメアメーカー作ったわけ:note](https://note.mu/yuki0113/n/n81aef5496eff)

## canvasの範囲指定
まず元の画像をもとにサイズを指定しました。
元画像が400×400なので同じサイズにcanvasタグを設定し表示。

```cremea-maker.html
<div>
  <canvas class="card-img-top" id="cremea" width="400" height="400"></canvas>
</div>
```


```cremea.maker.js
// canvasの指定
var canvas = document.getElementById('cremea');
var ctx = canvas.getContext('2d');
```

## 外円の描画
次に円を描画していきます。

「クレメアさんのマーク」⬇
![cremea.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481157/179b2ce4-ba29-9e10-69ba-473d6c654c46.png)

・・・クレメアさんのマークの円を外円と内円で分けて描画してみるかと思いつきます。
「canvas 円　描画」でググってコードを調べる。

[円・円弧を描く:JavaScript入門[HTML5編]](https://www.pazru.net/html5/Canvas/040.html)

ふむふむ・・・外円を外側と内側2つの線で描画やなと思いそうしました(書いてるときに見つけたけど、太さ指定できるの知らんかった・・・)。

[Canvasで円を描く方法:Syncer](https://lab.syncer.jp/Web/JavaScript/Canvas/4/)

座標位置・半径は元画像とにらめっこしながら、あーでもないこーでもないと言いながら指定しました。
ctx.arcの部分は(x座標, y座標, 半径, 開始角度, 終了角度, 方向)
方向は時計回り(false)か反時計回り(true)らしい(半円とかのときに役立つかもですが今回はあまり関係ないのでtrueにしてます)
塗りつぶしの色とかもここで指定。

```cremea.maker.js
function getCircleDraw(fColor, sColor) {
  // 円の描画位置
  var x = 200;
  var y = 200;
  // 塗りつぶし色
  var fillColor = (fColor)? fColor: '#000';
  // 枠線色
  var strokeColor = (sColor) ? sColor: '#000';
  // １番大きい円（外側）
  circleDraw(ctx, x, y, 197, fillColor, strokeColor);
  // １番大きい円（内側）
  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();
}
```
実行するとこんな感じで描画されます（わかりやすいよう、わざと塗りつぶしてません）。

![cremea-outer-circle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481157/db6b75a5-c53e-b555-e544-1994a2b2b1df.png)

よし、外の円できた！次は中の円や！

## 内円の描画
次に内側の円を描画。
右側に円をずらすのでx座標の位置を変更してサイズも小さくして描画します。
ここも元画像とにらめっこしながら半径を指定。

```cremea.maker.js
  // getCircleDrawに追加
  x = 300;
  // 右側に入れる円（外側）
  circleDraw(ctx, x, y, 93, fillColor, strokeColor);
  // 右側に入れる円（内側）
  circleDraw(ctx, x, y, 65, '#fff', strokeColor);
```
実行後⬇
![cremea-inner-circle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481157/4ad8c551-1bf3-ce4f-cc6a-c64205658f0f.png)

おー、なんかクレメアマークっぽいの出来てきた！
さて、次が難問の星マークの描画や！

## 星マークの描画
続いて星マーク(六角星)の描画。
「canvs 星　描画」で検索検索〜。

[HTML5 Canvas で Star Polygon(星型多角形)描画:TM Life](http://tmlife.net/programming/javascript/html5-canvas-star-polygon-draw.html)

とりあえずソースをコピる。
必要のない部分を除去して描画。
starDrow関数は(canvas, x座標, y座標, 半径, 塗りつぶし色, 枠線色, 辺の数, 凹ませ具合, 星の角度)です。
辺の数は6個なので6に指定。
凹ませ具合と星の角度は元画像とにらめっkry...

```cremea.maker.js

// 星の描画位置
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();
}
```
よし、基礎は出来た！
わーいわーい(｀・ω・´)

![cremea-star.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481157/2a973395-b468-b166-df74-7596b601d785.png)

## 星マークの数（辺）の変更
やりたかったこと①のクレメアマークの星の形の変更をします。
最初に考えたのは、radioボタンで任意の数字でやってもらうこと。
でも自由度が下がるのでやめて、ユーザーに好きな数字に変更して貰うようにinputタグを設置。
数字が変更されたら星の形を変えるよう変更。
ここで悩んだのは、inputをnumberにするかどうかでした。
numberであればpcの場合矢印キーで数字変更が容易にできるのですが、
スマホユーザーをメインターゲットにしていたので、スマホでテンキーが出るtelにしました。
途中うまく描画されなかったり、クリアしないと前の星描画残ったりとか色々試行錯誤しました。

```cremea-maker.html
<div>
  <label>星の形</label>
  <input type="tel" name="cremea-star" value="6" min="0" id="cremea-star" onchange="getDraw(this)">
</div>
```

```cremea.maker.js
// 星の描画
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ヶ月くらい放置してました←
![cremea-star-draw.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/481157/09c9c8e8-283d-2281-70d7-91c2e520ec86.png)

## 色の設定
潜伏期間を経てモチベが戻ったので最後にやりたかったこと②の色変更を行います。
色の種類はビビットカラーとパステルカラーにしました。
カラーピッカープラグインなども使おうかと思いましたが、プラグインは増やしたくなかったので自作することに。
色を変更したらspanタグを押した色になるようにしています。
読み込み時にカラーピッカーを生成。

```cremea-maker.html
<div class="card-text"> 
  色：<span id="select-color">■</span>
  <div id="picker"></div>
</div> 
```

```cremea.maker.js
// 生成する色配列
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画像を保存するボタンとコードを作成。
実務でも使った経験があるくらい便利なコードです。
[たった８行！キャンバスをワンクリックで保存させるJavaScriptコード（IEも大丈夫）](https://blog.capilano-fw.com/?p=247)

ボタンを押すとpng画像としてダウンロードされます。
IEとEdgeにも対応しています（確認はしていません）。
iPhoneとiPadは保存ボタンを押したあとに、表示された画像長押しで保存（safariの仕様上そうなっているぽい）

```cremea-maker.html

<input type="submit" name="save-cremea" value="画像を保存" class="btn btn-primary" id="download-button">
<a id="download-link"></a>

```

```cremea.maker.js

// 画像の保存
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にコード公開しているので興味ある方はみてみてくださいね。

[cremea-maker:ShakaSeYuki](https://github.com/ShakaSeYuki/cremea-maker)