0
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

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

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

クレメアメーカー

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

クレメアメーカー作ったわけ:note

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

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

円・円弧を描く:JavaScript入門[HTML5編]

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

Canvasで円を描く方法:Syncer

座標位置・半径は元画像とにらめっこしながら、あーでもないこーでもないと言いながら指定しました。
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';
  // 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();
}

実行するとこんな感じで描画されます(わかりやすいよう、わざと塗りつぶしてません)。

cremea-outer-circle.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

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

星マークの描画

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

HTML5 Canvas で Star Polygon(星型多角形)描画:TM Life

とりあえずソースをコピる。
必要のない部分を除去して描画。
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

星マークの数(辺)の変更

やりたかったこと①のクレメアマークの星の形の変更をします。
最初に考えたのは、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

色の設定

潜伏期間を経てモチベが戻ったので最後にやりたかったこと②の色変更を行います。
色の種類はビビットカラーとパステルカラーにしました。
カラーピッカープラグインなども使おうかと思いましたが、プラグインは増やしたくなかったので自作することに。
色を変更したら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画像を保存するボタンとコードを作成。
実務でも使った経験があるくらい便利なコードです。
たった8行!キャンバスをワンクリックで保存させるJavaScriptコード(IEも大丈夫)

ボタンを押すと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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
0
Help us understand the problem. What are the problem?