图像裁剪分为两个区域,预览区域、裁剪区域。
第一步:拿到File文件对象,创建Image对象并在此处理预览区域的宽高。
预览区域宽高处理:图片尺寸大小分为三种,宽大于高、高大于宽、宽高相等。宽大于高或者宽高相等处理方式,宽度计算公式:宽除以高乘以宽度基准值,高度固定。高大于宽处理方式,高度计算公式:高除以宽乘以高度基准值,宽度固定。(住:宽基准值184,高基准值:184)
第二步:获取预览区域canvasDOM,通过drawImage方法绘制图像。处理预览区域位置。预览裁剪同步处理,通过getImageData方法将预览区域图像同步到裁剪区域。
第三步:鼠标移动预览区域,预览裁剪同步处理。根据图片大小规格处理移动方式,宽大与高纵向移动,垂直方向固定。高大于宽垂直方向移动,纵向固定。宽高相等禁止移动。通过鼠标移动X,Y方向的值进行预览裁剪同步处理。
第四步:裁剪,获取裁剪区域canvasDOM,通过putImageData方法将获取到的预览区域图片信息绘制到裁剪区域。
第五步:下载,通过toDataURL获取裁剪区域图片信息,通过fetch方法将获取到的图片URL信息转换为blob。
整体代码:
<template>
<div class="cropping">
<div class="cropping-box">
<!-- 预览 -->
<div class="box-l">
<canvas class="pre_canvas"></canvas>
</div>
<!-- 裁剪 -->
<div class="box-r">
<canvas class="cro_canvas"></canvas>
</div>
</div>
</div>
</template>
<script>
import move from './move.js';
export default {
data() {
return {
preCanvasData: {
w: '',
h: '',
maxW: 184,
maxH: 184,
pre_ctx: null
},
croCanvasData: {
w: 184,
h: 184
}
};
},
mounted() {},
methods: {
// 创建Image对象,处理宽高
ready(file = null) {
let that = this;
const { maxH, maxW } = this.preCanvasData;
let img = new Image();
img.src = window.URL.createObjectURL(file);
img.onload = function () {
const W = img.width;
const H = img.height;
that.preCanvasData.w = W;
that.preCanvasData.h = H;
// 宽大于高或者宽高相等处理方式
if ((W > maxW && W > H) || W === H) {
that.preCanvasData.w = (W / H) * maxW; // 宽度计算公式:宽除以高乘以宽度基准值
that.preCanvasData.h = maxH; // 高度固定
}
// 高大于宽处理方式
if (H > maxH && H > W) {
that.preCanvasData.w = maxW;
that.preCanvasData.h = (H / W) * maxH; //高度计算公式:高除以宽乘以高度基准值
}
that.preview(img);
};
},
// 预览
preview(img) {
const { w, h } = this.preCanvasData;
let that = this;
let canvas = document.querySelector('.pre_canvas');
canvas.width = w;
canvas.height = h;
this.preCanvasData.pre_ctx = canvas.getContext('2d');
this.preCanvasData.pre_ctx.clearRect(0, 0, w, h);
this.preCanvasData.pre_ctx.drawImage(img, 0, 0, w, h);
// 图片预览居中
this.leftDefault();
// 鼠标事件
move(
'.pre_canvas',
function (x, y) {
that.cropping(that.preCanvasData.pre_ctx.getImageData(x, y, w, h));
},
this
);
},
// 裁剪
cropping(img) {
const { w, h } = this.croCanvasData;
let canvas = document.querySelector('.cro_canvas');
canvas.width = w;
canvas.height = h;
let cro_ctx = canvas.getContext('2d');
cro_ctx.clearRect(0, 0, w, h);
// 绘制图像
cro_ctx.putImageData(img, 0, 0);
},
// 下载
async upCropping() {
let canvas = document.querySelector('.cro_canvas');
let res = await fetch(canvas.toDataURL('image/png'));
let blob = await res.blob();
this.$emit('upCroppingBlob', blob);
},
// 预览居中处理
leftDefault() {
const { w, h, maxW } = this.preCanvasData;
let canvas = document.querySelector('.pre_canvas');
let x = 0;
let y = 0;
if (w > maxW && w > h) {
x = (w - maxW) / 2;
canvas.style.left = '-' + (w - maxW) / 2 + 'px';
}
if (w === maxW) {
canvas.style.left = (250 - w) / 2 + 'px';
}
// 预览裁剪同步处理
this.cropping(this.preCanvasData.pre_ctx.getImageData(x, y, w, h));
}
}
};
</script>
<style lang="scss" scoped>
.cropping {
.cropping-box {
height: 322px;
display: flex;
justify-content: space-around;
.box-l {
height: 250px;
width: 250px;
position: relative;
overflow: hidden;
border: 1px solid #ccc;
.pre_canvas {
cursor: move;
position: absolute;
top: 0;
left: 0;
}
}
.box-r {
width: 184px;
height: 184px;
border-radius: 100%;
border: 1px solid #ccc;
overflow: hidden;
}
}
}
</style>
const move = (id = "", callback = () => {}, that = null) => {
const { w, h, maxW, maxH } = that.preCanvasData;
let flag = false;
let dom = document.querySelector(id);
dom.onmousedown = function (ev) {
flag = true;
let left = ev.offsetX;
let pl = ev.target.parentNode.offsetLeft;
let top = ev.offsetY;
let pt = ev.target.parentNode.offsetTop;
dom.onmousemove = function (ev) {
// 获取鼠标在目标盒子上的X轴坐标
let l =
ev.clientX -
(document.querySelector(".el-dialog").offsetLeft + pl) -
left;
// 获取鼠标在目标盒子上的Y轴坐标
let t =
ev.clientY -
(document.querySelector(".el-dialog").offsetTop + pt) -
top;
if (flag) {
if (w > h) {
// 纵向滑动边界限制
l = l >= 0 ? 0 : l <= "-" + (w - maxW) ? "-" + (w - maxW) : l;
dom.style.left = l + "px";
// 右侧裁剪滑动同步处理
l = l > 0 ? "-" + l : Math.abs(l);
// 纵向滑动时垂直方向锁定
t = 0;
} else if (h > w) {
// 垂直方向滑动边界限制
t = t >= 0 ? 0 : t <= "-" + (h - maxH) ? "-" + (h - maxH) : t;
dom.style.top = t + "px";
// 右侧裁剪滑动同步处理
t = t > 0 ? "-" + t : Math.abs(t);
// 垂直向滑动时纵向锁定
l = 0;
} else {
// 禁止滑动
l = 0;
t = 0;
}
callback(l, t);
}
};
};
document.onmouseup = function () {
flag = false;
};
};
export default move;
效果图: