Canvasで画像を加工しよう -円環の理に導かれて-

ボクと契約してCanvas魔法使いになってよ!

ほむほむ「『キュゥべえ』をクリックしましょう」

QB

(1) 画像をCanvasに読み込み
onloadの完了を監視して、sketch関数を呼び出します。
sketch関数の中ではDOMで取得した画像の情報を読み込んで、
Canvas内に描画し、DOMの書き換えを行っています。
準備が整ったところで画像の加工に入ります。

(2) クリックイベント
ただ、機械的に処理するだけでは面白くありませんから、
ユーザーのアクションに対応させてみましょう。
addEventListener(IEの場合はattachEvent)で
Canvas要素がクリックされるのを監視し、shot関数を呼び出します。
引数にはクリックした座標を得るためのイベントオブジェクトeと
Canvasに描画するためのAPIオブジェクトctxを渡します。

(3) 画像の加工・合成処理
いよいよ画像の加工に入ります。
globalCompositeOperationで合成処理を行います。
'destination-out'は元の画像をくり抜く合成処理です。

今回の処理ではクリックした座標を中心に
ランダムな複数の円を描画しています。
クリックしたCanvas要素内での座標を e.offsetX, e.offsetY で取得し、
極座標でクリック位置からの距離radius、角度angleを乱数で指定します。

単純なの指定は3行で成り立ちます。ctx.beginでパスを開始し、
円形のっかをctx.arcのパスで指定、x,yを中心に半径rの円を描きます。
これら座標を中心にctx.fillで範囲を塗りつぶしています。
以上を解したら、あとはfor文で繰り返すのみです。

ついで加工前の状態に戻す処理もしてみましょう。
処理の線としてボタンのHTMLタグを用意します。
ボタンらイベントが起きるとcall関数を呼び出します。
描画さた状態を記憶することはできないので、
工夫し再度同じ処理を行う必要があります。

前回の合成処理で合成ルールが'destination-out'のままなので、
そのまま画像データを描画しようとすると、さらにくり抜かれてしまいます。
デフォルトの合成ルールである'source-over'に戻してから、
最初の画像データを上書きしましょう。

こんなのひどいよ!あんまりだよ!

以上。
Canvasによる画像の加工でした。

view source

JavaScript

function sketch(){
  // (1) 画像をキャンバス差し替える
  var image = document.getElementsByTagName('img')[0];
  var object = image.parentNode;
  var canvas = document.createElement('canvas');
  var ctx = canvas.getContext("2d");
  canvas.style.height = image.height+'px';
  canvas.style.width = image.width+'px';
  canvas.height = image.height;
  canvas.width = image.width;
  canvas.src = image.src; 
  object.replaceChild(canvas,image);
  ctx.drawImage(image, 0, 0);

  // (2) クリックイベントを監視して、shot()を実行する
  // ※ attachEventはIE用
  if(canvas.addEventListener){
    canvas.addEventListener("click", function(e){
      console.log(e);
      console.log(e.offsetY);
      shot(e, ctx);
    }, false);
  } else {
    canvas.attachEvent("click", function(e){
      shot(e, ctx);
    });
  }


  // ボタンのクリックイベントを監視
  var button = document.getElementsByTagName('button')[0];
  if(button.addEventListener){
    button.addEventListener("click", function(e){
      call(ctx, image);
    }, false);
    console.log(ctx);
  } else {
    button.attachEvent("click", function(e){
      call(ctx, image);
    });
  }


  //座標クリックイベント
  console.log(canvas.getBoundingClientRect().top);
  var clientX = canvas.getBoundingClientRect().left;
  var clientY = canvas.getBoundingClientRect().top;
  var e = document.createEvent('MouseEvents');
  e.initMouseEvent(
    'click', // type
    true, // bubbles
    true, // cancelable
    window, // view
    1, // detail(クリック数)
    0, // screenX(デバイス全体における座標)
    0, // screenY
    clientX+20, // clientX(ブラウザ表示域における座標)
    clientY+20, // clientY
    false, // ctrlKey
    false, // altKey
    false, // shiftKey
    false, // metaKey
    0, // button(0 が左、1 が中、2 が右クリック)
    null // relatedTarget
  );
  var n = canvas || document.body // クリックのターゲットにしたいノード
  n.dispatchEvent(e);


}




function shot(e, ctx){
  // (3) クリックした座標を中心に複数の円形を描画する
  // 合成処理"destination-out"で描画した範囲で元の画像を消去している
  ctx.globalCompositeOperation = 'destination-out';
  var cx = e.offsetX;
  var cy = e.offsetY;
  var d = 160;
  var bullets = 32;
  var i;
  var x, y, r;
  var radius ,angle;
  console.log('xy=' + cx + ':' + cy);
  for(i=0; i<bullets; i++){
    radius = Math.random() * d;
    angle =  Math.random() * Math.PI * 2;
    x = cx + radius * Math.sin(angle);
    y = cy + radius * Math.cos(angle);
    r = Math.random() * 10 + 5;

    ctx.beginPath();
    ctx.arc(x, y, r, 0, Math.PI * 2, false);
    ctx.fill();
  }
}



function call(ctx, image){
  // (4) 復活の呪文
  // 再度、画像を呼び出します。
  ctx.globalCompositeOperation = 'source-over';
  ctx.drawImage(image, 0, 0);
}


function clipCircle(ctx){
  // 同じ表現をclipでできないかと思ったが、
  // 透過のピクセルで上書きするputImageData()は円形のclip()が効かず、
  // ただの四角い透過になってしまう。残念。
  var x=80;
  var y=80;
  var r=30;
  ctx.beginPath();
  ctx.arc(x, y, r, 0, Math.PI * 2, false);
  ctx.clip();
  var img = ctx.createImageData(80, 80);
  ctx.putImageData(img, x, y);
}




if(window.addEventListener){
  window.addEventListener("load", sketch, false);
} else{
  window.attachEvent("onload", sketch);
}

CSS

img{
  background-color:#b9b9b9;
}
canvas{
  background-color:#9b9b9b;
}
p{
  font-family: monospace;
  margin:12px;
}
em{
  font-weight:bold;
  font-size:24px;
}

HTML

ページのソースを表示 : Ctrl+U , DevTools : F12

view-source:https://hi0a.com/demo/-js/js-canvas-composite/

ABOUT

hi0a.com 「ひまアプリ」は無料で遊べるミニゲームや便利ツールを公開しています。

プログラミング言語の動作デモやWEBデザイン、ソースコード、フロントエンド等の開発者のための技術を公開しています。

必要な機能の関数をコピペ利用したり勉強に活用できます。

プログラムの動作サンプル結果は画面上部に出力表示されています。

環境:最新のブラウザ GoogleChrome / Windows / Android / iPhone 等の端末で動作確認しています。

画像素材や音素材は半分自作でフリー素材配布サイトも利用しています。LINK参照。

動く便利なものが好きなだけで技術自体に興味はないのでコードは汚いです。

途中放置や実験状態、仕様変更、API廃止等で動かないページもあります。