| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821 |
- import BaseFoundation, { DefaultAdapter } from "../base/foundation";
- import { getMiddle, getAspectHW } from "./utils";
- export interface CropperAdapter<P = Record<string, any>, S = Record<string, any>> extends DefaultAdapter<P, S> {
- getContainer: () => HTMLElement;
- notifyZoomChange: (zoom: number) => void;
- getImg: () => HTMLImageElement
- }
- interface Point {
- x: number;
- y: number
- }
- export interface ImageData {
- originalWidth: number;
- originalHeight: number;
- scale: number
- }
- export interface ImageDataState {
- width: number;
- height: number;
- centerPoint: Point
- }
- export interface CropperBox {
- width: number;
- height: number;
- centerPoint: Point
- }
- export interface ContainerData {
- width: number;
- height: number
- }
- export interface CropperBoxBorder {
- borderTop: number;
- borderLeft: number
- }
- export default class CropperFoundation <P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<CropperAdapter<P, S>, P, S> {
- imgData: ImageData;
- containerData: ContainerData;
- boxMoveDir: string;
- cropperBoxMoveStart: Point;
- imgMoveStart: Point;
- moveRange: {
- xMax: number;
- xMin: number;
- yMax: number;
- yMin: number
- };
- boxMoveParam: {
- paramX: number;
- paramY: number
- }
- cropperBox: CropperBoxBorder;
- rangeX: [number, number];
- rangeY: [number, number];
- initial: boolean;
-
- constructor(adapter: CropperAdapter<P, S>) {
- super({ ...adapter });
- this.containerData = {} as ContainerData;
- this.imgData = {} as ImageData;
- this.boxMoveDir = '';
- this.boxMoveParam = {
- paramX: 0,
- paramY: 0,
- };
- this.rangeX = null;
- this.rangeY = null;
- this.initial = false;
- }
- init() {
- // 获取容器的宽高
- // get cropping Container 's width & height
- const container = this._adapter.getContainer();
- this.containerData.width = container.clientWidth;
- this.containerData.height = container.clientHeight;
- this.cropperBoxMoveStart = null;
- }
- destroy() {
- this.unBindMoveEvent();
- this.unBindResizeEvent();
- }
- getImgDataWhenResize = (ratio: number) => {
- const { imgData } = this.getStates();
- const newImgData = {
- width: imgData.width * ratio,
- height: imgData.height * ratio,
- centerPoint: {
- x: imgData.centerPoint.x * ratio,
- y: imgData.centerPoint.y * ratio,
- }
- };
- this.imgData.scale *= ratio;
- return newImgData;
- }
- getCropperBoxWhenResize = (ratio: number, newContainerData: ContainerData) => {
- const { cropperBox } = this.getStates();
- const { aspectRatio } = this.getProps();
- const tempCropperBox = {
- width: cropperBox.width * ratio,
- height: cropperBox.height * ratio,
- centerPoint: {
- x: cropperBox.centerPoint.x * ratio,
- y: cropperBox.centerPoint.y * ratio,
- }
- };
- let xMin = tempCropperBox.centerPoint.x - tempCropperBox.width / 2;
- let xMax = tempCropperBox.centerPoint.x + tempCropperBox.width / 2;
- let yMin = tempCropperBox.centerPoint.y - tempCropperBox.height / 2;
- let yMax = tempCropperBox.centerPoint.y + tempCropperBox.height / 2;
- if (aspectRatio) {
- if (xMax > newContainerData.width) {
- xMax = newContainerData.width;
- xMin = tempCropperBox.width > newContainerData.width ?
- 0 : newContainerData.width - tempCropperBox.width;
- tempCropperBox.width = xMax - xMin;
- tempCropperBox.height = tempCropperBox.width / aspectRatio;
- yMax = yMin + tempCropperBox.height;
- }
- if (yMax > newContainerData.height) {
- yMax = newContainerData.height;
- yMin = tempCropperBox.height > newContainerData.height ?
- 0 : newContainerData.height - tempCropperBox.height;
- tempCropperBox.height = yMax - yMin;
- tempCropperBox.width = tempCropperBox.height * aspectRatio;
- xMax = xMin + tempCropperBox.width;
- }
- } else {
- if (xMax > newContainerData.width) {
- xMax = newContainerData.width;
- xMin = tempCropperBox.width > newContainerData.width ?
- 0 : newContainerData.width - tempCropperBox.width;
- }
- if (yMax > newContainerData.height) {
- yMax = newContainerData.height;
- yMin = tempCropperBox.height > newContainerData.height ?
- 0 : newContainerData.height - tempCropperBox.height;
- }
- }
- return {
- width: xMax - xMin,
- height: yMax - yMin,
- centerPoint: {
- x: (xMax + xMin) / 2,
- y: (yMax + yMin) / 2,
- }
- };
- }
- handleResize = () => {
- const { loaded } = this.getStates();
- if (!this.initial) {
- this.initial = true;
- return;
- }
- if (!loaded) {
- return;
- }
- const container = this._adapter.getContainer();
- const newContainerData = {
- width: container.clientWidth,
- height: container.clientHeight,
- };
- const ratio = newContainerData.width / this.containerData.width;
- const newImgData = this.getImgDataWhenResize(ratio);
- const newCropperBox = this.getCropperBoxWhenResize(ratio, newContainerData);
-
- this.containerData = newContainerData;
- this.setState({
- imgData: newImgData,
- cropperBox: newCropperBox,
- } as any);
- }
- handleImageLoad = (e: any) => {
- /**
- * 1. 图片加载完成后,获得图片的原始大小
- * 2. 计算图片的缩放比例,中心点位置
- */
- const { naturalWidth, naturalHeight } = e.target;
- const { width: containerWidth, height: containerHeight } = this.containerData;
- this.imgData.originalWidth = naturalWidth;
- this.imgData.originalHeight = naturalHeight;
- let scale = 1;
- const newImgDataState = {} as ImageDataState;
- /* 计算图片加载后的初始显示尺寸 */
- if (naturalWidth / containerWidth > naturalHeight / containerHeight) {
- scale = containerWidth / naturalWidth;
- newImgDataState.width = containerWidth;
- newImgDataState.height = naturalHeight * scale;
- } else {
- scale = containerHeight / naturalHeight;
- newImgDataState.width = naturalWidth * scale;
- newImgDataState.height = containerHeight;
- }
- this.imgData.scale = scale;
- newImgDataState.centerPoint = {} as Point;
- newImgDataState.centerPoint.x = containerWidth / 2;
- newImgDataState.centerPoint.y = containerHeight / 2;
- /* 计算裁切框大小 */
- const newCropperBoxState = {} as CropperBox;
- const { defaultAspectRatio, aspectRatio } = this.getProps();
- const calcAspect = aspectRatio || defaultAspectRatio;
- if (containerWidth / containerHeight > calcAspect) {
- newCropperBoxState.width = containerHeight * calcAspect;
- newCropperBoxState.height = containerHeight;
- } else {
- newCropperBoxState.width = containerWidth;
- newCropperBoxState.height = containerWidth / calcAspect;
- }
- newCropperBoxState.centerPoint = {} as Point;
- newCropperBoxState.centerPoint.x = containerWidth / 2;
- newCropperBoxState.centerPoint.y = containerHeight / 2;
- this.setState({
- imgData: newImgDataState,
- cropperBox: newCropperBoxState,
- loaded: true,
- } as any);
- }
- handleWheel = (e: any) => {
- // 防止双手缩放导致页面被放大
- e.preventDefault();
- const { imgData, zoom: currZoom } = this.getStates();
- const { maxZoom, minZoom, zoomStep } = this.getProps();
- let _zoom: number;
- if (e.deltaY < 0) {
- /* zoom in */
- if (currZoom + zoomStep <= maxZoom) {
- _zoom = Number((currZoom + zoomStep).toFixed(2));
- }
- } else if (e.deltaY > 0) {
- /* zoom out */
- if (currZoom - zoomStep >= minZoom) {
- _zoom = Number((currZoom - zoomStep).toFixed(2));
- }
- }
- if (_zoom === undefined) {
- return;
- }
- const boundingRect = e.currentTarget.getBoundingClientRect();
- const offsetX = e.clientX - boundingRect.left;
- const offsetY = e.clientY - boundingRect.top;
- const scaleCenter = {
- x: offsetX,
- y: - offsetY,
- };
-
- // 计算新的中心点位置
- const currentPoint = { ...imgData.centerPoint } as Point;
- currentPoint.y = - currentPoint.y;
- const newCenterPoint = {
- x: (currentPoint.x - scaleCenter.x) / currZoom * _zoom + scaleCenter.x,
- y: - [(currentPoint.y - scaleCenter.y) / currZoom * _zoom + scaleCenter.y],
- };
- const newWidth = imgData.width / currZoom * _zoom;
- const newHeight = imgData.height / currZoom * _zoom;
-
- const newImgDataState = {
- width: newWidth,
- height: newHeight,
- centerPoint: newCenterPoint
- };
- this.setState({
- imgData: newImgDataState,
- zoom: _zoom
- } as any);
- this._adapter.notifyZoomChange(_zoom);
- }
- getMoveParamByDir(dir: string) {
- let paramX = 0, paramY = 0;
- switch (dir) {
- case 'tl':
- paramX = -1; paramY = -1; break;
- case 'tm':
- paramY = -1; break;
- case 'tr':
- paramX = 1; paramY = -1; break;
- case 'ml':
- paramX = -1; break;
- case 'mr':
- paramX = 1; break;
- case 'bl':
- paramX = -1; paramY = 1; break;
- case 'bm':
- paramY = 1; break;
- case 'br':
- paramX = 1; paramY = 1; break;
- default:
- break;
- }
- return {
- paramX,
- paramY
- };
- }
- getRangeForAspectChange = () => {
- const { cropperBox } = this.getStates();
- const { aspectRatio } = this.getProps();
- const { width: containerWidth, height: containerHeight } = this.containerData;
- // 可能的最大宽高
- let height: number, width: number;
- // 裁剪框当前的位置
- const xMin = cropperBox.centerPoint.x - cropperBox.width / 2;
- const xMax = cropperBox.centerPoint.x + cropperBox.width / 2;
- const yMin = cropperBox.centerPoint.y - cropperBox.height / 2;
- const yMax = cropperBox.centerPoint.y + cropperBox.height / 2;
- switch (this.boxMoveDir) {
- case 'tl':
- height = yMax;
- width = xMax;
- [width, height] = getAspectHW(width, height, aspectRatio);
- this.rangeX = [xMax - width, xMax];
- this.rangeY = [yMax - height, yMax];
- break;
- case 'tm':
- height = yMax;
- const leftHalfWidth = cropperBox.centerPoint.x;
- const rightHalfWidth = containerWidth - cropperBox.centerPoint.x;
- width = 2 * (leftHalfWidth < rightHalfWidth ? leftHalfWidth : rightHalfWidth);
- [width, height] = getAspectHW(width, height, aspectRatio);
- this.rangeX = [
- cropperBox.centerPoint.x - width / 2,
- cropperBox.centerPoint.x + width / 2
- ];
- this.rangeY = [yMax - height, yMax];
- break;
- case 'tr':
- height = yMax;
- width = containerWidth - xMin;
- [width, height] = getAspectHW(width, height, aspectRatio);
- this.rangeX = [xMin, xMin + width];
- this.rangeY = [yMax - height, yMax];
- break;
- case 'ml':
- width = xMax;
- const topHalfHeight = cropperBox.centerPoint.y;
- const bottomHalfHeight = containerHeight - cropperBox.centerPoint.y;
- height = 2 * (topHalfHeight < bottomHalfHeight ? topHalfHeight : bottomHalfHeight);
- [width, height] = getAspectHW(width, height, aspectRatio);
- this.rangeX = [xMax - width, xMax];
- this.rangeY = [
- cropperBox.centerPoint.y - height / 2,
- cropperBox.centerPoint.y + height / 2
- ];
- break;
- case 'mr':
- width = containerWidth - xMin;
- const topHalfHeight2 = cropperBox.centerPoint.y;
- const bottomHalfHeight2 = containerHeight - cropperBox.centerPoint.y;
- height = 2 * (topHalfHeight2 < bottomHalfHeight2 ? topHalfHeight2 : bottomHalfHeight2);
- [width, height] = getAspectHW(width, height, aspectRatio);
- this.rangeX = [xMin, xMin + width];
- this.rangeY = [
- cropperBox.centerPoint.y - height / 2,
- cropperBox.centerPoint.y + height / 2
- ];
- break;
- case 'bl':
- height = containerHeight - yMin;
- width = xMax;
- [width, height] = getAspectHW(width, height, aspectRatio);
- this.rangeX = [xMax - width, xMax];
- this.rangeY = [yMin, yMin + height];
- break;
- case 'bm':
- height = containerHeight - yMin;
- const leftHalfWidth2 = cropperBox.centerPoint.x;
- const rightHalfWidth2 = containerWidth - cropperBox.centerPoint.x;
- width = 2 * (leftHalfWidth2 < rightHalfWidth2 ? leftHalfWidth2 : rightHalfWidth2);
- [width, height] = getAspectHW(width, height, aspectRatio);
- this.rangeX = [
- cropperBox.centerPoint.x - width / 2,
- cropperBox.centerPoint.x + width / 2,
- ];
- this.rangeY = [yMin, yMin + height];
- break;
- case 'br':
- height = containerHeight - yMin;
- width = containerWidth - xMin;
- [width, height] = getAspectHW(width, height, aspectRatio);
- this.rangeX = [xMin, xMin + width];
- this.rangeY = [yMin, yMin + height];
- break;
- default:
- break;
- }
- }
- handleCornerMouseDown = (e: any) => {
- const currentTarget = e.currentTarget;
- if (!currentTarget) {
- return;
- }
- e.preventDefault();
- const dir = currentTarget.dataset.dir;
- this.boxMoveDir = dir;
- this.boxMoveParam = this.getMoveParamByDir(dir);
- this.bindResizeEvent();
- const { aspectRatio } = this.getProps();
- if (aspectRatio) {
- this.getRangeForAspectChange();
- } else {
- this.rangeX = [0, this.containerData.width];
- this.rangeY = [0, this.containerData.height];
- }
-
- }
- bindResizeEvent = () => {
- const { aspectRatio } = this.getProps();
- document.addEventListener('mousemove', aspectRatio ? this.handleCornerAspectMouseMove : this.handleCornerMouseMove);
- document.addEventListener('mouseup', this.handleCornerMouseUp);
- }
- unBindResizeEvent = () => {
- const { aspectRatio } = this.getProps();
- document.removeEventListener('mousemove', aspectRatio ? this.handleCornerAspectMouseMove : this.handleCornerMouseMove);
- document.removeEventListener('mouseup', this.handleCornerMouseUp);
- }
- viewIMGDragStart = (e: any) => {
- e.preventDefault();
- }
- handleCornerAspectMouseMove = (e: any) => {
- e.preventDefault();
- const { clientX, clientY } = e;
- const { cropperBox } = this.getStates();
- const { aspectRatio } = this.getProps();
- const boundingRect = this._adapter.getContainer().getBoundingClientRect();
- const newCropperBoxPos = {
- width: cropperBox.width,
- height: cropperBox.height,
- centerPoint: { ...cropperBox.centerPoint }
- };
- let offsetX: number, offsetY: number;
- if (['ml', 'mr'].includes(this.boxMoveDir)) {
- offsetX = getMiddle(clientX - boundingRect.left, this.rangeX);
- } else {
- offsetY = getMiddle(clientY - boundingRect.top, this.rangeY);
- }
- switch (this.boxMoveDir) {
- case 'tl':
- newCropperBoxPos.height = this.rangeY[1] - offsetY;
- newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
- newCropperBoxPos.centerPoint = {
- x: this.rangeX[1] - newCropperBoxPos.width / 2,
- y: this.rangeY[1] - newCropperBoxPos.height / 2,
- };
- break;
- case 'tm':
- newCropperBoxPos.height = this.rangeY[1] - offsetY;
- newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
- newCropperBoxPos.centerPoint = {
- x: cropperBox.centerPoint.x,
- y: this.rangeY[1] - newCropperBoxPos.height / 2,
- };
- break;
- case 'tr':
- newCropperBoxPos.height = this.rangeY[1] - offsetY;
- newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
- newCropperBoxPos.centerPoint = {
- x: this.rangeX[0] + newCropperBoxPos.width / 2,
- y: this.rangeY[1] - newCropperBoxPos.height / 2,
- };
- break;
- case 'ml':
- newCropperBoxPos.width = this.rangeX[1] - offsetX;
- newCropperBoxPos.height = newCropperBoxPos.width / aspectRatio;
- newCropperBoxPos.centerPoint = {
- x: this.rangeX[1] - newCropperBoxPos.width / 2,
- y: cropperBox.centerPoint.y,
- };
- break;
- case 'mr':
- newCropperBoxPos.width = offsetX - this.rangeX[0];
- newCropperBoxPos.height = newCropperBoxPos.width / aspectRatio;
- newCropperBoxPos.centerPoint = {
- x: this.rangeX[0] + newCropperBoxPos.width / 2,
- y: cropperBox.centerPoint.y,
- };
- break;
- case 'bl':
- newCropperBoxPos.height = offsetY - this.rangeY[0];
- newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
- newCropperBoxPos.centerPoint = {
- x: this.rangeX[1] - newCropperBoxPos.width / 2,
- y: this.rangeY[0] + newCropperBoxPos.height / 2,
- };
- break;
- case 'bm':
- newCropperBoxPos.height = offsetY - this.rangeY[0];
- newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
- newCropperBoxPos.centerPoint = {
- x: cropperBox.centerPoint.x,
- y: this.rangeY[0] + newCropperBoxPos.height / 2,
- };
- break;
- case 'br':
- newCropperBoxPos.height = offsetY - this.rangeY[0];
- newCropperBoxPos.width = newCropperBoxPos.height * aspectRatio;
- newCropperBoxPos.centerPoint = {
- x: this.rangeX[0] + newCropperBoxPos.width / 2,
- y: this.rangeY[0] + newCropperBoxPos.height / 2,
- };
- break;
- default:
- break;
- }
- if (newCropperBoxPos.height === 0 && newCropperBoxPos.width === 0) {
- this.changeDir();
- this.getRangeForAspectChange();
- }
- this.setState({
- cropperBox: newCropperBoxPos
- } as any);
- }
- changeDir = () => {
- if (this.boxMoveDir.includes('t')) {
- this.boxMoveDir = this.boxMoveDir.replace('t', 'b');
- } else if (this.boxMoveDir.includes('b')) {
- this.boxMoveDir = this.boxMoveDir.replace('b', 't');
- }
- if (this.boxMoveDir.includes('l')) {
- this.boxMoveDir = this.boxMoveDir.replace('l', 'r');
- } else if (this.boxMoveDir.includes('r')) {
- this.boxMoveDir = this.boxMoveDir.replace('r', 'l');
- }
- }
- handleCornerMouseMove = (e: any) => {
- e.preventDefault();
- const { clientX, clientY } = e;
- const { cropperBox } = this.getStates();
- const boundingRect = this._adapter.getContainer().getBoundingClientRect();
- let offsetX = getMiddle(clientX - boundingRect.left, this.rangeX);
- let offsetY = getMiddle(clientY - boundingRect.top, this.rangeY);
- const newCropperBoxPos = {
- width: cropperBox.width,
- height: cropperBox.height,
- centerPoint: {
- x: cropperBox.centerPoint.x,
- y: cropperBox.centerPoint.y
- }
- };
- const { paramX, paramY } = this.boxMoveParam;
- let x: number, y: number;
- if (paramX) {
- x = cropperBox.centerPoint.x + paramX * cropperBox.width / 2;
- newCropperBoxPos.width = cropperBox.width + paramX * (offsetX - x);
- if (newCropperBoxPos.width < 0) {
- newCropperBoxPos.width = - newCropperBoxPos.width;
- this.boxMoveParam.paramX = -paramX;
- }
- newCropperBoxPos.centerPoint.x = offsetX - paramX * newCropperBoxPos.width / 2;
- }
- if (paramY) {
- y = cropperBox.centerPoint.y + paramY * cropperBox.height / 2;
- newCropperBoxPos.height = cropperBox.height + paramY * (offsetY - y);
- if (newCropperBoxPos.height < 0) {
- newCropperBoxPos.height = -newCropperBoxPos.height;
- this.boxMoveParam.paramY = -paramY;
- }
- newCropperBoxPos.centerPoint.y = offsetY - paramY * newCropperBoxPos.height / 2;
- }
-
- this.setState({
- cropperBox: newCropperBoxPos
- } as any);
- }
- handleCornerMouseUp = (e: any) => {
- this.boxMoveParam = { paramX: 0, paramY: 0 };
- this.unBindResizeEvent();
- }
- handleCropperBoxMouseDown = (e: any) => {
- const target = e.target;
- const { cropperBox } = this.getStates();
- const container = this._adapter.getContainer();
- const boundingRect = container.getBoundingClientRect();
- if (target.dataset.dir) {
- // 如果鼠标是落在了corner上,那么不做任何操作
- return;
- }
- // 移动裁切框
- this.cropperBoxMoveStart = {
- x: e.clientX,
- y: e.clientY
- };
- this.bindMoveEvent();
- // 计算 cropperBox 中心点移动范围
- this.moveRange = {
- xMin: cropperBox.width / 2,
- xMax: boundingRect.width - cropperBox.width / 2,
- yMin: cropperBox.height / 2,
- yMax: boundingRect.height - cropperBox.height / 2,
- };
- }
- bindMoveEvent = () => {
- document.addEventListener('mousemove', this.handleCropperBoxMouseMove);
- document.addEventListener('mouseup', this.handleCropperBoxMouseUp);
- }
- unBindMoveEvent = () => {
- document.removeEventListener('mousemove', this.handleCropperBoxMouseMove);
- document.removeEventListener('mouseup', this.handleCropperBoxMouseUp);
- }
- handleCropperBoxMouseMove = (e: any) => {
- if (!this.cropperBoxMoveStart) {
- return;
- }
- const { clientX, clientY } = e;
- const { cropperBox } = this.getStates();
- const offsetX = clientX - this.cropperBoxMoveStart.x;
- const offsetY = clientY - this.cropperBoxMoveStart.y;
- const newCenterPointX = getMiddle(cropperBox.centerPoint.x + offsetX, [this.moveRange.xMin, this.moveRange.xMax]);
- const newCenterPointY = getMiddle(cropperBox.centerPoint.y + offsetY, [this.moveRange.yMin, this.moveRange.yMax]);
- const newCropperBoxPos = {
- width: cropperBox.width,
- height: cropperBox.height,
- centerPoint: {
- x: newCenterPointX,
- y: newCenterPointY
- }
- };
- this.cropperBoxMoveStart = {
- x: clientX,
- y: clientY
- };
- this.setState({
- cropperBox: newCropperBoxPos
- } as any);
- }
- handleCropperBoxMouseUp = (e: any) => {
- if (!this.cropperBoxMoveStart) {
- return;
- }
- this.cropperBoxMoveStart = null;
- this.unBindMoveEvent();
- }
- handleMaskMouseDown = (e: any) => {
- if (e.currentTarget !== e.target) {
- return;
- }
- this.bindImgMoveEvent();
- // 记录开始移动的位置
- this.imgMoveStart = {
- x: e.clientX,
- y: e.clientY
- };
- }
- bindImgMoveEvent = () => {
- document.addEventListener('mousemove', this.handleImgMove);
- document.addEventListener('mouseup', this.handleImgMoveUp);
- }
- unBindImgMoveEvent = () => {
- document.removeEventListener('mousemove', this.handleImgMove);
- document.removeEventListener('mouseup', this.handleImgMoveUp);
- }
- handleImgMove = (e: any) => {
- if (!this.imgMoveStart) {
- return;
- }
- const { clientX, clientY } = e;
- const { imgData } = this.getStates();
- const offsetX = clientX - this.imgMoveStart.x;
- const offsetY = clientY - this.imgMoveStart.y;
- const newCenterPointX = imgData.centerPoint.x + offsetX;
- const newCenterPointY = imgData.centerPoint.y + offsetY;
- const newImgData = {
- width: imgData.width,
- height: imgData.height,
- centerPoint: {
- x: newCenterPointX,
- y: newCenterPointY
- }
- };
- this.imgMoveStart = {
- x: clientX,
- y: clientY
- };
- this.setState({
- imgData: newImgData
- } as any);
- }
- handleImgMoveUp = (e: any) => {
- if (!this.imgMoveStart) {
- return;
- }
- this.imgMoveStart = null;
- this.unBindImgMoveEvent();
- }
- getCropperCanvas = () => {
- const { cropperBox, imgData, rotate, zoom } = this.getStates();
- const { fill } = this.getProps();
- const canvas = document.createElement('canvas');
- const ctx = canvas.getContext('2d');
- const img = this._adapter.getImg();
- // 计算包含旋转后的图片的矩形容器的宽高
- const angle = rotate * Math.PI / 180;
- const sine = Math.abs(Math.sin(angle));
- const cosine = Math.abs(Math.cos(angle));
- const imgWidth = this.imgData.originalWidth;
- const imgHeight = this.imgData.originalHeight;
- const containerWidth = imgWidth * cosine + imgHeight * sine;
- const containerHeight = imgHeight * cosine + imgWidth * sine;
- // 判断裁切区域和外接矩形是否存在交集,如果不存在,则直接返回空白图片
- // 计算需要裁剪的区域实际大小和位置
- const cropperContainerWidth = containerWidth * zoom * this.imgData.scale;
- const cropperContainerHeight = containerHeight * zoom * this.imgData.scale;
- const cropperContainerTop = imgData.centerPoint.y - cropperContainerHeight / 2;
- const cropperContainerLeft = imgData.centerPoint.x - cropperContainerWidth / 2;
- const cropperBoxLeft = cropperBox.centerPoint.x - cropperBox.width / 2;
- const cropperBoxTop = cropperBox.centerPoint.y - cropperBox.height / 2;
- const realZoom = zoom * this.imgData.scale;
-
- const relativeCropLeft = (cropperBoxLeft - cropperContainerLeft) / realZoom;
- const relativeCropTop = (cropperBoxTop - cropperContainerTop) / realZoom;
- const relativeWidth = cropperBox.width / realZoom;
- const relativeHeight = cropperBox.height / realZoom;
- const relativeCropRight = relativeCropLeft + relativeWidth;
- const relativeCropBottom = relativeCropTop + relativeHeight;
- if (relativeCropRight < 0 || relativeCropBottom < 0 || relativeCropLeft > containerWidth || relativeCropTop > containerHeight) {
- // 没有交集,直接返回空白图片
- const emptyCanvas = document.createElement('canvas');
- const ctx = emptyCanvas.getContext('2d');
- emptyCanvas.width = relativeWidth;
- emptyCanvas.height = relativeHeight;
- ctx.fillStyle = fill;
- ctx.fillRect(0, 0, relativeWidth, relativeHeight);
- return emptyCanvas;
- }
- canvas.width = containerWidth;
- canvas.height = containerHeight;
- ctx.fillStyle = fill;
- ctx.fillRect(0, 0, containerWidth, containerHeight);
- const halfWidth = containerWidth / 2;
- const halfHeight = containerHeight / 2;
- ctx.translate(halfWidth, halfHeight);
- ctx.rotate(rotate * Math.PI / 180);
- ctx.translate(-halfWidth, -halfHeight);
- const imgX = (containerWidth - imgWidth) / 2;
- const imgY = (containerHeight - imgHeight) / 2;
- ctx.drawImage(img, 0, 0, imgWidth, imgHeight, imgX, imgY, imgWidth, imgHeight);
- const canvas2 = document.createElement('canvas');
- const ctx2 = canvas2.getContext('2d');
- // 为了避免裁剪时候,超出被裁切的画布的部分颜色不正常,需要将裁切区域限制在画布范围内。
- // 相对位置会在后续进行修正
- let realLeft = relativeCropLeft;
- let realTop = relativeCropTop;
- let realWidth = relativeWidth;
- let realHeight = relativeHeight;
-
- if (relativeCropLeft < 0) {
- realLeft = 0;
- }
- if (relativeCropTop < 0) {
- realTop = 0;
- }
- if (relativeCropRight > containerWidth) {
- realWidth = containerWidth - realLeft;
- } else if (relativeCropLeft < 0) {
- realWidth = relativeCropRight;
- }
- if (relativeCropBottom > containerHeight) {
- realHeight = containerHeight - realTop;
- } else if (relativeCropTop < 0) {
- realHeight = relativeCropBottom;
- }
- const imgDataResult = ctx.getImageData(realLeft, realTop, realWidth, realHeight);
- canvas2.width = relativeWidth;
- canvas2.height = relativeHeight;
- ctx2.fillStyle = fill;
- ctx2.fillRect(0, 0, relativeWidth, relativeHeight);
- ctx2.putImageData(
- imgDataResult,
- relativeCropLeft < 0 ? - relativeCropLeft : 0,
- relativeCropTop < 0 ? - relativeCropTop : 0,
- );
- return canvas2;
- }
- }
|