画像の一部をキャプチャして書き出すの巻。
画像の書出しは以前も行いました。
使用メソッドはcanvas.toDataURL
です。
CanvasElementのメソッドで画像変換が可能なのですが、一部だけを画像にするオプションは用意されていないようです。
それで、書出し用のcanvasを別に用意しそこへキャプチャした画像を表示させるようにしています。
クロップ領域を表す破線矩形はマウスに追随します。
書出し用のボタンを配置しています。
どちらもマウスアクションにより操作するのですが、前回Canvas上で表示させて苦労したので今回はHTMLElemntで作成しCanvas上にのっけています。
Canvas上の画像の一部を複製書出し
HTML
<canvas id="canvas" width="600" height="400" style="background: #fff"></canvas> <div id="controller"> <div id="imageContainer"></div> <div id="view"><canvas id="view-canvas" width="240" height="180"></canvas></div> <div id="clip"></div> <div id="export"><a href="#export" id="png">Export</a></div> </div> |
JavaScript
var canvas = document.getElementById('canvas'); var context = canvas.getContext('2d'); var canvas2 = document.getElementById('view-canvas'); var context2 = canvas2.getContext('2d'); var requestAnimationFrame = new function () { var s = 'equestAnimationFrame', w = window, r = w['r'+r] = w['r'+r] || w['webkitR'+r] || w['mozR'+r] || w['msR'+r] || w['oR'+r]; return function (callback) { if (r) { return r(callback); }; setTimeout(callback, 1000 / 24) }; }; /** * * @type {Object} */ var MouseAction = { CLICK: 'click', MOUSE_OVER: 'mouseover', MOUSE_OUT: 'mouseout', MOUSE_UP: 'mouseup', MOUSE_DOWN: 'mousedown', MOUSE_MOVE: 'mousemove', /** * * @param e:MouseEvent * @return {Object} */ getOffset: function (e) { var x = y = 0; x = e.clientX - canvas.offsetLeft; y = e.clientY - canvas.offsetTop; return {x: x, y: y}; } }; /** * * @param canvas: CanvasElement * @param context: context 2d * @type {*} */ var Stage = (function (canvas, context) { var enable = false, mouse = {}, event = '', callbacks = [], loops = [], event = '', onOver = function (e) { enable = true; canvas.addEventListener(MouseAction.MOUSE_MOVE, onMove); event = e; }, onOut = function (e) { enable = false; canvas.removeEventListener(MouseAction.MOUSE_MOVE, onMove); event = e; }, onMove = function (e) { enable = true; event = e; for (var i = 0, limit = callbacks.length; i < limit; i++) { callbacks[i](event); }; }, loop = function () { for (var i = 0, limit = loops.length; i < limit; i++) { loops[i](event); }; requestAnimationFrame(loop); }, stage = function () { canvas.addEventListener(MouseAction.MOUSE_OVER, onOver); canvas.addEventListener(MouseAction.MOUSE_OUT, onOut); canvas.addEventListener(MouseAction.MOUSE_MOVE, onMove); loop(); } ; new stage(); return { addListener: function (type, callback) { if (type != MouseAction.MOUSE_OVER && type != MouseAction.MOUSE_OUT) { canvas.addEventListener(type, callback, false); } else { callbacks.push(callback); }; }, removeListener: function (type, callback) { if (type != MouseAction.MOUSE_OVER && type != MouseAction.MOUSE_OUT) { canvas.removeEventListener(type, callback, false); } else { callbacks = ArrayUtil.remove(callbacks, callback); }; }, addLoop: function (callback) { loops.push(callback); }, removeLoop: function (callback) { loops = []; }, clear: function () { context.clearRect(0,0,canvas.width,canvas.height); } }; }(canvas, context)); // ============================================ var init = function () { context.drawImage(image, 0, 0); var document = window.document, clip = document.querySelector('#clip'), img = document.querySelector('#imageContainer'), output = document.querySelector('#export'), position = {x: 0, y: 0}, check = function () { return !(position.x < 0 || position.y < 0 || position.x > 300 || position.y > 400); }, onLoop = function () { clip.style.left = position.x + 'px'; clip.style.top = position.y + 'px'; var iw = 120, ih = 90, ow = 240, oh = 180, p = position; clip.style.width = iw + 'px'; clip.style.height = ih + 'px'; if ((p.x + 120) > 300) { iw = 300 - p.x; ow = ow * ( iw / 120 ); clip.style.width = iw + 'px'; }; if ((p.y + 90) > 400) { ih = 400 - p.y; oh = oh * ( ih / 90 ); clip.style.height = ih + 'px'; }; context2.clearRect(0, 0, canvas2.width, canvas2.height); if (iw > 0 && ih > 0 && ow > 0 && oh > 00) { context2.drawImage(canvas, position.x, position.y, iw, ih, 0, 0, ow, oh); }; }, onImageUp = function (e) { disable(); enable(); }, onImageDown = function (e) { position = MouseAction.getOffset(e); img.removeEventListener(MouseAction.MOUSE_DOWN, onImageDown); window.addEventListener(MouseAction.MOUSE_UP, onImageUp, false); window.addEventListener(MouseAction.MOUSE_MOVE, onImageMove, false); onLoop(); clip.style.display = 'block'; document.body.style.cursor = 'crosshair'; }, onImageOver = function (e) { enable(); }, enable = function () { document.body.style.cursor = 'default'; img.addEventListener(MouseAction.MOUSE_DOWN, onImageDown, false); img.addEventListener(MouseAction.MOUSE_OUT, onImageOut, false); }, disable = function () { clip.style.display = 'none'; img.removeEventListener(MouseAction.MOUSE_OUT, onImageOut); img.removeEventListener(MouseAction.MOUSE_DOWN, onImageDown); window.removeEventListener(MouseAction.MOUSE_UP, onImageUp); window.removeEventListener(MouseAction.MOUSE_MOVE, onImageMove); }, onImageOut = function (e) { position = MouseAction.getOffset(e); if (check()) { return; }; disable(); }, onImageMove = function (e) { position = MouseAction.getOffset(e); if (!check()) { disable(); return; }; onLoop(); }, onOutputClick = function (e) { e.preventDefault(); e.stopPropagation(); window.open(canvas2.toDataURL(),"canvasClipImage","left=0,top=0,width=240,height=180,toolbar=0,resizable=0"); return false; } ; img.addEventListener(MouseAction.MOUSE_OVER, onImageOver, false); output.addEventListener(MouseAction.CLICK, onOutputClick, false); (function(){ context.fillStyle = "#000000"; context.font = "14px sans"; context.baseline = "top"; context.fillText('マウスダウンしている間クリップ可能です。', 310, 220, 280); context.fillText('マウスアップするとクリップはキャンセルされます。', 310, 250, 280); context.fillText('[Export]ボタンを押すとPNG画像を書出します。', 310, 280, 280); }()); }; var image = new Image(); (function (window) { image.addEventListener('load', init, false); image.src = '/images/shuttle_launch.jpg'; }()); |
CanvasからCanvasへの画像複製はdrawImageを使います。
context2.drawImage(canvas, canvas x, canvas y, canvas width, canvas height, canvas2 x, canvas2 y, canvas2 width, canvas2 height)
今回は2倍に拡大して複製しています。
画像の書出しは複製先のCanvasからtoDataURL
しています。
ボタンをCanvasのパーツを使わなかったので処理は随分楽になりましたが、面倒なことには変わりはありません。
またまた、つっこみどころ満載なコードですが、何かありましたらご指摘いただけると嬉しいです。
update 2012-05-17
処理が追いつかないのかマウスアップになってもドラッグを中止できないことがある。
カーソルをmoveに変えたいのにIビームになってる。
ほ〜ら、やっぱりダメだめコードだった。