JavaScript 实现图片框选裁剪
很多网站和 App 在上传头像的时候,如果图片的长宽比不一样,就需要选择区域裁剪为正方形。
在很早以前,基本上只能在前端选择裁剪区域,发到服务器裁剪,现在随着浏览器的功能越来越强大和用户设备性能的提升,基本上可以在前端裁剪完再上传。
这里简单实现图片的框选和裁剪,访问 图片框选裁剪demo 可以在线预览图片框选裁剪效果。
图片框选
下面先实现图片的框选。
HTML:
<!--Mr. Ma's Blog www.misterma.com-->
<img src="image.jpg" alt="图片">
<div id="select-box"></div>
<canvas width="200" height="200"></canvas>
<button type="button" id="crop-btn">裁剪</button>
我这里为了方便就直接使用 img
加载图片了,id
为 select-box
的 div
就是可以拖拽的选择框,canvas
裁剪的时候会用到。
给选择框加一点 CSS:
* {
margin: 0;
padding: 0;
}
#select-box {
width: 200px;
height: 200px;
background: rgba(255, 255, 0, 0.4);
position: absolute;
display: none;
cursor: move;
}
选择框的宽度和高度都是 200px,半透明,不可缩放,默认隐藏,只有图片加载完成后才显示。
下面是选择框拖拽的 JS:
const imgEl = document.querySelector('img'); // 图片
const selectBox = document.querySelector('#select-box'); // 区域选择框
const cropBtn = document.querySelector('#crop-btn'); // 裁剪按钮
const canvasEl = document.querySelector('canvas'); // canvas
// 图片加载完成
imgEl.addEventListener('load', () => {
// 显示区域选择框
selectBox.style.display = 'block';
// 把区域选择框放到 img 上
selectBox.style.top = imgEl.offsetTop + 'px';
selectBox.style.left = imgEl.offsetLeft + 'px';
});
// 区域选择框鼠标按下
selectBox.addEventListener('mousedown', ev => {
const X = ev.clientX - ev.target.offsetLeft;
const Y = ev.clientY - ev.target.offsetTop;
// 鼠标移动
document.onmousemove = ev => {
selectBox.style.left = ev.clientX - X + 'px';
selectBox.style.top = ev.clientY - Y + 'px';
// 限制选择框的拖动范围,禁止拖出图片区域
if (selectBox.offsetLeft <= imgEl.offsetLeft) {
selectBox.style.left = imgEl.offsetLeft + 'px';
}
if (selectBox.offsetLeft >= imgEl.offsetWidth - selectBox.offsetWidth) {
selectBox.style.left = imgEl.offsetWidth - selectBox.offsetWidth + 'px';
}
if (selectBox.offsetTop <= imgEl.offsetTop) {
selectBox.style.top = imgEl.offsetTop + 'px';
}
if (selectBox.offsetTop >= imgEl.offsetHeight - selectBox.offsetHeight) {
selectBox.style.top = imgEl.offsetHeight - selectBox.offsetHeight + 'px';
}
}
// 鼠标放开
document.onmouseup = () => {
document.onmousemove = null;
}
return false;
});
我在 CSS 中已经给区域选择框设置了 absolute
绝对定位,图片加载完成后区域选择框就会被放到图片的左上方。
我这里为了方便清除事件,没有用 addEventListener
给 document
添加鼠标移动和鼠标放开事件,而是直接给 document
绑定事件。
关于元素拖拽的实现方式我这里就不详细的写了,上面的代码很容易看懂。
canvas
和裁剪按钮上面还没有用到,下面写裁剪的时候会用到。
区域选择框拖拽效果如下:
区域选择框是无法拖出图片区域的。
图片裁剪
在上面的代码基础上再增加裁剪功能:
const imgEl = document.querySelector('img'); // 图片
const selectBox = document.querySelector('#select-box'); // 区域选择框
const cropBtn = document.querySelector('#crop-btn'); // 裁剪按钮
const canvasEl = document.querySelector('canvas'); // canvas
let imgFile = null; // 存放裁剪后的图片
// 图片加载完成
imgEl.addEventListener('load', () => {
// 显示区域选择框
selectBox.style.display = 'block';
// 把区域选择框放到 img 上
selectBox.style.top = imgEl.offsetTop + 'px';
selectBox.style.left = imgEl.offsetLeft + 'px';
});
// 区域选择框鼠标按下
selectBox.addEventListener('mousedown', ev => {
const X = ev.clientX - ev.target.offsetLeft;
const Y = ev.clientY - ev.target.offsetTop;
// 鼠标移动
document.onmousemove = ev => {
selectBox.style.left = ev.clientX - X + 'px';
selectBox.style.top = ev.clientY - Y + 'px';
// 限制选择框的拖动范围,禁止拖出图片区域
if (selectBox.offsetLeft <= imgEl.offsetLeft) {
selectBox.style.left = imgEl.offsetLeft + 'px';
}
if (selectBox.offsetLeft >= imgEl.offsetWidth - selectBox.offsetWidth) {
selectBox.style.left = imgEl.offsetWidth - selectBox.offsetWidth + 'px';
}
if (selectBox.offsetTop <= imgEl.offsetTop) {
selectBox.style.top = imgEl.offsetTop + 'px';
}
if (selectBox.offsetTop >= imgEl.offsetHeight - selectBox.offsetHeight) {
selectBox.style.top = imgEl.offsetHeight - selectBox.offsetHeight + 'px';
}
}
// 鼠标放开
document.onmouseup = () => {
document.onmousemove = null;
}
return false;
});
// 图片裁剪按钮点击
cropBtn.addEventListener('click', () => {
const sX = selectBox.offsetLeft - imgEl.offsetLeft; // 区域选择框左侧位置
const sY = selectBox.offsetTop - imgEl.offsetTop; // 区域选择框上方位置
const sW = selectBox.offsetWidth; // 区域选择框宽度
const sH = selectBox.offsetHeight; // 区域选择框高度
// 把图片截取到 canvas
canvasEl.getContext('2d').drawImage(imgEl, sX, sY, sW, sH , 0, 0, canvasEl.width, canvasEl.height);
// 把裁剪后的 canvas 图像转为 Blob
canvasEl.toBlob(blob => {
if (blob === null) return false;
imgFile = blob;
}, 'image/jpeg');
});
裁剪按钮点击后使用 canvas
的 drawImage
从 img
截取图像来绘制图像,drawImage
的参数说明如下:
image
: 截取的图像资源sX
: 截取图像的左侧起始位置sY
: 截取图像的顶部起始位置sW
: 截取图像的宽度sH
: 截取图像的高度dX
:canvas
绘制图像的左侧起始位置dY
:canvas
绘制图像的顶部起始位置dW
:canvas
绘制图像的宽度dH
:canvas
绘制图像的高度
我的 sX
使用的是区域选择框左侧位置减去图片左侧位置,sY
是区域选择框顶部位置减去图片顶部位置,sW
是区域选择框的宽度,sH
是区域选择框高度。
使用 canvas
的 toBlob
可以把图像转为 Blob 数据,下面是参数说明:
callback
: 回调函数,函数会返回一个 Blobtype
: 指定图片格式,可以省略
转换为 Blob 后就可以添加到 FormData
上传了。
注意,如果 canvas
受到污染是不能调用 toBlob
的!
如果 canvas
截取的图像源出现跨域情况,canvas
就会被判定为受污染。比如 img
跨域调用图片,通过 canvas
截取后 canvas
就会受污染,不能调用 toBlob
。
图片框选截取的效果如下:
我这里的区域选择框的大小是固定的,不能缩放。
如果要实现区域选择框缩放,可以让鼠标在区域选择框右侧和下方拖动时,让区域选择框跟随鼠标位置增加或减少宽高。
类似文章:
版权声明:本文为原创文章,版权归 Mr. Ma's Blog 所有,转载请联系博主获得授权。
本文地址:https://www.misterma.com/archives/910/
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。