HTML5 Canvas Zoom and Pan Image
by bc_rikko
HTML
<h1>Image Editor</h1>
<form class="container">
<h2>Input</h2>
<input type="text" id="image-url" value="https://ja.wikipedia.org/static/images/project-logos/jawiki.png">
<button type="button" id="load">load</button>
<h2>How to use</h2>
<ol>
<li>画像URLを入力し、loadボタンで画像を読み込む(※ CORSが許可されている画像のみ)</li>
<li>zoom-in, zoom-out, ドラッグ&ドロップで画像を編集する</li>
<li>exportボタンで画像を出力する</li>
</ol>
</form>
<div class="container">
<h2>Editor</h2>
<div class="form">
<button type="button" id="export">export</button>
</div>
<div id="edit-image"></div>
</div>
<div class="container">
<h2>Output</h2>
<div id="output-image"></div>
</div>
CSS
img, canvas {
border: 4px dashed rgb(221, 221, 221);
}
.container {
width: 90%;
margin: 10px;
padding: 10px;
background-color: white;
box-shadow: 0 1px 2px rgba(66, 66, 66, 0.7);
}
#output-image > img {
margin: 5px;
}
JavaScript
class ImageEditor {
/**
* Canvas操作
* @param {Object} opt
* @param {string} opt.imageSrc イメージのURL
* @param {string} opt.canvasId canvasタグのid(デフォルト: image-for-edit)
* @param {Number} opt.canvasSize canvasのサイズ(デフォルト: 128px)
* @param {Number} opt.scaleStep 拡大縮小の倍率(デフォルト: 0.25)
*/
constructor(opt = {}) {
this.src = opt.imageSrc || 'https://ja.wikipedia.org/static/images/project-logos/jawiki.png';
this.id = opt.canvasId || 'image-for-edit';
this.size = opt.canvasSize || 128;
this.scaleStep = opt.scaleStep || 0.25;
this.scale = 1;
this.dragInfo = {
isDragging: false,
startX: 0,
startY: 0,
diffX: 0,
diffY: 0,
canvasX: 0,
canvasY: 0
};
}
/**
* canvasを挿入する
* @param {HTMLElement} el canvasを挿入する親要素
* @return {void}
*/
insertTo(el) {
const container = document.createElement('div');
el.appendChild(container);
// slider
const zoomSlider = document.createElement('input');
zoomSlider.type = 'range';
zoomSlider.min = 0.01;
zoomSlider.max = 2;
zoomSlider.value = 1;
zoomSlider.step = 'any';
zoomSlider.addEventListener('input', this.zoom.bind(this));
container.appendChild(zoomSlider);
// canvas
this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext('2d');
this.canvas.id = this.id;
this.canvas.width = this.canvas.height = this.size;
this.img = new Image();
this.img.crossOrigin = 'anonymous'; // 「Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.」というエラーになるため
this.img.src = this.src;
this.img.onload = () => {
this.ctx.drawImage(this.img, 0, 0);
};
this.img.onerror = e => {
[...el.children].forEach(a => a.remove());
alert('画像読み込み失敗');
};
// mouse event
this.canvas.addEventListener('mousedown', this.dragStart.bind(this));
this.canvas.addEventListener('mousemove',...