canvas图片裁剪

8 篇文章 0 订阅

在这里插入图片描述
本来是按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
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值