HTML canvas & Javascript を使った画像の切り抜き「Crop」のサンプルコードです。
ブラウザ上で画像を加工しようとしたら、canvas しかないわけですが、
いちばんやりたいのは「切り抜き:Crop」なんだけど、まぁ、シンプルなコードがほしかったのにみつから無いわけで。
無いときは自分で作るのがエンジニアの掟なわけです。
切り取る(crop) Demo
中央の枠の中を切り出すタイプ。先に切り出す枠を決めておいて、画像の方を確認しながら切り出す。
なんと言いますか、マウスで角から角へドラッグしたり、枠をつまんで移動するUIよりも、このほうが直感的でわかりやすいし、スマホとかタブレットにも向いているUIだと思うのです。
Crop してますか?
実際に触ってみてもらうのが早いでしょう。⬇︎
- canvas 上で、マウスをドラッグすると切り抜く場所を変えられます。
- canvas 上で、マウスのホイールをグリグリすると拡大率が変わります。
- 「CROP」ボタンで、赤枠の部分が切り抜きされます。
- 上部の「A」「B」「C」のボタンで画像が変わります。
See the Pen HTML crop canvas by yamazaki.3104 (@yamazaki3104) on CodePen.
実際のコードがこちら
Qiita を見ているひとたちに、細かい解説は不要だとは思うので、ざっと概要のみ書く
- 主な canvas は編集用の id='cvs' と、ボタン「CROP」を押したあとに編集結果が入る id='out' の2つ
- ボタン「A」「B」「C」は入力画像の変更を行う load_img() を呼ぶだけ。読み込まれた画像は img に入っていて、img は読み出すだけで加工はしない。
- スライダーは id='scal' 画像の拡大縮小率を変えられるように用意したが、最終的にホイールイベントに対応したので、UIとしてはあまり役に立っていないかもしれない
- 画像の切り抜き処理は drawImage() がやってます
- イベントは、マウスとホイールとタッチを取っています(タッチイベントはスマホ&タブレット対応)
- あー、一番のポイントは、外部ライブラリは未使用で、素のJavascriptのみで実装している点
特に難しいことは何もやっていない、平凡なコードだと思っています。マウスのドラッグで画像の位置を変えるコードがいちばん大変だった、、、とはいっても、HTMLも入れて90行程度なので、、、まぁ、半日くらいの作業量でしょうか。
<html>
<body>
<button onclick="load_img('https://lh3.ggpht.com/O0aW5qsyCkR2i7Bu-jUU1b5BWA_NygJ6ui4MgaAvL7gfqvVWqkOBscDaq4pn-vkwByUx=w300')">A</button>
<button onclick="load_img('https://www.mozilla.org/media/img/logos/firefox/logo-quantum-wordmark-white.bd1944395fb6.png')">B</button>
<button onclick="load_img('')">C</button><br>
<input id='scal' type='range' value='' min='10' max='400' oninput="scaling(value)" style='width: 300px;'><br>
<canvas id='cvs' width='300' height='400'></canvas><br>
<button onclick='crop_img()'>CROP</button><br>
<canvas id='out' width='200' height='200'></canvas>
<script>
const cvs = document.getElementById( 'cvs' )
const cw = cvs.width
const ch = cvs.height
const out = document.getElementById( 'out' )
const oh = out.height
const ow = out.width
let ix = 0 // 中心座標
let iy = 0
let v = 1.0 // 拡大縮小率
const img = new Image()
img.onload = function( _ev ){ // 画像が読み込まれた
ix = img.width / 2
iy = img.height / 2
let scl = parseInt( cw / img.width * 100 )
document.getElementById( 'scal' ).value = scl
scaling( scl )
}
function load_img( _url ){ // 画像の読み込み
img.src = ( _url ? _url : 'https://1.bp.blogspot.com/-AoQB8vIvlcw/W8BOcXcEQ6I/AAAAAAABPZM/rXNbol90tXcxBZBlXsg__xix03b_F4nqwCLcBGAs/s800/pet_cat_omoi_sleep_man.png' )
}
load_img()
function scaling( _v ) { // スライダーが変った
v = parseInt( _v ) * 0.01
draw_canvas( ix, iy ) // 画像更新
}
function draw_canvas( _x, _y ){ // 画像更新
const ctx = cvs.getContext( '2d' )
ctx.fillStyle = 'rgb(200, 200, 200)'
ctx.fillRect( 0, 0, cw, ch ) // 背景を塗る
ctx.drawImage( img,
0, 0, img.width, img.height,
(cw/2)-_x*v, (ch/2)-_y*v, img.width*v, img.height*v,
)
ctx.strokeStyle = 'rgba(200, 0, 0, 0.8)'
ctx.strokeRect( (cw-ow)/2, (ch-oh)/2, ow, oh ) // 赤い枠
}
function crop_img(){ // 画像切り取り
const ctx = out.getContext( '2d' )
ctx.fillStyle = 'rgb(200, 200, 200)'
ctx.fillRect( 0, 0, ow, oh ) // 背景を塗る
ctx.drawImage( img,
0, 0, img.width, img.height,
(ow/2)-ix*v, (oh/2)-iy*v, img.width*v, img.height*v,
)
}
let mouse_down = false // canvas ドラッグ中フラグ
let sx = 0 // canvas ドラッグ開始位置
let sy = 0
cvs.ontouchstart =
cvs.onmousedown = function ( _ev ){ // canvas ドラッグ開始位置
mouse_down = true
sx = _ev.pageX
sy = _ev.pageY
return false // イベントを伝搬しない
}
cvs.ontouchend =
cvs.onmouseout =
cvs.onmouseup = function ( _ev ){ // canvas ドラッグ終了位置
if ( mouse_down == false ) return
mouse_down = false
draw_canvas( ix += (sx-_ev.pageX)/v, iy += (sy-_ev.pageY)/v )
return false // イベントを伝搬しない
}
cvs.ontouchmove =
cvs.onmousemove = function ( _ev ){ // canvas ドラッグ中
if ( mouse_down == false ) return
draw_canvas( ix + (sx-_ev.pageX)/v, iy + (sy-_ev.pageY)/v )
return false // イベントを伝搬しない
}
cvs.onmousewheel = function ( _ev ){ // canvas ホイールで拡大縮小
let scl = parseInt( parseInt( document.getElementById( 'scal' ).value ) + _ev.wheelDelta * 0.05 )
if ( scl < 10 ) scl = 10
if ( scl > 400 ) scl = 400
document.getElementById( 'scal' ).value = scl
scaling( scl )
return false // イベントを伝搬しない
}
</script>
</body>
</html>
動作確認環境
いまのところ
- Chrome バージョン: 72.0.3626.109(Official Build) (64 ビット)
- Safari バージョン 12.0.2 (14606.3.4)
- Edge 42.17134.1.0
でしか確認してません。ごめんなさい。
他の環境で確認できたら、追記します。
スマホ&タブレット向けにタッチイベントに対応したつもり
- iOS 版 Google Chrome 72.0.3626.101
- iOS 版 Safari 12.0
で動作確認すみ
2019/03/03: 追記
Firefox 65.0.2 で確認したらホイールのイベントが取れてなかった、、、なぜ??(原因不明の助)