本来是按react的语法糖写的,但是突然想用class的方式写组件应该比较有意思
使用案例
import PictureEdit from '@/compontent/comm/PictureEdit';
import png from '@/util/img'; //导入一个图片地址
// 传入父元素id名称,与图片地址,自动在下面生成组件
PictureEditClass = new PictureEdit('ceshipicture', png.videoPoster);
<div id="ceshipicture"></div>
index.ts
/*
* @Author: 刘强
* @Date: 2023-03-14 15:27:32
* @LastEditTime: 2023-03-15 17:52:29
* @LastEditors: 刘强
* @Description: 描述
* @FilePath: \LibrarySRR\src\compontent\comm\PictureEdit\index.ts
* 这是注释!
*/
import * as Iter from './interface';
class PictureEdit {
private imgs: HTMLImageElement; //图片
private canvasCxt: CanvasRenderingContext2D | null;
private imgDrag = false; // 裁剪框是否可以拖拽
private canvasDom: HTMLCanvasElement; //画布
private canvasParam = Iter.canvasParam; //画布属性
private editParam = Iter.editParam; //裁剪工具属性
private imgParam = Iter.imgParam; //图片参数
constructor(id: string, img: string, width = 500, height = 300) {
// canvas 生成
this.canvasDom = document.createElement('canvas');
document.getElementById(id)?.appendChild(this.canvasDom);
// canvas 属性设置-方法绑定
this.canvasParam.width = width;
this.canvasParam.height = height;
this.canvasDom.width = width;
this.canvasDom.height = height;
this.imgParam.width = width;
this.imgParam.height = height;
this.canvasDom.addEventListener('wheel', this.scaleFn.bind(this));
this.canvasDom.addEventListener('mousedown', this.mousePositonFn.bind(this));
document.addEventListener('mouseup', this.cancelDargFn.bind(this));
this.canvasDom.addEventListener('mousemove', this.mouseMoveFn.bind(this));
// 画布属性设定
const domClint = this.canvasDom.getBoundingClientRect();
this.canvasParam.screenX = domClint.x;
this.canvasParam.screenY = domClint.y;
this.canvasCxt = this.canvasDom.getContext('2d');
// 裁剪 属性设定
this.setEditSize(200, 200);
// 图片初始化
this.imgs = new Image();
this.imgs.setAttribute('crossOrigin', 'anonymous');
this.imgs.src = img;
this.imgs.onload = () => {
this.startDrawFn();
};
}
// 绘制
private startDrawFn() {
if (!this.canvasCxt) return;
// 清理画布
this.canvasCxt.clearRect(0, 0, this.canvasParam.width, this.canvasParam.height);
// 绘制底色
this.canvasCxt.rect(0, 0, this.canvasParam.width, this.canvasParam.height);
this.canvasCxt.fillStyle = '#000';
this.canvasCxt.fill();
// 锁定图片最小值
this.imgParam.width = this.imgParam.width < this.editParam.width ? this.editParam.width : this.imgParam.width;
this.canvasCxt.drawImage(
this.imgs,
0,
0,
this.imgs.width,
this.imgs.height,
this.imgParam.x,
this.imgParam.y,
this.imgParam.width,
this.imgParam.height
);
// 绘制裁剪框
this.canvasCxt.strokeStyle = '#ff0000';
this.canvasCxt.strokeRect(this.editParam.x, this.editParam.y, this.editParam.width, this.editParam.height);
this.canvasCxt.save();
}
// 缩放
private scaleFn(e) {
e.preventDefault();
if (!this.canvasCxt) return;
let isDraw = true;
if (e.deltaY < 0 && this.imgParam.scale < this.imgParam.maxScale) {
// 往上滚动
this.imgParam.scale = this.imgParam.scale + this.imgParam.paceScale;
} else if (e.deltaY > 0 && this.imgParam.scale > this.imgParam.minScale) {
// 往下滚动
this.imgParam.scale = this.imgParam.scale - this.imgParam.paceScale;
} else {
isDraw = false;
}
if (!isDraw) return;
// 更新图片数值
this.imgParam.width = this.canvasParam.width * this.imgParam.scale;
this.imgParam.height = this.canvasParam.height * this.imgParam.scale;
// 高度不足自动居中
if (this.imgParam.height < this.editParam.height) {
this.imgParam.y = (this.editParam.height - this.imgParam.height) / 2 + this.editParam.y;
}
// 宽度右边不足自动调整x
const rightNum = this.imgParam.width + this.imgParam.x - this.editParam.offsetX;
if (rightNum < 0) {
this.imgParam.x = this.imgParam.x - rightNum;
}
// 高度(底部)不足自动调整y
const bottomNum = this.imgParam.height + this.imgParam.y - this.editParam.offsetY;
if (bottomNum < 0 && this.imgParam.height > this.editParam.height) {
this.imgParam.y = this.imgParam.y - bottomNum;
}
// 高度(顶部)不足自动调整y
const topmNum = this.imgParam.y - this.editParam.y;
if (topmNum > 0 && this.imgParam.height > this.editParam.height) {
this.imgParam.y = this.imgParam.y - topmNum;
}
this.startDrawFn();
}
// 更新画布/裁剪工具 属性
private updateCanvasParam() {
const domClint = this.canvasDom.getBoundingClientRect();
this.canvasParam.screenX = domClint.x;
this.canvasParam.screenY = domClint.y;
this.imgParam.minX = this.editParam.width + this.editParam.x - this.imgParam.width;
this.imgParam.minY = this.editParam.height + this.editParam.y - this.imgParam.height;
}
// 判断鼠标点击所在位置
private mousePositonFn() {
this.updateCanvasParam();
// const x = e.screenX - this.canvasParam.screenX;
// const y = e.screenY - this.canvasParam.screenY;
// 计算是否在裁剪框中
// const condition1 = this.editParam.x < x && this.editParam.width > x - this.editParam.x;
// const condition2 = this.editParam.y < y && this.editParam.width > y - this.editParam.y;
// this.imgDrag = condition1 && condition2;
this.imgDrag = true;
this.canvasDom.style.cursor = 'move';
}
// 拖拽
private mouseMoveFn(e) {
if (this.imgDrag) this.dragImgFn(e);
}
// 取消拖拽-all
private cancelDargFn() {
this.imgDrag = false;
this.canvasDom.style.cursor = '';
}
// 拖拽图片
private dragImgFn(e) {
let x = this.imgParam.x + e.movementX;
let y = this.imgParam.y + e.movementY;
x = x < this.editParam.x ? x : this.editParam.x;
x = x > this.imgParam.minX ? x : this.imgParam.minX;
if (this.imgParam.height < this.editParam.height) {
y = (this.editParam.height - this.imgParam.height) / 2 + this.editParam.y;
} else {
y = y < this.editParam.y ? y : this.editParam.y;
y = y > this.imgParam.minY ? y : this.imgParam.minY;
}
this.imgParam.x = x;
this.imgParam.y = y;
this.startDrawFn();
}
// ----------------------用户对接方法
// 设定裁剪工具大小
setEditSize(width: number, height: number) {
width = width > this.canvasParam.width ? this.canvasParam.width * 0.75 : width;
width = width < this.editParam.minWidth ? this.editParam.minWidth : width;
height = height > this.canvasParam.height ? this.canvasParam.height * 0.75 : height;
height = height < this.editParam.minHeight ? this.editParam.minHeight : height;
this.editParam.width = width;
this.editParam.height = height;
this.editParam.x = this.canvasParam.width / 2 - this.editParam.width / 2;
this.editParam.y = this.canvasParam.height / 2 - this.editParam.height / 2;
this.editParam.offsetX = this.editParam.x + this.editParam.width;
this.editParam.offsetY = this.editParam.y + this.editParam.height;
}
// 重新选择图片
imgChange(img: string) {
this.imgs = new Image();
this.imgs.setAttribute('crossOrigin', 'anonymous');
this.imgs.src = img;
this.imgs.onload = () => {
this.resetStatus();
this.startDrawFn();
};
}
// 保存图片
saveFn() {
if (!this.canvasCxt) return '';
// 取得截取的图片范围-除掉边框的1px
let y = this.editParam.y + 1;
let height = this.editParam.height;
if (y < this.imgParam.y) {
y = this.imgParam.y + 1;
height = this.imgParam.height;
}
const resultData = this.canvasCxt.getImageData(this.editParam.x + 1, y, this.editParam.width - 2, height - 2);
// 将图片绘制到新的画布并转图片
const newCanvas = document.createElement('canvas');
newCanvas.width = this.editParam.width;
newCanvas.height = this.editParam.height;
const newCtx = newCanvas.getContext('2d');
const ctxY = resultData.height < this.editParam.height ? (this.editParam.height - resultData.height) / 2 : 0;
newCtx?.putImageData(resultData, 0, ctxY, 0, 0, resultData.width, resultData.height);
return newCanvas.toDataURL('image/png');
}
// 重置状态
resetStatus() {
// 裁剪 属性设定
this.imgParam.x = 0;
this.imgParam.y = 0;
this.imgParam.scale = 1;
}
// 释放内存
destoryFn() {
this.canvasDom.removeEventListener('wheel', this.scaleFn.bind(this));
this.canvasDom.removeEventListener('mousedown', this.mousePositonFn.bind(this));
this.canvasDom.removeEventListener('mousemove', this.mouseMoveFn.bind(this));
}
}
export default PictureEdit;
interface.ts
// canvas参数
export const canvasParam = {
width: 0, // 初始化默认500
height: 0, // 初始化默认300
screenX: 0,
screenY: 0
};
// 绘制后图片的参数
export const imgParam = {
width: 0,
height: 0,
x: 0,
y: 0,
minX: 0,
minY: 0,
scale: 1, //缩放倍率
minScale: 0.5, //最小缩放
maxScale: 2, //最大缩放
paceScale: 0.1 //每次缩放量
};
export const editParam = {
width: 0, // 初始化默认200
height: 0, // 初始化默认200
minWidth: 50,
minHeight: 50,
x: 0,
y: 0,
offsetX: 0,
offsetY: 0,
maxX: 0,
maxY: 0
};