fix(zhst/meta、zhst/biz、zhst/material): 修改图片标注组件
This commit is contained in:
parent
37c34eb785
commit
8dc81e1743
@ -1,5 +1,16 @@
|
||||
# @zhst/biz
|
||||
|
||||
## 0.22.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- fix: zhst/meta、zhst/biz、zhst/material-修改图片标注组件
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @zhst/meta@0.21.0
|
||||
|
||||
## 0.21.5
|
||||
|
||||
### Patch Changes
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@zhst/biz",
|
||||
"version": "0.21.5",
|
||||
"version": "0.22.0",
|
||||
"description": "业务库",
|
||||
"keywords": [
|
||||
"business",
|
||||
|
@ -1,5 +1,17 @@
|
||||
# @zhst/material
|
||||
|
||||
## 0.18.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- fix: zhst/meta、zhst/biz、zhst/material-修改图片标注组件
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @zhst/meta@0.21.0
|
||||
- @zhst/biz@0.22.0
|
||||
|
||||
## 0.17.4
|
||||
|
||||
### Patch Changes
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@zhst/material",
|
||||
"version": "0.17.4",
|
||||
"version": "0.18.0",
|
||||
"description": "物料库",
|
||||
"keywords": [
|
||||
"business",
|
||||
|
@ -1,5 +1,16 @@
|
||||
# @zhst/utils
|
||||
|
||||
## 0.21.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- fix: zhst/meta、zhst/biz、zhst/material-修改图片标注组件
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @zhst/meta@0.21.0
|
||||
|
||||
## 0.20.3
|
||||
|
||||
### Patch Changes
|
||||
|
@ -176,6 +176,9 @@ export default {
|
||||
h: this.image.height
|
||||
};
|
||||
},
|
||||
getImage: function getImage() {
|
||||
return this;
|
||||
},
|
||||
calcFitScreen: function calcFitScreen() {
|
||||
if (!this.image) return;
|
||||
var w = this.containerData.width;
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@zhst/meta",
|
||||
"version": "0.20.3",
|
||||
"version": "0.21.0",
|
||||
"description": "原子组件",
|
||||
"keywords": [
|
||||
"meta",
|
||||
|
@ -26,14 +26,16 @@ const props = {
|
||||
objects: [
|
||||
{
|
||||
"bboxRatio": {
|
||||
"id": "123",
|
||||
"x": 0.5519352,
|
||||
"y": 0.2965385,
|
||||
"w": 0.05185461,
|
||||
"h": 0.24698898
|
||||
"h": 0.24698898,
|
||||
},
|
||||
},
|
||||
{
|
||||
"bboxRatio": {
|
||||
"id": "456",
|
||||
"x": 0.58543766,
|
||||
"y": 0.3203356,
|
||||
"w": 0.052037954,
|
||||
|
@ -153,6 +153,10 @@ export default {
|
||||
return { w: this.image.width, h: this.image.height };
|
||||
},
|
||||
|
||||
getImage() {
|
||||
return this
|
||||
},
|
||||
|
||||
calcFitScreen() {
|
||||
if (!this.image) return;
|
||||
const w = this.containerData.width;
|
||||
|
@ -1,19 +1,25 @@
|
||||
import React, { useRef, useEffect, forwardRef, useImperativeHandle, useContext, useState, ReactNode, useMemo } from 'react'
|
||||
import React, { useRef, useEffect, forwardRef, useImperativeHandle, useContext, useState, ReactNode } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { fabric } from 'fabric'
|
||||
import { addEventListenerWrapper, getTransforms, pick } from '@zhst/func'
|
||||
import { addEventListenerWrapper, pick } from '@zhst/func'
|
||||
import { useDebounceFn } from '@zhst/hooks'
|
||||
import Viewer from '../ImageEditor/viewer';
|
||||
import './index.less'
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { Cropper, EVENT_CROP_END, EVENT_CROP_START, EVENT_SHAPE_SELECT } from '../ImageEditor';
|
||||
import { EVENT_VIEWER_READY } from '../ImageEditor';
|
||||
import { Rect } from '../ImageEditor/viewer/shape';
|
||||
import { checkPointInRect, drawArrowLine, getImageDataByPosition, percentToLength } from './cropperImagehelper';
|
||||
import Align from 'rc-align';
|
||||
|
||||
interface RectPro extends Rect {
|
||||
imageRect?: string;
|
||||
type?: 'line' | 'rect'; // line:线,rect:矩形
|
||||
}
|
||||
import {
|
||||
checkPointInRect,
|
||||
drawArrowLine,
|
||||
getImageDataByPosition,
|
||||
checkMouseInRect,
|
||||
createFabricShape,
|
||||
originPercentToShapeLength,
|
||||
changeColor,
|
||||
shapeLengthToPercent
|
||||
} from './cropperImagehelper';
|
||||
import { getTransformRect } from '../BigImagePreview/bigImagePreviewHelper';
|
||||
import { IArrowLinePosition, IRectData, ITransform, RectPro } from './type'
|
||||
|
||||
export interface CropperImageProps {
|
||||
prefixCls?: string;
|
||||
@ -30,27 +36,22 @@ export interface CropperImageProps {
|
||||
// 是否展示框选拓展框
|
||||
showToast?: boolean;
|
||||
// 自定义拓展框
|
||||
customToast?: (data?: any) => React.JSX.Element
|
||||
customToast?: (data: any) => React.JSX.Element;
|
||||
toastStyle?: CSSStyleSheet
|
||||
type?: 'line' | 'rect'; // 编辑类型
|
||||
onMouseDown?: (data: { x: number; y: number }) => void;
|
||||
onMouseUp?: (data: {
|
||||
startX: number;
|
||||
startY: number;
|
||||
endX: number;
|
||||
endY: number;
|
||||
imageDom?: HTMLImageElement,
|
||||
targetTransform?: {
|
||||
translateX: number
|
||||
translateY: number
|
||||
scale: number
|
||||
rotate: number
|
||||
}
|
||||
}) => void;
|
||||
onMouseUp?: (e?: fabric.IEvent<MouseEvent>) => void;
|
||||
onMouseMove?: (e?: fabric.IEvent<MouseEvent>) => void;
|
||||
onShapeSelected?: (id: string, shapeData?: RectPro & {
|
||||
originData: Rect
|
||||
}) => void
|
||||
onCropStart?: () => void
|
||||
onCropEnd?: (data: RectPro) => void
|
||||
onCropEnd?: (data: Partial<IRectData> & Partial<IArrowLinePosition> & {
|
||||
type: RectPro['type'],
|
||||
rectImageBase64?: string
|
||||
targetTransform?: ITransform
|
||||
targetData?: Rect
|
||||
}) => void
|
||||
children?: ReactNode
|
||||
}
|
||||
|
||||
@ -75,6 +76,7 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
|
||||
selectedItem,
|
||||
onMouseDown,
|
||||
onMouseUp,
|
||||
onMouseMove,
|
||||
onCropStart,
|
||||
onCropEnd,
|
||||
editAble,
|
||||
@ -82,7 +84,8 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
|
||||
selectAble = true,
|
||||
showToast = false,
|
||||
customToast = () => <div>无</div>,
|
||||
type = 'ract',
|
||||
toastStyle = {},
|
||||
type = 'line',
|
||||
scaleAble = false,
|
||||
lineConfig = {
|
||||
stroke: '#09f',
|
||||
@ -96,202 +99,68 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
|
||||
const [isDrawing, setIsDrawing] = useState(false) // 矩形是否在移动
|
||||
|
||||
const canvasRef = useRef<any>(null);
|
||||
const currentShapeRef = useRef<any>(null)
|
||||
const imageRef = useRef<HTMLDivElement>(null)
|
||||
const viewerRef = useRef<any>(null)
|
||||
const currentFabricRef = useRef<CanvasPro>(null)
|
||||
|
||||
// 自定义弹框
|
||||
const alginContainerRef: any = useRef(null);
|
||||
const alignRef: any = useRef(null);
|
||||
const imageRectRef: any = useRef(null)
|
||||
const currentShapeRef = useRef<fabric.Object>(null)
|
||||
const [isImgReady, setIsImgReady] = useState(false);
|
||||
|
||||
// 初始化 - 图片
|
||||
useEffect(() => {
|
||||
const handleReady = addEventListenerWrapper(imageRef.current, EVENT_VIEWER_READY, () => {
|
||||
setIsImgReady(true)
|
||||
});
|
||||
|
||||
viewerRef.current = new Viewer(imageRef.current!!, {
|
||||
image: url,
|
||||
scaleAble,
|
||||
selectAble,
|
||||
height: 600,
|
||||
fitScaleAsMinScale: true,
|
||||
dragAble: false,
|
||||
});
|
||||
|
||||
// 监听形状选择事件
|
||||
imageRectRef.current = addEventListenerWrapper(imageRef.current, EVENT_SHAPE_SELECT, (e: { detail: any; }) => {
|
||||
// 选中的od
|
||||
const id = e.detail;
|
||||
if (id) {
|
||||
const selectRectData = odList!.filter(_od => _od.id === id)?.[0]
|
||||
const _data = percentToLength(selectRectData, viewerRef.current.canvas)
|
||||
const imageRect = getImageDataByPosition(
|
||||
{ x: _data.x, y: _data.y, w: _data.w, h: _data.h },
|
||||
{ canvas: viewerRef.current.canvas }
|
||||
)
|
||||
id && onShapeSelected?.(id, { ..._data, imageRect, originData: selectRectData })
|
||||
}
|
||||
})
|
||||
|
||||
return () => {
|
||||
// 再次加载,销毁原来的实例
|
||||
viewerRef?.current?.destroy?.();
|
||||
viewerRef.current = null;
|
||||
imageRectRef.current?.remove();
|
||||
viewerRef.current?.clearShape?.();
|
||||
handleReady.remove?.()
|
||||
}
|
||||
}, [url])
|
||||
|
||||
const cropStartRef = useRef<any>(null)
|
||||
const cropEndRef = useRef<any>(null)
|
||||
/**
|
||||
* 监听图形选中事件
|
||||
* @param e
|
||||
*/
|
||||
const handleSelected = (e: fabric.IEvent<MouseEvent>) => {
|
||||
const _viewer = viewerRef?.current || {}
|
||||
const { containerData = {}, targetTransform = {} } = _viewer
|
||||
changeColor(e.selected!, 'rgba(255, 0, 0, 1)')
|
||||
const selectedItem = e.selected?.[0] || 0
|
||||
// @ts-ignore
|
||||
const _originData = selectedItem?.originData || selectedItem
|
||||
const _data = shapeLengthToPercent(_originData, {
|
||||
sourceImageWidth: containerData.width,
|
||||
sourceImageHeight: containerData.height,
|
||||
targetTransform
|
||||
})
|
||||
let rectImageBase64 = ''
|
||||
|
||||
if (_data.w && _data.h) {
|
||||
rectImageBase64 = getImageDataByPosition(
|
||||
_originData,
|
||||
{ canvas: viewerRef.current.canvas }
|
||||
)
|
||||
}
|
||||
onShapeSelected?.(_originData?.id, { ..._data, imageRect: rectImageBase64, originData: _originData })
|
||||
}
|
||||
|
||||
// 初始化 - 编辑器
|
||||
useEffect(() => {
|
||||
const _viewer = viewerRef?.current || {}
|
||||
// 判断是否可编辑 - 非编辑态
|
||||
if (!editAble) {
|
||||
_viewer.clearShape()
|
||||
// 判定是否存在od框
|
||||
odList && odList.forEach(_od => {
|
||||
_viewer?.addShape?.(_od);
|
||||
})
|
||||
return
|
||||
} else {
|
||||
// 编辑模式
|
||||
_viewer?.clearShape?.();
|
||||
|
||||
if (type === 'rect') {
|
||||
// 编辑模式 - 矩形绘制
|
||||
currentShapeRef.current = initRect()
|
||||
// cropEndRef.current = addEventListenerWrapper(imageRef.current, EVENT_CROP_END, (event: { detail: any; }) => {
|
||||
// const data = event.detail;
|
||||
// const targetPosition = { x: data.left, y: data.top, w: data.width, h: data.height }
|
||||
// const imageRect = getImageDataByPosition(targetPosition, { canvas: viewerRef.current.canvas })
|
||||
// const targetData = getTransformRect(_viewer.image, targetTransform, targetPosition)
|
||||
// onCropEnd?.({ ...data , imageRect, targetTransform, targetData })
|
||||
// setIsMove(false)
|
||||
// })
|
||||
} else {
|
||||
// 编辑模式 - 线绘制
|
||||
currentShapeRef.current = initLine()
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
cropStartRef.current?.remove?.()
|
||||
cropEndRef.current?.remove?.()
|
||||
currentShapeRef.current?.destroy?.()
|
||||
currentShapeRef.current?.dispose?.()
|
||||
}
|
||||
},[type, editAble])
|
||||
|
||||
// 初始化 - 矩形圈选工具
|
||||
const initRect = () => {
|
||||
const viewer = viewerRef?.current || {}
|
||||
const { containerData = {}, targetTransform = {} } = viewer
|
||||
let currentFabric: CanvasPro = new fabric.Canvas(
|
||||
canvasRef.current,
|
||||
{
|
||||
backgroundColor: 'transparent',
|
||||
width: containerData.width,
|
||||
height: containerData.height,
|
||||
selection: false,
|
||||
}
|
||||
)
|
||||
let rect: fabric.Rect
|
||||
let origX: number, origY: number
|
||||
|
||||
function addOrReplaceRect(newRect) {
|
||||
// 移除画布上所有的矩形对象
|
||||
const objects = currentFabric.getObjects();
|
||||
for (let i = objects.length - 1; i >= 0; i--) {
|
||||
if (objects[i].type === 'rect') {
|
||||
currentFabric.remove(objects[i]);
|
||||
}
|
||||
}
|
||||
// 添加新的矩形对象
|
||||
currentFabric.add(newRect);
|
||||
}
|
||||
|
||||
const checkPointInRect = (o: fabric.IEvent<MouseEvent>) => {
|
||||
var pointer = currentFabric.getPointer(o.e);
|
||||
var point = new fabric.Point(pointer.x, pointer.y);
|
||||
let inRect = false
|
||||
currentFabric.forEachObject(function(obj) {
|
||||
if (obj.containsPoint(point) || obj._findTargetCorner(pointer)) {
|
||||
inRect = true
|
||||
} else {
|
||||
inRect = false
|
||||
}
|
||||
});
|
||||
return inRect
|
||||
}
|
||||
|
||||
// 鼠标按下事件
|
||||
currentFabric.on('mouse:down', function(o) {
|
||||
currentFabric.startDraw = true
|
||||
var pointer = currentFabric.getPointer(o.e);
|
||||
origX = pointer.x;
|
||||
origY = pointer.y;
|
||||
|
||||
// 创建一个矩形对象
|
||||
rect = new fabric.Rect({
|
||||
left: origX,
|
||||
top: origY,
|
||||
originX: 'left',
|
||||
originY: 'top',
|
||||
borderColor: '#09f',
|
||||
cornerColor: '#09f',
|
||||
cornerSize: 6,
|
||||
width: pointer.x - origX,
|
||||
height: pointer.y - origY,
|
||||
angle: 0,
|
||||
fill: 'transparent',
|
||||
hasControls: true,
|
||||
hasBorders: true,
|
||||
lockRotation: true, // 锁定旋转
|
||||
hasRotatingPoint: false // 隐藏旋转控制点
|
||||
});
|
||||
|
||||
// 判断存在实例,并且鼠标点击在实例上
|
||||
if (checkPointInRect(o) && rect) {
|
||||
} else {
|
||||
addOrReplaceRect(rect)
|
||||
// 监听移动
|
||||
rect.on('moving', o => console.log('o', o))
|
||||
rect.on('resizing', o => console.log('o', o))
|
||||
// 监听缩放
|
||||
rect.on('scaling', o => console.log('o', o))
|
||||
}
|
||||
currentFabric.setActiveObject(currentFabric.item(0));
|
||||
});
|
||||
// 鼠标移动事件
|
||||
currentFabric.on('mouse:move', function(o) {
|
||||
if (!currentFabric.startDraw) return;
|
||||
var pointer = currentFabric.getPointer(o.e);
|
||||
|
||||
if(origX > pointer.x){
|
||||
rect.set({ left: Math.abs(pointer.x) });
|
||||
}
|
||||
|
||||
if(origY > pointer.y){
|
||||
rect.set({ top: Math.abs(pointer.y) });
|
||||
}
|
||||
rect.set({ width: Math.abs(origX - pointer.x) });
|
||||
rect.set({ height: Math.abs(origY - pointer.y) });
|
||||
currentFabric.renderAll();
|
||||
});
|
||||
// 鼠标松开事件
|
||||
currentFabric.on('mouse:up', function(o) {
|
||||
currentFabric.startDraw = false
|
||||
});
|
||||
|
||||
return currentFabric
|
||||
}
|
||||
|
||||
// 初始化线条
|
||||
const initLine = () => {
|
||||
const viewer = viewerRef?.current || {}
|
||||
const { containerData = {}, targetTransform = {} } = viewer
|
||||
const imageSize = viewer.getImgSize()
|
||||
|
||||
const { containerData = {}, targetTransform = {} } = _viewer
|
||||
const _imgSize = _viewer.getImgSize() || {}
|
||||
// @ts-ignore
|
||||
currentFabricRef.current = new fabric.Canvas(
|
||||
canvasRef.current,
|
||||
@ -302,10 +171,206 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
|
||||
selection: false,
|
||||
}
|
||||
)
|
||||
const currentFabric = currentFabricRef.current
|
||||
|
||||
// 事件监听: 鼠标抬起事件
|
||||
// 判断是否可编辑 - 非编辑态
|
||||
if (!editAble) {
|
||||
currentFabricRef.current.clear()
|
||||
let originWidth = (_imgSize.w * targetTransform.scale)
|
||||
let originHeight = (_imgSize.h * targetTransform.scale)
|
||||
odList && odList?.forEach(od => {
|
||||
const _shapeData = originPercentToShapeLength(od, { sourceImageWidth: originWidth, sourceImageHeight: originHeight, targetTransform })
|
||||
let _data = {
|
||||
id: od.id,
|
||||
type: od.type || 'rect',
|
||||
originData: od,
|
||||
..._shapeData
|
||||
}
|
||||
let item = createFabricShape(_data, {
|
||||
})
|
||||
currentFabricRef.current?.add(item)
|
||||
})
|
||||
currentFabricRef.current?.renderAll()
|
||||
// 当矩形被选中时
|
||||
currentFabricRef.current?.on('selection:created', e => handleSelected(e));
|
||||
currentFabricRef.current?.on('selection:updated', function(e) {
|
||||
handleSelected(e)
|
||||
changeColor(e.deselected!, '#FFF566')
|
||||
});
|
||||
// 当取消选中时
|
||||
currentFabricRef.current?.on('selection:cleared', function(e) {
|
||||
changeColor(e.deselected!, '#FFF566')
|
||||
});
|
||||
} else {
|
||||
if (type === 'rect') {
|
||||
// 编辑模式 - 矩形绘制
|
||||
initRect(currentFabricRef.current)
|
||||
} else {
|
||||
// 编辑模式 - 线绘制
|
||||
initLine(currentFabricRef.current)
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
currentFabricRef.current?.removeListeners?.()
|
||||
currentFabricRef.current?.dispose?.()
|
||||
}
|
||||
// TODO: 监听odList 需要优化
|
||||
},[type, editAble, isImgReady])
|
||||
|
||||
// 监听矩形拖动,防抖
|
||||
const { run: handleRectChange } = useDebounceFn(
|
||||
(data: IRectData) => {
|
||||
const _viewer = viewerRef.current
|
||||
const { targetTransform = {} } = _viewer || {}
|
||||
const { left = 0, top = 0, width = 0, height = 0, scaleX = 1, scaleY = 1 } = data || {}
|
||||
const targetPosition = { x: Math.abs(left), y: Math.abs(top), w: Math.abs(width * scaleX), h: Math.abs(height * scaleY) }
|
||||
let rectImageBase64 = ''
|
||||
|
||||
if (width && height) {
|
||||
rectImageBase64 = getImageDataByPosition(data, { canvas: _viewer.canvas })
|
||||
}
|
||||
const targetData = getTransformRect(_viewer.image, targetTransform, targetPosition)
|
||||
onCropEnd?.({ type: 'rect', left, top, width, height, rectImageBase64, targetTransform, targetData })
|
||||
},
|
||||
{
|
||||
wait: 500,
|
||||
},
|
||||
);
|
||||
|
||||
// 初始化 - 矩形圈选工具
|
||||
const initRect = (_fabricCanvas: CanvasPro) => {
|
||||
const viewer = viewerRef?.current || {}
|
||||
const { targetTransform = {} } = viewer
|
||||
const imageSize = viewer.getImgSize() || { x: 0, y: 0 }
|
||||
let currentFabric = _fabricCanvas
|
||||
let rect: fabric.Rect
|
||||
let origX: number, origY: number
|
||||
// 坐标限制
|
||||
let limitStartX = targetTransform.translateX
|
||||
let limitStartY = targetTransform.translateY
|
||||
let limitEndX = limitStartX + (imageSize.w * targetTransform.scale)
|
||||
let limitEndY = limitStartY + (imageSize.h * targetTransform.scale)
|
||||
|
||||
function _getLimitPointer(_pointer: { x: number; y: number; } = { x: 0, y: 0 }) {
|
||||
return {
|
||||
x: Math.min(Math.max(_pointer.x, limitStartX), limitEndX),
|
||||
y: Math.min(Math.max(_pointer.y, limitStartY), limitEndY)
|
||||
};
|
||||
}
|
||||
|
||||
const _handleMove = (e: fabric.IEvent<MouseEvent>) => {
|
||||
// 阻止对象移动到画布外面
|
||||
var obj = e.target!
|
||||
var top = obj.top || 0
|
||||
var left = obj.left || 0
|
||||
var w = obj.width! * (obj?.scaleX || 1)!
|
||||
var h = obj.height! * (obj?.scaleY || 1)!
|
||||
var top_bound = limitStartY;
|
||||
var bottom_bound = limitEndY - h;
|
||||
var left_bound = limitStartX;
|
||||
var right_bound = limitEndX - w;
|
||||
|
||||
if( w > currentFabric.width! ) {
|
||||
obj.set('left', left_bound)
|
||||
} else {
|
||||
obj.set('left', (Math.min(Math.max(left, left_bound), right_bound)))
|
||||
}
|
||||
|
||||
if( h > currentFabric.height! ) {
|
||||
obj.set('top', top_bound)
|
||||
} else {
|
||||
obj.set('top', (Math.min(Math.max(top, top_bound), bottom_bound)))
|
||||
}
|
||||
// @ts-ignore
|
||||
handleRectChange(pick(obj, 'width', 'height', 'left', 'top', 'scaleX', 'scaleY'))
|
||||
}
|
||||
// 鼠标:按下事件
|
||||
currentFabric.on('mouse:down', function(o) {
|
||||
currentFabric.startDraw = true
|
||||
setIsDrawing(true)
|
||||
var pointer = currentFabric.getPointer(o.e);
|
||||
|
||||
if (pointer.x < limitStartX || pointer.x > limitEndX
|
||||
|| pointer.y < limitStartY || pointer.y > limitEndY
|
||||
) return
|
||||
|
||||
origX = pointer.x;
|
||||
origY = pointer.y;
|
||||
// 创建一个矩形对象
|
||||
rect = createFabricShape({
|
||||
left: origX,
|
||||
top: origY,
|
||||
width: pointer.x - origX,
|
||||
height: pointer.y - origY,
|
||||
}, {
|
||||
borderColor: '#09f',
|
||||
stroke: '#09f',
|
||||
cornerColor: '#09f',
|
||||
fill: 'transparent',
|
||||
hasControls: true,
|
||||
hasBorders: true,
|
||||
lockMovementX: false,
|
||||
lockMovementY: false
|
||||
});
|
||||
|
||||
// 判断存在实例,并且鼠标点击在实例上
|
||||
if (checkMouseInRect(o, currentFabric) && rect) {
|
||||
} else {
|
||||
currentFabric.clear()
|
||||
currentFabric.setActiveObject(rect)
|
||||
currentFabric.add(rect);
|
||||
}
|
||||
onCropStart?.()
|
||||
});
|
||||
// 鼠标:移动事件
|
||||
currentFabric.on('mouse:move', function(o) {
|
||||
if (!currentFabric.startDraw || !rect) return;
|
||||
const pointer = currentFabric.getPointer(o.e);
|
||||
const limitPointer = _getLimitPointer(pointer) // 限制绘制的图形大小
|
||||
const width = limitPointer.x - (rect.left || 0)
|
||||
const height = limitPointer.y - (rect.top || 0)
|
||||
|
||||
if(origX > pointer.x) {
|
||||
rect.set({ left: Math.abs(pointer.x) });
|
||||
}
|
||||
|
||||
if(origY > pointer.y){
|
||||
rect.set({ top: Math.abs(pointer.y) });
|
||||
}
|
||||
rect.set({ width });
|
||||
rect.set({ height });
|
||||
currentFabric.renderAll();
|
||||
onMouseMove?.(o)
|
||||
});
|
||||
// 鼠标:松开事件
|
||||
currentFabric.on('mouse:up', function(e) {
|
||||
currentFabric.startDraw = false
|
||||
const currentRef = currentFabric.getActiveObject()
|
||||
if (!currentRef) return
|
||||
setIsDrawing(false)
|
||||
// @ts-ignore
|
||||
currentShapeRef.current = currentRef
|
||||
// @ts-ignore
|
||||
handleRectChange(pick(currentRef, 'width', 'height', 'left', 'top', 'scaleX', 'scaleY'))
|
||||
onMouseUp?.(e)
|
||||
});
|
||||
currentFabric.on('object:moving', (e) => _handleMove(e));
|
||||
currentFabric.on('object:scaling', (e) => _handleMove(e));
|
||||
}
|
||||
|
||||
// 初始化线条
|
||||
const initLine = (_fabricCanvas: CanvasPro) => {
|
||||
if (!currentFabricRef.current) return
|
||||
const viewer = viewerRef?.current || {}
|
||||
const { targetTransform = {} } = viewer
|
||||
const imageSize = viewer.getImgSize()
|
||||
let arrowLine: fabric.Object
|
||||
const currentFabric = _fabricCanvas
|
||||
|
||||
// 事件监听: 鼠标按下
|
||||
currentFabric.on('mouse:down', function(options) {
|
||||
currentFabric.startDraw = true
|
||||
setIsDrawing(true)
|
||||
currentFabric.clear()
|
||||
var evt = options.e;
|
||||
|
||||
// 检查鼠标是否按下左键并且没有按住Ctrl键(Windows系统)
|
||||
@ -328,41 +393,13 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
|
||||
x: pointer.x,
|
||||
y: pointer.y
|
||||
};
|
||||
currentFabric.startDraw = true
|
||||
currentFabric.renderAll();
|
||||
onMouseDown?.(currentFabric.selectionStart)
|
||||
});
|
||||
|
||||
// 事件监听:鼠标松开事件
|
||||
currentFabric.on('mouse:up', async function(_options) {
|
||||
currentFabric.clear()
|
||||
let group = drawArrowLine(
|
||||
currentFabric?.selectionStart?.x as number,
|
||||
currentFabric.selectionStart?.y as number,
|
||||
currentFabric.selectionEnd?.x as number,
|
||||
currentFabric.selectionEnd?.y as number,
|
||||
lineConfig
|
||||
)
|
||||
|
||||
currentFabric.add(group)
|
||||
// 停止绘制
|
||||
currentFabric.startDraw = false
|
||||
currentFabric.renderAll();
|
||||
|
||||
const _shapeData = {
|
||||
startX: currentFabric.selectionStart?.x as number,
|
||||
startY: currentFabric.selectionStart?.y as number,
|
||||
endX: currentFabric.selectionEnd?.x as number,
|
||||
endY: currentFabric.selectionEnd?.y as number
|
||||
}
|
||||
onMouseUp?.({ ..._shapeData, targetTransform })
|
||||
});
|
||||
|
||||
// 事件监听:鼠标移动事件
|
||||
currentFabric.on('mouse:move', function(options) {
|
||||
// 存在起始点,开始绘制
|
||||
if (currentFabric.selectionStart && currentFabric.startDraw) {
|
||||
|
||||
if (!currentFabric.startDraw) return;// 存在起始点,开始绘制
|
||||
// 阻止默认行为
|
||||
options.e.preventDefault();
|
||||
var endPointer = options.pointer!!;
|
||||
@ -373,28 +410,45 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
|
||||
...imageSize
|
||||
}
|
||||
)) return
|
||||
|
||||
// 更新选区大小
|
||||
currentFabric.selectionEnd = {
|
||||
x: endPointer?.x || 0,
|
||||
y: endPointer?.y || 0
|
||||
};
|
||||
|
||||
// 清除之前的选择框
|
||||
currentFabric.clear();
|
||||
let group = drawArrowLine(
|
||||
currentFabric.selectionStart.x,
|
||||
currentFabric.selectionStart.y,
|
||||
endPointer?.x || 0,
|
||||
endPointer?.y || 0,
|
||||
lineConfig
|
||||
)
|
||||
|
||||
currentFabric.add(group)
|
||||
}
|
||||
currentFabric.clear()
|
||||
arrowLine = drawArrowLine({
|
||||
startX: currentFabric?.selectionStart?.x as number,
|
||||
startY: currentFabric.selectionStart?.y as number,
|
||||
endX: currentFabric.selectionEnd?.x as number,
|
||||
endY: currentFabric.selectionEnd?.y as number,
|
||||
}, {
|
||||
fill: '#09f',
|
||||
...lineConfig
|
||||
})
|
||||
currentFabric.add(arrowLine)
|
||||
// @ts-ignore
|
||||
currentShapeRef.current = arrowLine
|
||||
// 停止绘制
|
||||
currentFabric.renderAll();
|
||||
onMouseMove?.(options)
|
||||
});
|
||||
|
||||
// 事件监听:鼠标松开事件
|
||||
currentFabric.on('mouse:up', async function(e) {
|
||||
currentFabric.startDraw = false
|
||||
setIsDrawing(false)
|
||||
const targetTransform = viewerRef.current?.targetTransform || {}
|
||||
// @ts-ignore
|
||||
onCropEnd?.({
|
||||
type: 'line',
|
||||
startX: currentFabric.selectionStart?.x,
|
||||
startY: currentFabric.selectionStart?.y,
|
||||
endX: currentFabric.selectionEnd?.x,
|
||||
endY: currentFabric.selectionEnd?.y,
|
||||
targetTransform
|
||||
})
|
||||
onMouseUp?.(e)
|
||||
});
|
||||
return currentFabric
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
@ -409,6 +463,7 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
|
||||
}
|
||||
}));
|
||||
|
||||
const { left: shapeLeft, width: shapeWidth = 0, top: shapeTop, scaleX: shapeScaleX = 0 } = currentShapeRef.current || {}
|
||||
return (
|
||||
<div className={classNames(`${componentName}`)}>
|
||||
{/* 图片 */}
|
||||
@ -416,49 +471,27 @@ const CropperImage = forwardRef<CropperImageRefProps, CropperImageProps>((props,
|
||||
ref={imageRef}
|
||||
className={classNames(`${componentName}_img`)}
|
||||
/>
|
||||
{showToast && selectedItem && !isDrawing && (<>
|
||||
<div
|
||||
ref={alginContainerRef}
|
||||
// @ts-ignore
|
||||
style={Object.assign(
|
||||
{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: selectedItem.w,
|
||||
height: selectedItem.h,
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'none'
|
||||
},
|
||||
getTransforms({
|
||||
translateX: selectedItem.x,
|
||||
translateY: selectedItem.y,
|
||||
})
|
||||
)}
|
||||
>
|
||||
</div>
|
||||
<Align
|
||||
ref={alignRef}
|
||||
monitorWindowResize
|
||||
align={{
|
||||
points: ['bl', 'br'],
|
||||
offset: [6, 0],
|
||||
overflow: {
|
||||
adjustX: true,
|
||||
adjustY: true,
|
||||
},
|
||||
}}
|
||||
target={function () {
|
||||
return alginContainerRef.current;
|
||||
}}
|
||||
>
|
||||
{customToast?.({
|
||||
selectedItem
|
||||
})}
|
||||
</Align>
|
||||
</>)}
|
||||
<canvas ref={canvasRef} className={classNames(`${componentName}_draw`)}></canvas>
|
||||
{children}
|
||||
<div
|
||||
// @ts-ignore
|
||||
className={classNames(`${componentName}_toast`)}
|
||||
style={{
|
||||
display: (showToast && currentShapeRef.current && !isDrawing) ? 'block' : 'none',
|
||||
left:currentShapeRef.current ? (shapeLeft || 0) + (shapeWidth * shapeScaleX || 0) : 0,
|
||||
top: currentShapeRef.current ? shapeTop : 0,
|
||||
...toastStyle
|
||||
}}
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
{customToast?.({
|
||||
...selectedItem,
|
||||
}) || (
|
||||
<div>
|
||||
测试
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { isString } from "@zhst/func";
|
||||
import { isString, omit } from "@zhst/func";
|
||||
import { fabric } from 'fabric'
|
||||
import { ILineOptions } from "fabric/fabric-impl";
|
||||
import { Rect } from '../ImageEditor/viewer/shape';
|
||||
import { IArrowLinePosition, IRectData, IShape, ITransform, RectPro } from "./type";
|
||||
|
||||
export const getImage = (propImage: HTMLImageElement | string) => {
|
||||
return new Promise<HTMLElement>((resolve, reject) => {
|
||||
@ -21,6 +22,44 @@ export const getImage = (propImage: HTMLImageElement | string) => {
|
||||
});
|
||||
}
|
||||
|
||||
// OD源数据转图形坐标
|
||||
export const originPercentToShapeLength = (odData: Rect & { x2?: number; y2?: number }, opt: {
|
||||
sourceImageWidth: number
|
||||
sourceImageHeight: number
|
||||
targetTransform?: ITransform
|
||||
}) => {
|
||||
const { targetTransform, sourceImageWidth = 0, sourceImageHeight = 0 } = opt || {}
|
||||
const { translateX = 0, translateY = 0 } = targetTransform || {}
|
||||
return {
|
||||
left: odData.x * sourceImageWidth + translateX,
|
||||
top: odData.y * sourceImageHeight + translateY,
|
||||
width: odData.w * sourceImageWidth,
|
||||
height: odData.h * sourceImageHeight,
|
||||
endLeft: (odData.x2 || 0) * sourceImageWidth + translateX,
|
||||
endTop: (odData.y2 || 0) * sourceImageHeight + translateY,
|
||||
}
|
||||
}
|
||||
|
||||
// 图形坐标转OD源数据
|
||||
export const shapeLengthToPercent = (shapeData: IShape & { endLeft?: number, endTop?: number }, opt: {
|
||||
sourceImageWidth: number
|
||||
sourceImageHeight: number
|
||||
targetTransform?: ITransform
|
||||
}) => {
|
||||
const { left = 0, width = 0, top = 0, height = 0, endLeft = 0, endTop = 0 } = shapeData
|
||||
const { targetTransform, sourceImageWidth = 0, sourceImageHeight = 0 } = opt || {}
|
||||
const { translateX = 0, translateY = 0 } = targetTransform || {}
|
||||
|
||||
return {
|
||||
x: (left - translateX) / sourceImageWidth,
|
||||
y: (top - translateY) / sourceImageHeight,
|
||||
w: width / sourceImageWidth,
|
||||
h: height / sourceImageHeight,
|
||||
x2: (endLeft - translateX) / sourceImageWidth,
|
||||
y2: (endTop - translateX) / sourceImageWidth,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查鼠标是否在矩形中、矩形编辑器上
|
||||
* @param o 鼠标对象实例
|
||||
@ -28,8 +67,8 @@ export const getImage = (propImage: HTMLImageElement | string) => {
|
||||
* @returns Boolean
|
||||
*/
|
||||
export const checkMouseInRect = (o: fabric.IEvent<MouseEvent>, _fabricCanvas: fabric.Canvas) => {
|
||||
var pointer = _fabricCanvas.getPointer(o.e);
|
||||
var point = new fabric.Point(pointer.x, pointer.y);
|
||||
let pointer = _fabricCanvas.getPointer(o.e);
|
||||
let point = new fabric.Point(pointer.x, pointer.y);
|
||||
let inRect = false
|
||||
_fabricCanvas.forEachObject(function(obj) {
|
||||
if (obj.containsPoint(point) || obj._findTargetCorner(pointer)) {
|
||||
@ -47,7 +86,7 @@ export const checkMouseInRect = (o: fabric.IEvent<MouseEvent>, _fabricCanvas: fa
|
||||
* @param rect 原始画布宽高:w、h;缩放比例:scale;整体偏移的坐标:translateX,translateY
|
||||
* @returns boolean
|
||||
*/
|
||||
export const checkPointInRect = (point: { x: number; y: number;}, rect: { w: number; h: number, translateX?: number; translateY?: number, scale: number }) => {
|
||||
export const checkPointInRect = (point: Pick<Rect, 'x' | 'y'>, rect: { w: number; h: number, translateX?: number; translateY?: number, scale: number }) => {
|
||||
const { w, h, translateX = 0, translateY = 0, scale = 1 } = rect;
|
||||
const limitStartX = translateX
|
||||
const limitEndX = translateX + (w * scale)
|
||||
@ -62,61 +101,16 @@ export const checkPointInRect = (point: { x: number; y: number;}, rect: { w: num
|
||||
return false
|
||||
}
|
||||
|
||||
// 绘制带箭头的直线函数
|
||||
export const drawArrowLine = (startX: number, startY: number, endX: number, endY: number, lineConfig: ILineOptions) => {
|
||||
|
||||
var angle = Math.atan2(endY - startY, endX - startX);
|
||||
|
||||
var line = new fabric.Line([startX, startY, endX, endY], lineConfig);
|
||||
|
||||
var arrowLength = 20;
|
||||
var arrowWidth = 20;
|
||||
|
||||
var arrow = new fabric.Triangle({
|
||||
left: endX - arrowLength / 2 * Math.cos(angle),
|
||||
top: endY - arrowLength / 2 * Math.sin(angle),
|
||||
width: arrowWidth,
|
||||
height: arrowWidth,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
fill: '#09f',
|
||||
angle: angle * 180 / Math.PI + 90
|
||||
});
|
||||
|
||||
return new fabric.Group([line, arrow], {
|
||||
selectable: false,
|
||||
});
|
||||
}
|
||||
|
||||
// 百分比转长度
|
||||
export const percentToLength = (originData: Rect, canvas: HTMLCanvasElement) => {
|
||||
const { x = 0, y = 0, w = 0, h = 0 } = originData
|
||||
const canvasW = canvas.width
|
||||
const canvasH = canvas.height
|
||||
|
||||
return {
|
||||
x: x * canvasW,
|
||||
y: y * canvasH,
|
||||
w: w * canvasW,
|
||||
h: h * canvasH
|
||||
}
|
||||
}
|
||||
|
||||
// 通过位置截取图片
|
||||
export const getImageDataByPosition = (position: {
|
||||
w: number;
|
||||
h: number;
|
||||
x: number;
|
||||
y: number;
|
||||
}, opt: {
|
||||
export const getImageDataByPosition = (position: IShape, opt: {
|
||||
canvas: HTMLCanvasElement
|
||||
fileType?: string
|
||||
}) => {
|
||||
const { x =0, y = 0, w = 0, h = 0 } = position
|
||||
const { left = 0, top = 0, width = 0, height = 0 } = position
|
||||
const { fileType = 'image/jpg', canvas } = opt
|
||||
const _canvas = canvas
|
||||
const ctx = _canvas.getContext('2d')
|
||||
const imageData = ctx?.getImageData(x, y, w, h)
|
||||
const imageData = ctx?.getImageData(left, top, width, height)
|
||||
const newCanvas = document.createElement('canvas')
|
||||
const newCtx = newCanvas.getContext('2d')
|
||||
newCanvas.width = imageData?.width || 0
|
||||
@ -124,3 +118,136 @@ export const getImageDataByPosition = (position: {
|
||||
newCtx?.putImageData(imageData!!, 0, 0)
|
||||
return newCanvas.toDataURL(fileType)
|
||||
}
|
||||
|
||||
/**
|
||||
* 限制矩形绘制范围
|
||||
* @param e fabric鼠标指针对象
|
||||
* @param opt 配置
|
||||
*/
|
||||
export const limitPointMove = (
|
||||
e: fabric.IEvent<MouseEvent>,
|
||||
opt: {
|
||||
limitRect: IArrowLinePosition,
|
||||
containerSize: Pick<IRectData, 'width' | 'height'>
|
||||
}
|
||||
) => {
|
||||
const { limitRect, containerSize } = opt
|
||||
const { startX, startY, endX, endY } = limitRect
|
||||
// 阻止对象移动到画布外面
|
||||
var obj = e.target!
|
||||
var top = obj.top || 0
|
||||
var left = obj.left || 0
|
||||
var w = obj.width! * obj.scaleX!
|
||||
var h = obj.height! * obj.scaleY!
|
||||
|
||||
var top_bound = startY;
|
||||
var bottom_bound = endY - h;
|
||||
var left_bound = startX;
|
||||
var right_bound = endX - w;
|
||||
|
||||
if( w >= containerSize.width! ) {
|
||||
obj.set('left', left_bound)
|
||||
} else {
|
||||
obj.set('left', (Math.min(Math.max(left, left_bound), right_bound)))
|
||||
}
|
||||
|
||||
if( h >= containerSize.height! ) {
|
||||
obj.set('top', top_bound)
|
||||
} else {
|
||||
obj.set('top', (Math.min(Math.max(top, top_bound), bottom_bound)))
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制带箭头的直线函数
|
||||
export const drawArrowLine = (arrowPosition: IArrowLinePosition, lineConfig: ILineOptions) => {
|
||||
const { startX = 0, startY = 0, endX = 0, endY = 0 } = arrowPosition
|
||||
let angle = Math.atan2(endY - startY, endX - startX);
|
||||
let line = new fabric.Line([startX, startY, endX, endY], {
|
||||
perPixelTargetFind: true, // 启用逐像素检测
|
||||
selectable: true,
|
||||
...lineConfig
|
||||
});
|
||||
let arrowLength = 20;
|
||||
let arrowWidth = 20;
|
||||
|
||||
let arrow = new fabric.Triangle({
|
||||
left: endX - arrowLength / 2 * Math.cos(angle),
|
||||
top: endY - arrowLength / 2 * Math.sin(angle),
|
||||
width: arrowWidth,
|
||||
height: arrowWidth,
|
||||
fill: '#FFF566',
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
angle: angle * 180 / Math.PI + 90,
|
||||
perPixelTargetFind: true, // 启用逐像素检测
|
||||
...lineConfig
|
||||
});
|
||||
|
||||
return new fabric.Group([line, arrow], {
|
||||
selectable: true,
|
||||
evented: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过参数绘制fabric图形
|
||||
* @param data 需要绘制的图形参数
|
||||
* @returns fabric 图形对象
|
||||
*/
|
||||
export const createFabricShape = (data: Partial<RectPro> & IShape & { endLeft?: number; endTop?: number }, config: Partial<fabric.Object>) => {
|
||||
let shape: any
|
||||
let { type = 'rect', left = 0, width = 0, top = 0, height = 0, endLeft = 0, endTop = 0 } = data
|
||||
const defaultConfig = {
|
||||
borderColor: '#FFF566',
|
||||
cornerColor: '#FFF566',
|
||||
cornerSize: 8,
|
||||
stroke: '#FFF566',
|
||||
strokeWidth: 2,
|
||||
hasControls: false,
|
||||
hasBorders: false,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true, // 锁定旋转
|
||||
hasRotatingPoint: false, // 隐藏旋转控制点
|
||||
...config
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'line':
|
||||
shape = drawArrowLine({
|
||||
startX: left,
|
||||
startY: top,
|
||||
endX: endLeft,
|
||||
endY: endTop
|
||||
}, {
|
||||
...defaultConfig
|
||||
})
|
||||
break;
|
||||
case 'rect':
|
||||
shape = new fabric.Rect({
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
height,
|
||||
originX: 'left',
|
||||
originY: 'top',
|
||||
angle: 0,
|
||||
fill: 'transparent',
|
||||
...defaultConfig
|
||||
})
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return shape
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改fabric图形颜色
|
||||
* @param shapes 图形
|
||||
* @param color 颜色
|
||||
*/
|
||||
export const changeColor = (shapes: fabric.Object[], color: string) => {
|
||||
shapes?.forEach(obj => {
|
||||
obj.set('stroke', color)
|
||||
})
|
||||
}
|
||||
|
@ -23,6 +23,16 @@ export default () => {
|
||||
"y": 0.3203356,
|
||||
"w": 0.052037954,
|
||||
"h": 0.2664015
|
||||
},
|
||||
{
|
||||
"id": "46",
|
||||
type: 'line',
|
||||
"x": 0.18543766,
|
||||
"y": 0.1203356,
|
||||
"w": 0.62037954,
|
||||
"h": 0.5864015,
|
||||
"x2": 0.62037954,
|
||||
"y2": 0.5864015
|
||||
}
|
||||
])
|
||||
const [selectedItem, setSelectedItem] = useState<any>()
|
||||
@ -64,23 +74,23 @@ export default () => {
|
||||
url="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
|
||||
onMouseUp={data => console.log('箭头绘制结束:', data)}
|
||||
onShapeSelected={(id, shapeData) => {
|
||||
console.log('矩形选择', id, shapeData)
|
||||
console.log('shapeData', shapeData)
|
||||
setImgUrl(shapeData?.imageRect as string)
|
||||
}}
|
||||
onCropStart={() => console.log('矩形开始绘制')}
|
||||
onCropEnd={(data) => {
|
||||
console.log('data', data)
|
||||
console.log('绘制完成', data)
|
||||
setSelectedItem({ x: data.left, y: data.top, h: data.height, w: data.width })
|
||||
setImgUrl(data?.imageRect as string)
|
||||
setImgUrl(data?.rectImageBase64 as string)
|
||||
}}
|
||||
selectedItem={selectedItem}
|
||||
showToast={editAble}
|
||||
customToast={() => (
|
||||
<div style={{ marginLeft: '12px', color: '#fff', fontSize: '24px' }}>自定义框</div>
|
||||
<div style={{ marginLeft: '12px', color: '#fff', fontSize: '24px' }}><Button>自定义DOM</Button></div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Image src={imgUrl} />
|
||||
{imgUrl && <Image src={imgUrl} />}
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import CropperImage from "./CropperImage";
|
||||
|
||||
export type { CropperImageRefProps, CropperImageProps } from './CropperImage'
|
||||
export * from './cropperImagehelper'
|
||||
|
||||
export default CropperImage
|
||||
|
34
packages/meta/src/cropperImage/type.ts
Normal file
34
packages/meta/src/cropperImage/type.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Rect } from '../ImageEditor/viewer/shape';
|
||||
|
||||
export type IShapeType = 'line' | 'rect'
|
||||
|
||||
export interface IShape {
|
||||
top?: number;
|
||||
left?: number
|
||||
width: number;
|
||||
height: number
|
||||
}
|
||||
|
||||
export interface RectPro extends Rect {
|
||||
imageRect?: string;
|
||||
type?: IShapeType; // line:线,rect:矩形
|
||||
}
|
||||
|
||||
export interface IArrowLinePosition {
|
||||
startX: number
|
||||
startY: number
|
||||
endX: number
|
||||
endY: number
|
||||
}
|
||||
|
||||
export interface IRectData extends IShape {
|
||||
scaleX?: number;
|
||||
scaleY?: number;
|
||||
}
|
||||
|
||||
export interface ITransform {
|
||||
translateX: number
|
||||
translateY: number
|
||||
scale: number
|
||||
rotate: number
|
||||
}
|
@ -27,7 +27,10 @@
|
||||
"lib": ["dom", "es2017"],
|
||||
"stripInternal": true,
|
||||
"resolvePackageJsonExports": true,
|
||||
"resolveJsonModule": true
|
||||
"resolveJsonModule": true,
|
||||
"types": [
|
||||
// "@zhst/meta" 全局使用的工具包,不建议写到 npm 包中去
|
||||
]
|
||||
},
|
||||
"include": [".dumirc.ts", "src/**/*", "packages/**/*"],
|
||||
"exclude": ["node_modules", "lib", "es", ".dumi"]
|
||||
|
Loading…
Reference in New Issue
Block a user