antd-img-crop 4.7.0 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +40 -28
- package/README.zh-CN.md +45 -26
- package/dist/antd-img-crop.cjs.js +60 -56
- package/dist/antd-img-crop.d.ts +11 -10
- package/dist/antd-img-crop.esm.js +63 -59
- package/package.json +9 -8
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -37,33 +37,45 @@ const Demo = () => (
|
|
|
37
37
|
|
|
38
38
|
## Props
|
|
39
39
|
|
|
40
|
-
| Prop
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
| onModalOk
|
|
59
|
-
| onModalCancel
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
40
|
+
| Prop | Type | Default | Description |
|
|
41
|
+
| -------------- | -------------------- | -------------- | ----------------------------------------------------------------- |
|
|
42
|
+
| quality | `number` | `0.4` | Cropped image quality, `0` to `1` |
|
|
43
|
+
| fillColor | `string` | `'white'` | Fill color when cropped image smaller than canvas |
|
|
44
|
+
| zoomSlider | `boolean` | `true` | Enable zoom adjustment for image |
|
|
45
|
+
| rotationSlider | `boolean` | `false` | Enable rotation adjustment for image |
|
|
46
|
+
| aspectSlider | `boolean` | `false` | Enable aspect adjustment for crop area |
|
|
47
|
+
| aspect | `number` | `1 / 1` | Aspect of crop area , `width / height` |
|
|
48
|
+
| minZoom | `number` | `1` | Minimum zoom factor |
|
|
49
|
+
| maxZoom | `number` | `3` | Maximum zoom factor |
|
|
50
|
+
| cropShape | `string` | `'rect'` | Shape of crop area, `'rect'` or `'round'` |
|
|
51
|
+
| showGrid | `boolean` | `false` | Show grid of crop area (third-lines) |
|
|
52
|
+
| cropperProps | `object` | - | [react-easy-crop] props (\* existing props cannot be overridden) |
|
|
53
|
+
| modalClassName | `string` | `''` | Provide your own classname for the Modal container |
|
|
54
|
+
| modalTitle | `string` | `'Edit image'` | Title of modal |
|
|
55
|
+
| modalWidth | `number` \| `string` | | Width of modal in pixels number or percentages |
|
|
56
|
+
| modalOk | `string` | | Text of modal confirm button |
|
|
57
|
+
| modalCancel | `string` | | Text of modal cancel button |
|
|
58
|
+
| onModalOk | `function` | - | Call when click modal confirm button |
|
|
59
|
+
| onModalCancel | `function` | - | Call when click modal mask, top right "x", or cancel button |
|
|
60
|
+
| modalProps | `object` | | [Ant Design Modal] props (\* existing props cannot be overridden) |
|
|
61
|
+
| beforeCrop | `function` | - | Call before modal open, if return `false`, it'll not open |
|
|
62
|
+
| onUploadFail | `function` | - | Call when upload failed |
|
|
63
|
+
|
|
64
|
+
### `ConfigProvider` not work?
|
|
65
|
+
|
|
66
|
+
Try to set `libraryDirectory`(`'es'` or `'lib'`) to `babel-plugin-import` config, see which one will work.
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
module.exports = {
|
|
70
|
+
plugins: [
|
|
71
|
+
['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }],
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## No style? (only `antd<=v4`)
|
|
77
|
+
|
|
78
|
+
If you use `antd<=v4` + `babel-plugin-import`, and no `Modal` or `Slider` were imported, please import these styles manually:
|
|
67
79
|
|
|
68
80
|
```js
|
|
69
81
|
import 'antd/es/modal/style';
|
|
@@ -75,7 +87,7 @@ import 'antd/es/slider/style';
|
|
|
75
87
|
[MIT License](https://github.com/nanxiaobei/antd-img-crop/blob/main/LICENSE) (c) [nanxiaobei](https://lee.so/)
|
|
76
88
|
|
|
77
89
|
[react-easy-crop]: https://github.com/ricardo-ch/react-easy-crop#props
|
|
78
|
-
[
|
|
90
|
+
[Ant Design Modal]: https://ant.design/components/modal#api
|
|
79
91
|
|
|
80
92
|
## FUTAKE
|
|
81
93
|
|
package/README.zh-CN.md
CHANGED
|
@@ -37,33 +37,52 @@ const Demo = () => (
|
|
|
37
37
|
|
|
38
38
|
## Props
|
|
39
39
|
|
|
40
|
-
| 属性
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
| onModalOK
|
|
59
|
-
| onModalCancel
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
40
|
+
| 属性 | 类型 | 默认 | 说明 |
|
|
41
|
+
| -------------- | -------------------- | ------------ | ----------------------------------------------------- |
|
|
42
|
+
| quality | `number` | `0.4` | 裁切图片质量,`0` 到 `1` 之间 |
|
|
43
|
+
| fillColor | `string` | `'white'` | 裁切图像小于画布时的填充颜色 |
|
|
44
|
+
| zoomSlider | `boolean` | `true` | 启用图片缩放调整 |
|
|
45
|
+
| rotationSlider | `boolean` | `false` | 启用图片旋转调整 |
|
|
46
|
+
| aspectSlider | `boolean` | `false` | 启用裁切比率调整 |
|
|
47
|
+
| aspect | `number` | `1 / 1` | 裁切区域宽高比,`width / height` |
|
|
48
|
+
| minZoom | `number` | `1` | 最小缩放倍数 |
|
|
49
|
+
| maxZoom | `number` | `3` | 最大缩放倍数 |
|
|
50
|
+
| cropShape | `string` | `'rect'` | 裁切区域形状,`'rect'` 或 `'round'` |
|
|
51
|
+
| showGrid | `boolean` | `false` | 显示裁切区域网格(九宫格) |
|
|
52
|
+
| cropperProps | `object` | - | [react-easy-crop] 的 props(\* 已有 props 无法重写) |
|
|
53
|
+
| modalClassName | `string` | `''` | 为 Modal 容器提供您自己的类名 |
|
|
54
|
+
| modalTitle | `string` | `'编辑图片'` | 弹窗标题 |
|
|
55
|
+
| modalWidth | `number` \| `string` | | 弹窗宽度,`px` 的数值或百分比 |
|
|
56
|
+
| modalOk | `string` | | 弹窗确定按钮文字 |
|
|
57
|
+
| modalCancel | `string` | | 弹窗取消按钮文字 |
|
|
58
|
+
| onModalOK | `function` | - | 点击弹窗确定回调 |
|
|
59
|
+
| onModalCancel | `function` | - | 点击弹窗遮罩层、右上角叉、取消的回调 |
|
|
60
|
+
| modalProps | `object` | | [Ant Design Modal] 的 props(\* 已有 props 无法重写) |
|
|
61
|
+
| beforeCrop | `function` | - | 弹窗打开前调用,若返回 `false`,弹框将不会打开 |
|
|
62
|
+
| onUploadFail | `function` | - | 上传失败时的回调 |
|
|
63
|
+
|
|
64
|
+
### `ConfigProvider` 无效?
|
|
65
|
+
|
|
66
|
+
尝试设置 `libraryDirectory`(`'es'` 或 `'lib'`)到 `babel-plugin-import` 的配置项,看看哪个会生效。
|
|
63
67
|
|
|
64
|
-
|
|
68
|
+
```js
|
|
69
|
+
module.exports = {
|
|
70
|
+
plugins: [
|
|
71
|
+
['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }],
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
```
|
|
65
75
|
|
|
66
|
-
|
|
76
|
+
## 没有样式?(仅 `antd<=v4`)
|
|
77
|
+
|
|
78
|
+
若使用 `antd<=v4` + `babel-plugin-import`,且未引入 `Modal` 或 `Slider`,请手动引入这些样式:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
import 'antd/es/modal/style';
|
|
82
|
+
import 'antd/es/slider/style';
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 在 antd v5 之前
|
|
67
86
|
|
|
68
87
|
```js
|
|
69
88
|
import 'antd/es/modal/style';
|
|
@@ -75,7 +94,7 @@ import 'antd/es/slider/style';
|
|
|
75
94
|
[MIT License](https://github.com/nanxiaobei/antd-img-crop/blob/main/LICENSE) (c) [nanxiaobei](https://lee.so/)
|
|
76
95
|
|
|
77
96
|
[react-easy-crop]: https://github.com/ricardo-ch/react-easy-crop#props
|
|
78
|
-
[
|
|
97
|
+
[Ant Design Modal]: https://ant.design/components/modal-cn#api
|
|
79
98
|
|
|
80
99
|
## FUTAKE
|
|
81
100
|
|
|
@@ -25,53 +25,69 @@ var Cropper = require('react-easy-crop');
|
|
|
25
25
|
var AntSlider = require('antd/lib/slider');
|
|
26
26
|
|
|
27
27
|
const PREFIX = 'img-crop';
|
|
28
|
-
const
|
|
28
|
+
const ZOOM_INITIAL = 1;
|
|
29
29
|
const ZOOM_STEP = 0.1;
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
30
|
+
const ROTATION_INITIAL = 0;
|
|
31
|
+
const ROTATION_MIN = -180;
|
|
32
|
+
const ROTATION_MAX = 180;
|
|
33
|
+
const ROTATION_STEP = 1;
|
|
34
|
+
const ASPECT_MIN = 0.5;
|
|
35
|
+
const ASPECT_MAX = 2;
|
|
36
|
+
const ASPECT_STEP = 0.01;
|
|
34
37
|
|
|
35
38
|
const EasyCrop = react.forwardRef((props, ref) => {
|
|
36
|
-
const { cropperRef,
|
|
39
|
+
const { cropperRef, zoomSlider, rotationSlider, aspectSlider, image, aspect: propAspect, minZoom, maxZoom, cropShape, showGrid, cropperProps, } = props;
|
|
37
40
|
const [crop, onCropChange] = react.useState({ x: 0, y: 0 });
|
|
38
|
-
const [
|
|
39
|
-
const [
|
|
40
|
-
const [
|
|
41
|
+
const [zoom, setZoom] = react.useState(ZOOM_INITIAL);
|
|
42
|
+
const [rotation, setRotation] = react.useState(ROTATION_INITIAL);
|
|
43
|
+
const [aspect, setAspect] = react.useState(propAspect);
|
|
41
44
|
const cropPixelsRef = react.useRef({ width: 0, height: 0, x: 0, y: 0 });
|
|
42
|
-
const onMediaLoaded = react.useCallback((mediaSize) => {
|
|
43
|
-
const { width, height } = mediaSize;
|
|
44
|
-
const ratioWidth = height * aspect;
|
|
45
|
-
if (width > ratioWidth) {
|
|
46
|
-
setCropSize({ width: ratioWidth, height });
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
setCropSize({ width, height: width / aspect });
|
|
50
|
-
}
|
|
51
|
-
}, [aspect]);
|
|
52
45
|
const onCropComplete = react.useCallback((_, croppedAreaPixels) => {
|
|
53
46
|
cropPixelsRef.current = croppedAreaPixels;
|
|
54
47
|
}, []);
|
|
55
48
|
react.useImperativeHandle(ref, () => ({
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
setZoom,
|
|
50
|
+
rotation,
|
|
51
|
+
setRotation,
|
|
59
52
|
cropPixelsRef,
|
|
60
53
|
}));
|
|
61
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Cropper, Object.assign({}, cropperProps, { ref: cropperRef, image: image, crop: crop,
|
|
54
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Cropper, Object.assign({}, cropperProps, { ref: cropperRef, image: image, crop: crop,
|
|
55
|
+
//
|
|
56
|
+
zoom: zoom, rotation: rotation, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, zoomWithScroll: zoomSlider,
|
|
57
|
+
//
|
|
58
|
+
cropShape: cropShape, showGrid: showGrid, onCropChange: onCropChange, onZoomChange: setZoom, onRotationChange: setRotation, onCropComplete: onCropComplete, classes: {
|
|
62
59
|
containerClassName: `${PREFIX}-container`,
|
|
63
60
|
mediaClassName: `${PREFIX}-media`,
|
|
64
|
-
} })),
|
|
61
|
+
} })), zoomSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-zoom` }, { children: [jsxRuntime.jsx("button", Object.assign({ onClick: () => setZoom(zoom - ZOOM_STEP), disabled: zoom - ZOOM_STEP < minZoom }, { children: "\uFF0D" })), jsxRuntime.jsx(AntSlider, { min: minZoom, max: maxZoom, step: ZOOM_STEP, value: zoom, onChange: setZoom }), jsxRuntime.jsx("button", Object.assign({ onClick: () => setZoom(zoom + ZOOM_STEP), disabled: zoom + ZOOM_STEP > maxZoom }, { children: "\uFF0B" }))] }))), rotationSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-rotation` }, { children: [jsxRuntime.jsx("button", Object.assign({ onClick: () => setRotation(rotation - ROTATION_STEP), disabled: rotation === ROTATION_MIN }, { children: "\u21BA" })), jsxRuntime.jsx(AntSlider, { min: ROTATION_MIN, max: ROTATION_MAX, step: ROTATION_STEP, value: rotation, onChange: setRotation }), jsxRuntime.jsx("button", Object.assign({ onClick: () => setRotation(rotation + ROTATION_STEP), disabled: rotation === ROTATION_MAX }, { children: "\u21BB" }))] }))), aspectSlider && (jsxRuntime.jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-aspect` }, { children: [jsxRuntime.jsx("button", Object.assign({ onClick: () => setAspect(aspect - ASPECT_STEP), disabled: aspect - ASPECT_STEP < ASPECT_MIN }, { children: "\u2195\uFE0F" })), jsxRuntime.jsx(AntSlider, { min: ASPECT_MIN, max: ASPECT_MAX, step: ASPECT_STEP, value: aspect, onChange: setAspect }), jsxRuntime.jsx("button", Object.assign({ onClick: () => setAspect(aspect + ASPECT_STEP), disabled: aspect + ASPECT_STEP > ASPECT_MAX }, { children: "\u2194\uFE0F" }))] })))] }));
|
|
65
62
|
});
|
|
66
63
|
var EasyCrop$1 = react.memo(EasyCrop);
|
|
67
64
|
|
|
68
|
-
__$styleInject(".img-crop-modal .img-crop-container {\n position: relative;\n width: 100%;\n height: 40vh;\n}\n.img-crop-modal .img-crop-control {\n display: flex;\n align-items: center;\n width: 60%;\n margin-left: auto;\n margin-right: auto;\n}\n.img-crop-modal .img-crop-control:first-of-type {\n margin-top: 16px;\n}\n.img-crop-modal .img-crop-control:last-of-type {\n margin-bottom: -8px;\n}\n.img-crop-modal .img-crop-control button {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 34px;\n height: 34px;\n padding: 0;\n font-style: normal;\n background: transparent;\n border: 0;\n outline: 0;\n cursor: pointer;\n}\n.img-crop-modal .img-crop-control button[disabled] {\n cursor: default;\n}\n.img-crop-modal .img-crop-control button + div:only-of-type {\n flex: 1;\n margin: 0 8px;\n}\n.img-crop-modal .img-crop-control-zoom button {\n font-size: 18px;\n}\n.img-crop-modal .img-crop-control-
|
|
65
|
+
__$styleInject(".img-crop-modal .img-crop-container {\n position: relative;\n width: 100%;\n height: 40vh;\n}\n.img-crop-modal .img-crop-control {\n display: flex;\n align-items: center;\n width: 60%;\n margin-left: auto;\n margin-right: auto;\n}\n.img-crop-modal .img-crop-control:first-of-type {\n margin-top: 16px;\n}\n.img-crop-modal .img-crop-control:last-of-type {\n margin-bottom: -8px;\n}\n.img-crop-modal .img-crop-control button {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 34px;\n height: 34px;\n padding: 0;\n font-style: normal;\n background: transparent;\n border: 0;\n outline: 0;\n cursor: pointer;\n}\n.img-crop-modal .img-crop-control button[disabled] {\n cursor: default;\n}\n.img-crop-modal .img-crop-control button + div:only-of-type {\n flex: 1;\n margin: 0 8px;\n}\n.img-crop-modal .img-crop-control-zoom button {\n font-size: 18px;\n}\n.img-crop-modal .img-crop-control-rotation button {\n font-size: 16px;\n}\n.img-crop-modal .img-crop-control-rotation button:first-of-type {\n transform: rotate(-20deg);\n}\n.img-crop-modal .img-crop-control-rotation button:last-of-type {\n transform: rotate(20deg);\n}\n");
|
|
69
66
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
const openKey = compareVersions.compareVersions(antd.version, '4.23.0') === -1 ? 'visible' : 'open';
|
|
68
|
+
const deprecate = (obj, old, now) => {
|
|
69
|
+
if (old in obj) {
|
|
70
|
+
console.error(`\`${old}\` is deprecated, please use \`${now}\` instead`);
|
|
71
|
+
return obj[old];
|
|
72
|
+
}
|
|
73
|
+
return obj[now];
|
|
74
|
+
};
|
|
73
75
|
const ImgCrop = react.forwardRef((props, cropperRef) => {
|
|
74
|
-
const {
|
|
76
|
+
const { quality = 0.4, fillColor = 'white',
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
zoomSlider: ZOOM_SLIDER = true,
|
|
79
|
+
// @ts-ignore
|
|
80
|
+
rotationSlider: ROTATION_SLIDER = false, aspectSlider = false, aspect = 1, minZoom = 1, maxZoom = 3,
|
|
81
|
+
// @ts-ignore
|
|
82
|
+
cropShape: CROP_SHAPE = 'rect',
|
|
83
|
+
// @ts-ignore
|
|
84
|
+
showGrid: SHOW_GRID = false, cropperProps, modalClassName, modalTitle, modalWidth, modalOk, modalCancel, onModalOk, onModalCancel, modalProps, beforeCrop, onUploadFail, children, } = props;
|
|
85
|
+
const cropShape = deprecate(props, 'shape', 'cropShape');
|
|
86
|
+
const showGrid = deprecate(props, 'grid', 'showGrid');
|
|
87
|
+
const zoomSlider = deprecate(props, 'zoom', 'zoomSlider');
|
|
88
|
+
const rotationSlider = deprecate(props, 'rotate', 'rotationSlider');
|
|
89
|
+
deprecate(props, 'modalMaskTransitionName', 'modalProps.maskTransitionName');
|
|
90
|
+
deprecate(props, 'modalTransitionName', 'modalProps.transitionName');
|
|
75
91
|
const cb = react.useRef({});
|
|
76
92
|
cb.current.onModalOk = onModalOk;
|
|
77
93
|
cb.current.onModalCancel = onModalCancel;
|
|
@@ -125,32 +141,20 @@ const ImgCrop = react.forwardRef((props, cropperRef) => {
|
|
|
125
141
|
/**
|
|
126
142
|
* modal
|
|
127
143
|
*/
|
|
128
|
-
const
|
|
129
|
-
const obj = {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Object.keys(obj).forEach((prop) => {
|
|
137
|
-
const key = prop;
|
|
138
|
-
if (obj[key] === undefined) {
|
|
139
|
-
delete obj[key];
|
|
140
|
-
}
|
|
141
|
-
});
|
|
144
|
+
const modalBaseProps = react.useMemo(() => {
|
|
145
|
+
const obj = {};
|
|
146
|
+
if (modalWidth !== undefined)
|
|
147
|
+
obj.width = modalWidth;
|
|
148
|
+
if (modalOk !== undefined)
|
|
149
|
+
obj.okText = modalOk;
|
|
150
|
+
if (modalCancel !== undefined)
|
|
151
|
+
obj.cancelText = modalCancel;
|
|
142
152
|
return obj;
|
|
143
|
-
}, [
|
|
144
|
-
modalCancel,
|
|
145
|
-
modalMaskTransitionName,
|
|
146
|
-
modalOk,
|
|
147
|
-
modalTransitionName,
|
|
148
|
-
modalWidth,
|
|
149
|
-
]);
|
|
153
|
+
}, [modalCancel, modalOk, modalWidth]);
|
|
150
154
|
const onClose = () => {
|
|
151
155
|
setImage('');
|
|
152
|
-
easyCropRef.current.
|
|
153
|
-
easyCropRef.current.
|
|
156
|
+
easyCropRef.current.setZoom(ZOOM_INITIAL);
|
|
157
|
+
easyCropRef.current.setRotation(ROTATION_INITIAL);
|
|
154
158
|
};
|
|
155
159
|
const onCancel = react.useCallback(() => {
|
|
156
160
|
var _a, _b;
|
|
@@ -166,9 +170,9 @@ const ImgCrop = react.forwardRef((props, cropperRef) => {
|
|
|
166
170
|
const context = ((_a = target === null || target === void 0 ? void 0 : target.getRootNode) === null || _a === void 0 ? void 0 : _a.call(target)) || document;
|
|
167
171
|
const imgSource = context.querySelector(`.${PREFIX}-media`);
|
|
168
172
|
const { width: cropWidth, height: cropHeight, x: cropX, y: cropY, } = easyCropRef.current.cropPixelsRef.current;
|
|
169
|
-
if (
|
|
173
|
+
if (rotationSlider && easyCropRef.current.rotation !== ROTATION_INITIAL) {
|
|
170
174
|
const { naturalWidth: imgWidth, naturalHeight: imgHeight } = imgSource;
|
|
171
|
-
const angle = easyCropRef.current.
|
|
175
|
+
const angle = easyCropRef.current.rotation * (Math.PI / 180);
|
|
172
176
|
// get container for rotated image
|
|
173
177
|
const sine = Math.abs(Math.sin(angle));
|
|
174
178
|
const cosine = Math.abs(Math.cos(angle));
|
|
@@ -228,7 +232,7 @@ const ImgCrop = react.forwardRef((props, cropperRef) => {
|
|
|
228
232
|
return resolveRef.current(result);
|
|
229
233
|
}
|
|
230
234
|
}), type, quality);
|
|
231
|
-
}), [fillColor, quality,
|
|
235
|
+
}), [fillColor, quality, rotationSlider]);
|
|
232
236
|
const wrapClassName = `${PREFIX}-modal${modalClassName ? ` ${modalClassName}` : ''}`;
|
|
233
237
|
const title = react.useMemo(() => {
|
|
234
238
|
if (modalTitle) {
|
|
@@ -237,7 +241,7 @@ const ImgCrop = react.forwardRef((props, cropperRef) => {
|
|
|
237
241
|
const lang = typeof window !== 'undefined' ? window.navigator.language : '';
|
|
238
242
|
return lang === 'zh-CN' ? '编辑图片' : 'Edit image';
|
|
239
243
|
}, [modalTitle]);
|
|
240
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [uploadComponent, image && (jsxRuntime.jsx(AntModal, Object.assign({},
|
|
244
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [uploadComponent, image && (jsxRuntime.jsx(AntModal, Object.assign({}, modalProps, modalBaseProps, { [openKey]: true }, { title: title, onOk: onOk, onCancel: onCancel, wrapClassName: wrapClassName, maskClosable: false, destroyOnClose: true }, { children: jsxRuntime.jsx(EasyCrop$1, { ref: easyCropRef, cropperRef: cropperRef, zoomSlider: zoomSlider, rotationSlider: rotationSlider, aspectSlider: aspectSlider, image: image, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, cropShape: cropShape, showGrid: showGrid, cropperProps: cropperProps }) })))] }));
|
|
241
245
|
});
|
|
242
246
|
|
|
243
247
|
module.exports = ImgCrop;
|
package/dist/antd-img-crop.d.ts
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import Cropper, { CropperProps } from 'react-easy-crop';
|
|
3
|
-
import {
|
|
3
|
+
import { ModalProps } from 'antd';
|
|
4
|
+
import { RcFile } from 'antd/es/upload/interface';
|
|
4
5
|
|
|
5
6
|
type ImgCropProps = {
|
|
6
|
-
aspect?: number;
|
|
7
|
-
shape?: 'rect' | 'round';
|
|
8
|
-
grid?: boolean;
|
|
9
7
|
quality?: number;
|
|
10
8
|
fillColor?: string;
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
zoomSlider?: boolean;
|
|
10
|
+
rotationSlider?: boolean;
|
|
11
|
+
aspectSlider?: boolean;
|
|
12
|
+
aspect?: number;
|
|
13
13
|
minZoom?: number;
|
|
14
14
|
maxZoom?: number;
|
|
15
|
+
cropShape?: 'rect' | 'round';
|
|
16
|
+
showGrid?: boolean;
|
|
17
|
+
cropperProps?: Omit<CropperProps, 'image' | 'crop' | 'zoom' | 'rotation' | 'aspect' | 'minZoom' | 'maxZoom' | 'zoomWithScroll' | 'cropShape' | 'showGrid' | 'onCropChange' | 'onZoomChange' | 'onRotationChange' | 'onCropComplete' | 'classes'>;
|
|
18
|
+
modalClassName?: string;
|
|
15
19
|
modalTitle?: string;
|
|
16
20
|
modalWidth?: number | string;
|
|
17
21
|
modalOk?: string;
|
|
18
22
|
modalCancel?: string;
|
|
19
|
-
modalMaskTransitionName?: string;
|
|
20
|
-
modalClassName?: string;
|
|
21
|
-
modalTransitionName?: string;
|
|
22
23
|
onModalOk?: (file: void | boolean | string | Blob | File) => void;
|
|
23
24
|
onModalCancel?: () => void;
|
|
25
|
+
modalProps?: Omit<ModalProps, 'className' | 'title' | 'width' | 'okText' | 'cancelText' | 'onOk' | 'onCancel' | 'open' | 'visible' | 'wrapClassName' | 'maskClosable' | 'destroyOnClose'>;
|
|
24
26
|
beforeCrop?: (file: RcFile, fileList: RcFile[]) => boolean | Promise<boolean>;
|
|
25
27
|
onUploadFail?: (err: Error) => void;
|
|
26
|
-
cropperProps?: Partial<CropperProps>;
|
|
27
28
|
children: JSX.Element;
|
|
28
29
|
};
|
|
29
30
|
|
|
@@ -14,60 +14,76 @@ import { __rest, __awaiter } from 'tslib';
|
|
|
14
14
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
15
15
|
import { forwardRef, useState, useRef, useCallback, useImperativeHandle, memo, useMemo } from 'react';
|
|
16
16
|
import { version } from 'antd';
|
|
17
|
-
import AntModal from 'antd/
|
|
18
|
-
import AntUpload from 'antd/
|
|
17
|
+
import AntModal from 'antd/es/modal';
|
|
18
|
+
import AntUpload from 'antd/es/upload';
|
|
19
19
|
import { compareVersions } from 'compare-versions';
|
|
20
20
|
import Cropper from 'react-easy-crop';
|
|
21
|
-
import AntSlider from 'antd/
|
|
21
|
+
import AntSlider from 'antd/es/slider';
|
|
22
22
|
|
|
23
23
|
const PREFIX = 'img-crop';
|
|
24
|
-
const
|
|
24
|
+
const ZOOM_INITIAL = 1;
|
|
25
25
|
const ZOOM_STEP = 0.1;
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
26
|
+
const ROTATION_INITIAL = 0;
|
|
27
|
+
const ROTATION_MIN = -180;
|
|
28
|
+
const ROTATION_MAX = 180;
|
|
29
|
+
const ROTATION_STEP = 1;
|
|
30
|
+
const ASPECT_MIN = 0.5;
|
|
31
|
+
const ASPECT_MAX = 2;
|
|
32
|
+
const ASPECT_STEP = 0.01;
|
|
30
33
|
|
|
31
34
|
const EasyCrop = forwardRef((props, ref) => {
|
|
32
|
-
const { cropperRef,
|
|
35
|
+
const { cropperRef, zoomSlider, rotationSlider, aspectSlider, image, aspect: propAspect, minZoom, maxZoom, cropShape, showGrid, cropperProps, } = props;
|
|
33
36
|
const [crop, onCropChange] = useState({ x: 0, y: 0 });
|
|
34
|
-
const [
|
|
35
|
-
const [
|
|
36
|
-
const [
|
|
37
|
+
const [zoom, setZoom] = useState(ZOOM_INITIAL);
|
|
38
|
+
const [rotation, setRotation] = useState(ROTATION_INITIAL);
|
|
39
|
+
const [aspect, setAspect] = useState(propAspect);
|
|
37
40
|
const cropPixelsRef = useRef({ width: 0, height: 0, x: 0, y: 0 });
|
|
38
|
-
const onMediaLoaded = useCallback((mediaSize) => {
|
|
39
|
-
const { width, height } = mediaSize;
|
|
40
|
-
const ratioWidth = height * aspect;
|
|
41
|
-
if (width > ratioWidth) {
|
|
42
|
-
setCropSize({ width: ratioWidth, height });
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
setCropSize({ width, height: width / aspect });
|
|
46
|
-
}
|
|
47
|
-
}, [aspect]);
|
|
48
41
|
const onCropComplete = useCallback((_, croppedAreaPixels) => {
|
|
49
42
|
cropPixelsRef.current = croppedAreaPixels;
|
|
50
43
|
}, []);
|
|
51
44
|
useImperativeHandle(ref, () => ({
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
setZoom,
|
|
46
|
+
rotation,
|
|
47
|
+
setRotation,
|
|
55
48
|
cropPixelsRef,
|
|
56
49
|
}));
|
|
57
|
-
return (jsxs(Fragment, { children: [jsx(Cropper, Object.assign({}, cropperProps, { ref: cropperRef, image: image, crop: crop,
|
|
50
|
+
return (jsxs(Fragment, { children: [jsx(Cropper, Object.assign({}, cropperProps, { ref: cropperRef, image: image, crop: crop,
|
|
51
|
+
//
|
|
52
|
+
zoom: zoom, rotation: rotation, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, zoomWithScroll: zoomSlider,
|
|
53
|
+
//
|
|
54
|
+
cropShape: cropShape, showGrid: showGrid, onCropChange: onCropChange, onZoomChange: setZoom, onRotationChange: setRotation, onCropComplete: onCropComplete, classes: {
|
|
58
55
|
containerClassName: `${PREFIX}-container`,
|
|
59
56
|
mediaClassName: `${PREFIX}-media`,
|
|
60
|
-
} })),
|
|
57
|
+
} })), zoomSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-zoom` }, { children: [jsx("button", Object.assign({ onClick: () => setZoom(zoom - ZOOM_STEP), disabled: zoom - ZOOM_STEP < minZoom }, { children: "\uFF0D" })), jsx(AntSlider, { min: minZoom, max: maxZoom, step: ZOOM_STEP, value: zoom, onChange: setZoom }), jsx("button", Object.assign({ onClick: () => setZoom(zoom + ZOOM_STEP), disabled: zoom + ZOOM_STEP > maxZoom }, { children: "\uFF0B" }))] }))), rotationSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-rotation` }, { children: [jsx("button", Object.assign({ onClick: () => setRotation(rotation - ROTATION_STEP), disabled: rotation === ROTATION_MIN }, { children: "\u21BA" })), jsx(AntSlider, { min: ROTATION_MIN, max: ROTATION_MAX, step: ROTATION_STEP, value: rotation, onChange: setRotation }), jsx("button", Object.assign({ onClick: () => setRotation(rotation + ROTATION_STEP), disabled: rotation === ROTATION_MAX }, { children: "\u21BB" }))] }))), aspectSlider && (jsxs("section", Object.assign({ className: `${PREFIX}-control ${PREFIX}-control-aspect` }, { children: [jsx("button", Object.assign({ onClick: () => setAspect(aspect - ASPECT_STEP), disabled: aspect - ASPECT_STEP < ASPECT_MIN }, { children: "\u2195\uFE0F" })), jsx(AntSlider, { min: ASPECT_MIN, max: ASPECT_MAX, step: ASPECT_STEP, value: aspect, onChange: setAspect }), jsx("button", Object.assign({ onClick: () => setAspect(aspect + ASPECT_STEP), disabled: aspect + ASPECT_STEP > ASPECT_MAX }, { children: "\u2194\uFE0F" }))] })))] }));
|
|
61
58
|
});
|
|
62
59
|
var EasyCrop$1 = memo(EasyCrop);
|
|
63
60
|
|
|
64
|
-
__$styleInject(".img-crop-modal .img-crop-container {\n position: relative;\n width: 100%;\n height: 40vh;\n}\n.img-crop-modal .img-crop-control {\n display: flex;\n align-items: center;\n width: 60%;\n margin-left: auto;\n margin-right: auto;\n}\n.img-crop-modal .img-crop-control:first-of-type {\n margin-top: 16px;\n}\n.img-crop-modal .img-crop-control:last-of-type {\n margin-bottom: -8px;\n}\n.img-crop-modal .img-crop-control button {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 34px;\n height: 34px;\n padding: 0;\n font-style: normal;\n background: transparent;\n border: 0;\n outline: 0;\n cursor: pointer;\n}\n.img-crop-modal .img-crop-control button[disabled] {\n cursor: default;\n}\n.img-crop-modal .img-crop-control button + div:only-of-type {\n flex: 1;\n margin: 0 8px;\n}\n.img-crop-modal .img-crop-control-zoom button {\n font-size: 18px;\n}\n.img-crop-modal .img-crop-control-
|
|
61
|
+
__$styleInject(".img-crop-modal .img-crop-container {\n position: relative;\n width: 100%;\n height: 40vh;\n}\n.img-crop-modal .img-crop-control {\n display: flex;\n align-items: center;\n width: 60%;\n margin-left: auto;\n margin-right: auto;\n}\n.img-crop-modal .img-crop-control:first-of-type {\n margin-top: 16px;\n}\n.img-crop-modal .img-crop-control:last-of-type {\n margin-bottom: -8px;\n}\n.img-crop-modal .img-crop-control button {\n display: flex;\n justify-content: center;\n align-items: center;\n width: 34px;\n height: 34px;\n padding: 0;\n font-style: normal;\n background: transparent;\n border: 0;\n outline: 0;\n cursor: pointer;\n}\n.img-crop-modal .img-crop-control button[disabled] {\n cursor: default;\n}\n.img-crop-modal .img-crop-control button + div:only-of-type {\n flex: 1;\n margin: 0 8px;\n}\n.img-crop-modal .img-crop-control-zoom button {\n font-size: 18px;\n}\n.img-crop-modal .img-crop-control-rotation button {\n font-size: 16px;\n}\n.img-crop-modal .img-crop-control-rotation button:first-of-type {\n transform: rotate(-20deg);\n}\n.img-crop-modal .img-crop-control-rotation button:last-of-type {\n transform: rotate(20deg);\n}\n");
|
|
65
62
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
const openKey = compareVersions(version, '4.23.0') === -1 ? 'visible' : 'open';
|
|
64
|
+
const deprecate = (obj, old, now) => {
|
|
65
|
+
if (old in obj) {
|
|
66
|
+
console.error(`\`${old}\` is deprecated, please use \`${now}\` instead`);
|
|
67
|
+
return obj[old];
|
|
68
|
+
}
|
|
69
|
+
return obj[now];
|
|
70
|
+
};
|
|
69
71
|
const ImgCrop = forwardRef((props, cropperRef) => {
|
|
70
|
-
const {
|
|
72
|
+
const { quality = 0.4, fillColor = 'white',
|
|
73
|
+
// @ts-ignore
|
|
74
|
+
zoomSlider: ZOOM_SLIDER = true,
|
|
75
|
+
// @ts-ignore
|
|
76
|
+
rotationSlider: ROTATION_SLIDER = false, aspectSlider = false, aspect = 1, minZoom = 1, maxZoom = 3,
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
cropShape: CROP_SHAPE = 'rect',
|
|
79
|
+
// @ts-ignore
|
|
80
|
+
showGrid: SHOW_GRID = false, cropperProps, modalClassName, modalTitle, modalWidth, modalOk, modalCancel, onModalOk, onModalCancel, modalProps, beforeCrop, onUploadFail, children, } = props;
|
|
81
|
+
const cropShape = deprecate(props, 'shape', 'cropShape');
|
|
82
|
+
const showGrid = deprecate(props, 'grid', 'showGrid');
|
|
83
|
+
const zoomSlider = deprecate(props, 'zoom', 'zoomSlider');
|
|
84
|
+
const rotationSlider = deprecate(props, 'rotate', 'rotationSlider');
|
|
85
|
+
deprecate(props, 'modalMaskTransitionName', 'modalProps.maskTransitionName');
|
|
86
|
+
deprecate(props, 'modalTransitionName', 'modalProps.transitionName');
|
|
71
87
|
const cb = useRef({});
|
|
72
88
|
cb.current.onModalOk = onModalOk;
|
|
73
89
|
cb.current.onModalCancel = onModalCancel;
|
|
@@ -121,32 +137,20 @@ const ImgCrop = forwardRef((props, cropperRef) => {
|
|
|
121
137
|
/**
|
|
122
138
|
* modal
|
|
123
139
|
*/
|
|
124
|
-
const
|
|
125
|
-
const obj = {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
Object.keys(obj).forEach((prop) => {
|
|
133
|
-
const key = prop;
|
|
134
|
-
if (obj[key] === undefined) {
|
|
135
|
-
delete obj[key];
|
|
136
|
-
}
|
|
137
|
-
});
|
|
140
|
+
const modalBaseProps = useMemo(() => {
|
|
141
|
+
const obj = {};
|
|
142
|
+
if (modalWidth !== undefined)
|
|
143
|
+
obj.width = modalWidth;
|
|
144
|
+
if (modalOk !== undefined)
|
|
145
|
+
obj.okText = modalOk;
|
|
146
|
+
if (modalCancel !== undefined)
|
|
147
|
+
obj.cancelText = modalCancel;
|
|
138
148
|
return obj;
|
|
139
|
-
}, [
|
|
140
|
-
modalCancel,
|
|
141
|
-
modalMaskTransitionName,
|
|
142
|
-
modalOk,
|
|
143
|
-
modalTransitionName,
|
|
144
|
-
modalWidth,
|
|
145
|
-
]);
|
|
149
|
+
}, [modalCancel, modalOk, modalWidth]);
|
|
146
150
|
const onClose = () => {
|
|
147
151
|
setImage('');
|
|
148
|
-
easyCropRef.current.
|
|
149
|
-
easyCropRef.current.
|
|
152
|
+
easyCropRef.current.setZoom(ZOOM_INITIAL);
|
|
153
|
+
easyCropRef.current.setRotation(ROTATION_INITIAL);
|
|
150
154
|
};
|
|
151
155
|
const onCancel = useCallback(() => {
|
|
152
156
|
var _a, _b;
|
|
@@ -162,9 +166,9 @@ const ImgCrop = forwardRef((props, cropperRef) => {
|
|
|
162
166
|
const context = ((_a = target === null || target === void 0 ? void 0 : target.getRootNode) === null || _a === void 0 ? void 0 : _a.call(target)) || document;
|
|
163
167
|
const imgSource = context.querySelector(`.${PREFIX}-media`);
|
|
164
168
|
const { width: cropWidth, height: cropHeight, x: cropX, y: cropY, } = easyCropRef.current.cropPixelsRef.current;
|
|
165
|
-
if (
|
|
169
|
+
if (rotationSlider && easyCropRef.current.rotation !== ROTATION_INITIAL) {
|
|
166
170
|
const { naturalWidth: imgWidth, naturalHeight: imgHeight } = imgSource;
|
|
167
|
-
const angle = easyCropRef.current.
|
|
171
|
+
const angle = easyCropRef.current.rotation * (Math.PI / 180);
|
|
168
172
|
// get container for rotated image
|
|
169
173
|
const sine = Math.abs(Math.sin(angle));
|
|
170
174
|
const cosine = Math.abs(Math.cos(angle));
|
|
@@ -224,7 +228,7 @@ const ImgCrop = forwardRef((props, cropperRef) => {
|
|
|
224
228
|
return resolveRef.current(result);
|
|
225
229
|
}
|
|
226
230
|
}), type, quality);
|
|
227
|
-
}), [fillColor, quality,
|
|
231
|
+
}), [fillColor, quality, rotationSlider]);
|
|
228
232
|
const wrapClassName = `${PREFIX}-modal${modalClassName ? ` ${modalClassName}` : ''}`;
|
|
229
233
|
const title = useMemo(() => {
|
|
230
234
|
if (modalTitle) {
|
|
@@ -233,7 +237,7 @@ const ImgCrop = forwardRef((props, cropperRef) => {
|
|
|
233
237
|
const lang = typeof window !== 'undefined' ? window.navigator.language : '';
|
|
234
238
|
return lang === 'zh-CN' ? '编辑图片' : 'Edit image';
|
|
235
239
|
}, [modalTitle]);
|
|
236
|
-
return (jsxs(Fragment, { children: [uploadComponent, image && (jsx(AntModal, Object.assign({},
|
|
240
|
+
return (jsxs(Fragment, { children: [uploadComponent, image && (jsx(AntModal, Object.assign({}, modalProps, modalBaseProps, { [openKey]: true }, { title: title, onOk: onOk, onCancel: onCancel, wrapClassName: wrapClassName, maskClosable: false, destroyOnClose: true }, { children: jsx(EasyCrop$1, { ref: easyCropRef, cropperRef: cropperRef, zoomSlider: zoomSlider, rotationSlider: rotationSlider, aspectSlider: aspectSlider, image: image, aspect: aspect, minZoom: minZoom, maxZoom: maxZoom, cropShape: cropShape, showGrid: showGrid, cropperProps: cropperProps }) })))] }));
|
|
237
241
|
});
|
|
238
242
|
|
|
239
243
|
export { ImgCrop as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antd-img-crop",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.0",
|
|
4
4
|
"description": "An image cropper for Ant Design Upload",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -29,25 +29,26 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"compare-versions": "6.0.0-rc.1",
|
|
32
|
-
"react-easy-crop": "^4.7.
|
|
32
|
+
"react-easy-crop": "^4.7.4",
|
|
33
33
|
"tslib": "^2.5.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
+
"@rollup/plugin-replace": "^5.0.2",
|
|
36
37
|
"@rollup/plugin-typescript": "^11.0.0",
|
|
37
|
-
"@trivago/prettier-plugin-sort-imports": "^4.1.
|
|
38
|
-
"@types/node": "^18.
|
|
38
|
+
"@trivago/prettier-plugin-sort-imports": "^4.1.1",
|
|
39
|
+
"@types/node": "^18.15.1",
|
|
39
40
|
"@types/react": "^18.0.28",
|
|
40
41
|
"@types/react-dom": "^18.0.11",
|
|
41
42
|
"@types/rollup-plugin-less": "^1.1.2",
|
|
42
43
|
"@vitejs/plugin-react": "^3.1.0",
|
|
43
|
-
"antd": "^5.
|
|
44
|
-
"eslint": "^8.
|
|
45
|
-
"eslint-config-prettier": "^8.
|
|
44
|
+
"antd": "^5.3.1",
|
|
45
|
+
"eslint": "^8.36.0",
|
|
46
|
+
"eslint-config-prettier": "^8.7.0",
|
|
46
47
|
"eslint-config-react-app": "^7.0.1",
|
|
47
48
|
"prettier": "^2.8.4",
|
|
48
49
|
"react": "^18.2.0",
|
|
49
50
|
"react-dom": "^18.2.0",
|
|
50
|
-
"rollup": "3.
|
|
51
|
+
"rollup": "3.19.1",
|
|
51
52
|
"rollup-plugin-dts": "^5.2.0",
|
|
52
53
|
"rollup-plugin-less": "^1.1.3",
|
|
53
54
|
"typescript": "^4.9.5",
|