├── .eslintrc.cjs
├── .github
└── workflows
│ ├── gh-pages.yml
│ └── npm_publish.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
└── extensions.json
├── LICENSE
├── README.md
├── dist
├── index.d.ts
├── index.es.js
├── index.umd.js
└── shims-vue.d.ts
├── example
├── .eslintrc.cjs
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .vscode
│ └── extensions.json
├── demo
│ ├── assets
│ │ ├── demo-d54cd5f0.jpg
│ │ ├── index-987b8e67.js
│ │ └── index-eb952961.css
│ └── index.html
├── env.d.ts
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── App.vue
│ ├── CanvasModal.vue
│ ├── assets
│ │ ├── app.css
│ │ └── demo.jpg
│ └── main.ts
├── tsconfig.config.json
├── tsconfig.json
└── vite.config.ts
├── package-lock.json
├── package.json
├── src
├── VueCropper.vue
├── index.ts
├── shims-vue.d.ts
└── types.ts
├── tsconfig.config.json
├── tsconfig.json
└── vite.config.ts
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | require("@rushstack/eslint-patch/modern-module-resolution");
3 |
4 | module.exports = {
5 | "root": true,
6 | "extends": [
7 | "plugin:vue/vue3-essential",
8 | "eslint:recommended",
9 | "@vue/eslint-config-typescript/recommended",
10 | "@vue/eslint-config-prettier"
11 | ],
12 | "env": {
13 | "vue/setup-compiler-macros": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: github pages
2 |
3 | # on 是 Actions 的触发条件,这里的配置说明当 master 分支有提交的时候,根据这个配置文件执行
4 | on:
5 | push:
6 | branches:
7 | - master # Set a branch to deploy
8 |
9 | # jobs 是要执行的任务,我们看到他要执行 deploy
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest # 运行环境
13 | steps: # 执行步骤
14 |
15 | # checkout 分支
16 | - uses: actions/checkout@v3
17 | - run: cp -r example/demo .temp
18 |
19 | - uses: actions/checkout@v3
20 | with:
21 | ref: gh-pages
22 | clean: false
23 |
24 | - run: cp -rf .temp/* .
25 | - run: rm -r .temp
26 | - run: git config user.name "${{ github.actor }}"
27 | - run: git config user.email "${{ github.actor }}@users.noreply.github.com"
28 | - run: git add --all
29 | - run: git commit --message "${{ github.ref_name }}"
30 | - run: git push
31 |
--------------------------------------------------------------------------------
/.github/workflows/npm_publish.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags:
4 | - '*'
5 |
6 | jobs:
7 | publish:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v3
11 | - uses: actions/setup-node@v3
12 | with:
13 | node-version: 16
14 | - run: npm install
15 | - uses: JS-DevTools/npm-publish@v1
16 | with:
17 | token: ${{ secrets.NPM_ACCESS_TOKEN }}
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist-ssr
13 | coverage
14 | *.local
15 |
16 | /cypress/videos/
17 | /cypress/screenshots/
18 |
19 | # Editor directories and files
20 | .vscode/*
21 | !.vscode/extensions.json
22 | .idea
23 | *.suo
24 | *.ntvs*
25 | *.njsproj
26 | *.sln
27 | *.sw?
28 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /dist/*
2 | .local
3 | /node_modules/**
4 | /example/src/assets/*
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | semi: false # 语句末尾是否加分号
2 | singleQuote: true # 使用单引号代替双引号
3 | printWidth: 100 # 超过多长进行换行
4 | trailingComma: 'none' # 多行输入的尾逗号是否添加
5 | arrowParens: 'avoid' # (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号
6 | endOfLine: 'lf' # 格式化换行符,默认值 lf
7 | # vueIndentScriptAndStyle: true # vue script 标签的缩进开启
8 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Hccake
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-cropper
2 |
3 | A Vue image cropper components by cropperjs.
4 |
5 | Github: https://github.com/ballcat-projects/vue-cropper
6 |
7 | ## Demo
8 |
9 | online demo: https://ballcat-projects.github.io/vue-cropper
10 |
11 | You can also clone the repository and run the demo locally:
12 |
13 | ```shell
14 | # clone
15 | git clone https://github.com/ballcat-projects/vue-cropper.git
16 |
17 | # enter the folder
18 | cd vue-cropper/example
19 |
20 | # install dependency
21 | npm install
22 |
23 | # run it
24 | npm run dev
25 | ```
26 |
27 | ## Getting started
28 |
29 | ### Installation
30 |
31 | ```npm
32 | npm install @ballcat/vue-cropper
33 | ```
34 | or
35 | ```yarn
36 | yarn add @ballcat/vue-cropper
37 | ```
38 |
39 | ### Usage
40 |
41 | #### Global Registration
42 |
43 | ```vue
44 | import { createApp } from 'vue';
45 | import App from './App';
46 |
47 | import VueCropper from '@ballcat/vue-cropper';
48 | import 'cropperjs/dist/cropper.css';
49 |
50 | const app = createApp(App);
51 |
52 | app.use(VueCropper).mount('#app');
53 | ```
54 |
55 | #### Local Registration
56 |
57 | ```vue
58 |
59 |
64 |
65 |
89 | ```
90 |
91 | or use setup script
92 |
93 | ```vue
94 |
106 | ```
107 |
108 | ## API
109 |
110 | VueCropper props that can be used are divided into two parts, custom and all properties supported by cropperjs
111 |
112 | ### custom options
113 |
114 | | Property | Description | Type | Required |
115 | | :------------- | :------------------------------------------ | :------ | :------- |
116 | | src | origin image src | string | true |
117 | | imgStyle | the img element style | object | -- |
118 | | imgCrossOrigin | the img element crossOrigin attribute value | string | -- |
119 | | alt | the img element alt attribute value | boolean | -- |
120 |
121 | ### Cropperjs options
122 |
123 | see [cropperjs document](https://github.com/fengyuanchen/cropperjs/blob/main/README.md)
124 |
125 |
126 | ### custom expose method
127 |
128 | | Method | Description | Type |
129 | |:--------|:------------------------------|:-----------|
130 | | flipX | flip the picture horizontally | () => void |
131 | | flipY | flip the picture vertically | () => void |
132 |
--------------------------------------------------------------------------------
/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | import type { AllowedComponentProps } from 'vue';
2 | import type { ComponentCustomProps } from 'vue';
3 | import type { ComponentOptionsMixin } from 'vue';
4 | import { default as Cropper_2 } from 'cropperjs';
5 | import type { CSSProperties } from 'vue';
6 | import type { DefineComponent } from 'vue';
7 | import type { ExtractPropTypes } from 'vue';
8 | import type { PropType } from 'vue';
9 | import type { Ref } from 'vue';
10 | import type { VNodeProps } from 'vue';
11 |
12 | declare const _sfc_main: DefineComponent<{
13 | src: {
14 | type: StringConstructor;
15 | required: true;
16 | };
17 | alt: {
18 | type: StringConstructor;
19 | default: string;
20 | };
21 | imgStyle: {
22 | type: PropType;
23 | default: () => {};
24 | };
25 | imgCrossOrigin: {
26 | type: PropType<"" | "anonymous" | "use-credentials" | undefined>;
27 | default: undefined;
28 | };
29 | viewMode: {
30 | type: PropType;
31 | default: number;
32 | };
33 | dragMode: {
34 | type: PropType;
35 | default: string;
36 | };
37 | initialAspectRatio: {
38 | type: NumberConstructor;
39 | default: number;
40 | };
41 | aspectRatio: {
42 | type: NumberConstructor;
43 | default: number;
44 | };
45 | data: {
46 | type: PropType;
47 | default: undefined;
48 | };
49 | preview: {
50 | type: PropType | undefined>;
51 | default: string;
52 | };
53 | responsive: {
54 | type: BooleanConstructor;
55 | default: boolean;
56 | };
57 | restore: {
58 | type: BooleanConstructor;
59 | default: boolean;
60 | };
61 | checkCrossOrigin: {
62 | type: BooleanConstructor;
63 | default: boolean;
64 | };
65 | checkOrientation: {
66 | type: BooleanConstructor;
67 | default: boolean;
68 | };
69 | modal: {
70 | type: BooleanConstructor;
71 | default: boolean;
72 | };
73 | guides: {
74 | type: BooleanConstructor;
75 | default: boolean;
76 | };
77 | center: {
78 | type: BooleanConstructor;
79 | default: boolean;
80 | };
81 | highlight: {
82 | type: BooleanConstructor;
83 | default: boolean;
84 | };
85 | background: {
86 | type: BooleanConstructor;
87 | default: boolean;
88 | };
89 | autoCrop: {
90 | type: BooleanConstructor;
91 | default: boolean;
92 | };
93 | autoCropArea: {
94 | type: NumberConstructor;
95 | default: number;
96 | };
97 | movable: {
98 | type: BooleanConstructor;
99 | default: boolean;
100 | };
101 | rotatable: {
102 | type: BooleanConstructor;
103 | default: boolean;
104 | };
105 | scalable: {
106 | type: BooleanConstructor;
107 | default: boolean;
108 | };
109 | zoomable: {
110 | type: BooleanConstructor;
111 | default: boolean;
112 | };
113 | zoomOnTouch: {
114 | type: BooleanConstructor;
115 | default: boolean;
116 | };
117 | zoomOnWheel: {
118 | type: BooleanConstructor;
119 | default: boolean;
120 | };
121 | wheelZoomRatio: {
122 | type: NumberConstructor;
123 | default: number;
124 | };
125 | cropBoxMovable: {
126 | type: BooleanConstructor;
127 | default: boolean;
128 | };
129 | cropBoxResizable: {
130 | type: BooleanConstructor;
131 | default: boolean;
132 | };
133 | toggleDragModeOnDblclick: {
134 | type: BooleanConstructor;
135 | default: boolean;
136 | };
137 | minCanvasWidth: {
138 | type: NumberConstructor;
139 | default: number;
140 | };
141 | minCanvasHeight: {
142 | type: NumberConstructor;
143 | default: number;
144 | };
145 | minCropBoxWidth: {
146 | type: NumberConstructor;
147 | default: number;
148 | };
149 | minCropBoxHeight: {
150 | type: NumberConstructor;
151 | default: number;
152 | };
153 | minContainerWidth: {
154 | type: NumberConstructor;
155 | default: number;
156 | };
157 | minContainerHeight: {
158 | type: NumberConstructor;
159 | default: number;
160 | };
161 | ready: {
162 | type: PropType<(event: Cropper_2.ReadyEvent) => void>;
163 | default: undefined;
164 | };
165 | cropstart: {
166 | type: PropType<(event: Cropper_2.CropStartEvent) => void>;
167 | default: undefined;
168 | };
169 | cropmove: {
170 | type: PropType<(event: Cropper_2.CropMoveEvent) => void>;
171 | default: undefined;
172 | };
173 | cropend: {
174 | type: PropType<(event: Cropper_2.CropEndEvent) => void>;
175 | default: undefined;
176 | };
177 | crop: {
178 | type: PropType<(event: Cropper_2.CropEvent) => void>;
179 | default: undefined;
180 | };
181 | zoom: {
182 | type: PropType<(event: Cropper_2.ZoomEvent) => void>;
183 | default: undefined;
184 | };
185 | }, {
186 | imageStyle: {
187 | display: string;
188 | maxWidth: string;
189 | };
190 | props: any;
191 | imageRef: Ref;
192 | cropper: Cropper_2 | undefined;
193 | initCropper: () => void;
194 | }, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly;
205 | default: () => {};
206 | };
207 | imgCrossOrigin: {
208 | type: PropType<"" | "anonymous" | "use-credentials" | undefined>;
209 | default: undefined;
210 | };
211 | viewMode: {
212 | type: PropType;
213 | default: number;
214 | };
215 | dragMode: {
216 | type: PropType;
217 | default: string;
218 | };
219 | initialAspectRatio: {
220 | type: NumberConstructor;
221 | default: number;
222 | };
223 | aspectRatio: {
224 | type: NumberConstructor;
225 | default: number;
226 | };
227 | data: {
228 | type: PropType;
229 | default: undefined;
230 | };
231 | preview: {
232 | type: PropType | undefined>;
233 | default: string;
234 | };
235 | responsive: {
236 | type: BooleanConstructor;
237 | default: boolean;
238 | };
239 | restore: {
240 | type: BooleanConstructor;
241 | default: boolean;
242 | };
243 | checkCrossOrigin: {
244 | type: BooleanConstructor;
245 | default: boolean;
246 | };
247 | checkOrientation: {
248 | type: BooleanConstructor;
249 | default: boolean;
250 | };
251 | modal: {
252 | type: BooleanConstructor;
253 | default: boolean;
254 | };
255 | guides: {
256 | type: BooleanConstructor;
257 | default: boolean;
258 | };
259 | center: {
260 | type: BooleanConstructor;
261 | default: boolean;
262 | };
263 | highlight: {
264 | type: BooleanConstructor;
265 | default: boolean;
266 | };
267 | background: {
268 | type: BooleanConstructor;
269 | default: boolean;
270 | };
271 | autoCrop: {
272 | type: BooleanConstructor;
273 | default: boolean;
274 | };
275 | autoCropArea: {
276 | type: NumberConstructor;
277 | default: number;
278 | };
279 | movable: {
280 | type: BooleanConstructor;
281 | default: boolean;
282 | };
283 | rotatable: {
284 | type: BooleanConstructor;
285 | default: boolean;
286 | };
287 | scalable: {
288 | type: BooleanConstructor;
289 | default: boolean;
290 | };
291 | zoomable: {
292 | type: BooleanConstructor;
293 | default: boolean;
294 | };
295 | zoomOnTouch: {
296 | type: BooleanConstructor;
297 | default: boolean;
298 | };
299 | zoomOnWheel: {
300 | type: BooleanConstructor;
301 | default: boolean;
302 | };
303 | wheelZoomRatio: {
304 | type: NumberConstructor;
305 | default: number;
306 | };
307 | cropBoxMovable: {
308 | type: BooleanConstructor;
309 | default: boolean;
310 | };
311 | cropBoxResizable: {
312 | type: BooleanConstructor;
313 | default: boolean;
314 | };
315 | toggleDragModeOnDblclick: {
316 | type: BooleanConstructor;
317 | default: boolean;
318 | };
319 | minCanvasWidth: {
320 | type: NumberConstructor;
321 | default: number;
322 | };
323 | minCanvasHeight: {
324 | type: NumberConstructor;
325 | default: number;
326 | };
327 | minCropBoxWidth: {
328 | type: NumberConstructor;
329 | default: number;
330 | };
331 | minCropBoxHeight: {
332 | type: NumberConstructor;
333 | default: number;
334 | };
335 | minContainerWidth: {
336 | type: NumberConstructor;
337 | default: number;
338 | };
339 | minContainerHeight: {
340 | type: NumberConstructor;
341 | default: number;
342 | };
343 | ready: {
344 | type: PropType<(event: Cropper_2.ReadyEvent) => void>;
345 | default: undefined;
346 | };
347 | cropstart: {
348 | type: PropType<(event: Cropper_2.CropStartEvent) => void>;
349 | default: undefined;
350 | };
351 | cropmove: {
352 | type: PropType<(event: Cropper_2.CropMoveEvent) => void>;
353 | default: undefined;
354 | };
355 | cropend: {
356 | type: PropType<(event: Cropper_2.CropEndEvent) => void>;
357 | default: undefined;
358 | };
359 | crop: {
360 | type: PropType<(event: Cropper_2.CropEvent) => void>;
361 | default: undefined;
362 | };
363 | zoom: {
364 | type: PropType<(event: Cropper_2.ZoomEvent) => void>;
365 | default: undefined;
366 | };
367 | }>>, {
368 | crop: (event: Cropper_2.CropEvent) => void;
369 | preview: string | HTMLElement | HTMLElement[] | NodeListOf | undefined;
370 | alt: string;
371 | imgStyle: CSSProperties;
372 | imgCrossOrigin: "" | "anonymous" | "use-credentials" | undefined;
373 | viewMode: Cropper_2.ViewMode;
374 | dragMode: Cropper_2.DragMode;
375 | initialAspectRatio: number;
376 | aspectRatio: number;
377 | data: Cropper_2.SetDataOptions;
378 | responsive: boolean;
379 | restore: boolean;
380 | checkCrossOrigin: boolean;
381 | checkOrientation: boolean;
382 | modal: boolean;
383 | guides: boolean;
384 | center: boolean;
385 | highlight: boolean;
386 | background: boolean;
387 | autoCrop: boolean;
388 | autoCropArea: number;
389 | movable: boolean;
390 | rotatable: boolean;
391 | scalable: boolean;
392 | zoomable: boolean;
393 | zoomOnTouch: boolean;
394 | zoomOnWheel: boolean;
395 | wheelZoomRatio: number;
396 | cropBoxMovable: boolean;
397 | cropBoxResizable: boolean;
398 | toggleDragModeOnDblclick: boolean;
399 | minCanvasWidth: number;
400 | minCanvasHeight: number;
401 | minCropBoxWidth: number;
402 | minCropBoxHeight: number;
403 | minContainerWidth: number;
404 | minContainerHeight: number;
405 | ready: (event: Cropper_2.ReadyEvent) => void;
406 | cropstart: (event: Cropper_2.CropStartEvent) => void;
407 | cropmove: (event: Cropper_2.CropMoveEvent) => void;
408 | cropend: (event: Cropper_2.CropEndEvent) => void;
409 | zoom: (event: Cropper_2.ZoomEvent) => void;
410 | }>;
411 | export default _sfc_main;
412 |
413 | export declare interface VueCropperInstance {
414 | clear(): Cropper_2;
415 | crop(): Cropper_2;
416 | destroy(): Cropper_2;
417 | disable(): Cropper_2;
418 | enable(): Cropper_2;
419 | getCanvasData(): Cropper_2.CanvasData;
420 | getContainerData(): Cropper_2.ContainerData;
421 | getCropBoxData(): Cropper_2.CropBoxData;
422 | getCroppedCanvas(options?: Cropper_2.GetCroppedCanvasOptions): HTMLCanvasElement;
423 | getData(rounded?: boolean): Cropper_2.Data;
424 | getImageData(): Cropper_2.ImageData;
425 | move(offsetX: number, offsetY?: number): Cropper_2;
426 | moveTo(x: number, y?: number): Cropper_2;
427 | replace(url: string, onlyColorChanged?: boolean): Cropper_2;
428 | reset(): Cropper_2;
429 | rotate(degree: number): Cropper_2;
430 | rotateTo(degree: number): Cropper_2;
431 | scale(scaleX: number, scaleY?: number): Cropper_2;
432 | scaleX(scaleX: number): Cropper_2;
433 | scaleY(scaleY: number): Cropper_2;
434 | setAspectRatio(aspectRatio: number): Cropper_2;
435 | setCanvasData(data: Cropper_2.SetCanvasDataOptions): Cropper_2;
436 | setCropBoxData(data: Cropper_2.SetCropBoxDataOptions): Cropper_2;
437 | setData(data: Cropper_2.SetDataOptions): Cropper_2;
438 | setDragMode(dragMode: Cropper_2.DragMode): Cropper_2;
439 | zoom(ratio: number): Cropper_2;
440 | zoomTo(ratio: number, pivot?: {
441 | x: number;
442 | y: number;
443 | }): Cropper_2;
444 | flipX(): void;
445 | flipY(): void;
446 | }
447 |
448 | export { }
449 |
--------------------------------------------------------------------------------
/dist/index.es.js:
--------------------------------------------------------------------------------
1 | import { defineComponent as s, ref as r, onMounted as f, watch as m, nextTick as y, openBlock as g, createElementBlock as v, createElementVNode as B, normalizeStyle as C, toRaw as b } from "vue";
2 | import D from "cropperjs";
3 | const h = ["src", "alt", "crossorigin"], x = /* @__PURE__ */ s({
4 | __name: "VueCropper",
5 | props: {
6 | // custom props
7 | src: {
8 | type: String,
9 | required: !0
10 | },
11 | alt: {
12 | type: String,
13 | default: "image"
14 | },
15 | imgStyle: {
16 | type: Object,
17 | default: () => ({})
18 | },
19 | imgCrossOrigin: {
20 | type: String,
21 | default: void 0
22 | },
23 | // ========= CropperJS options =======
24 | // Define the view mode of the cropper
25 | viewMode: {
26 | type: Number,
27 | // 0, 1, 2, 3
28 | default: 0
29 | },
30 | // Define the dragging mode of the cropper
31 | dragMode: {
32 | type: String,
33 | // 'crop', 'move' or 'none'
34 | default: "crop"
35 | },
36 | // Define the initial aspect ratio of the crop box
37 | initialAspectRatio: {
38 | type: Number,
39 | default: NaN
40 | },
41 | // Define the aspect ratio of the crop box
42 | aspectRatio: {
43 | type: Number,
44 | default: NaN
45 | },
46 | // An object with the previous cropping result data
47 | data: {
48 | type: Object,
49 | default: void 0
50 | },
51 | // A selector for adding extra containers to preview
52 | preview: {
53 | type: [String, Array, Object],
54 | default: ""
55 | },
56 | // Re-render the cropper when resize the window
57 | responsive: {
58 | type: Boolean,
59 | default: !0
60 | },
61 | // Restore the cropped area after resize the window
62 | restore: {
63 | type: Boolean,
64 | default: !0
65 | },
66 | // Check if the current image is a cross-origin image
67 | checkCrossOrigin: {
68 | type: Boolean,
69 | default: !0
70 | },
71 | // Check the current image's Exif Orientation information
72 | checkOrientation: {
73 | type: Boolean,
74 | default: !0
75 | },
76 | // Show the black modal
77 | modal: {
78 | type: Boolean,
79 | default: !0
80 | },
81 | // Show the dashed lines for guiding
82 | guides: {
83 | type: Boolean,
84 | default: !0
85 | },
86 | // Show the center indicator for guiding
87 | center: {
88 | type: Boolean,
89 | default: !0
90 | },
91 | // Show the white modal to highlight the crop box
92 | highlight: {
93 | type: Boolean,
94 | default: !0
95 | },
96 | // Show the grid background
97 | background: {
98 | type: Boolean,
99 | default: !0
100 | },
101 | // Enable to crop the image automatically when initialize
102 | autoCrop: {
103 | type: Boolean,
104 | default: !0
105 | },
106 | // Define the percentage of automatic cropping area when initializes
107 | autoCropArea: {
108 | type: Number,
109 | default: 0.8
110 | },
111 | // Enable to move the image
112 | movable: {
113 | type: Boolean,
114 | default: !0
115 | },
116 | // Enable to rotate the image
117 | rotatable: {
118 | type: Boolean,
119 | default: !0
120 | },
121 | // Enable to scale the image
122 | scalable: {
123 | type: Boolean,
124 | default: !0
125 | },
126 | // Enable to zoom the image
127 | zoomable: {
128 | type: Boolean,
129 | default: !0
130 | },
131 | // Enable to zoom the image by dragging touch
132 | zoomOnTouch: {
133 | type: Boolean,
134 | default: !0
135 | },
136 | // Enable to zoom the image by wheeling mouse
137 | zoomOnWheel: {
138 | type: Boolean,
139 | default: !0
140 | },
141 | // Define zoom ratio when zoom the image by wheeling mouse
142 | wheelZoomRatio: {
143 | type: Number,
144 | default: 0.1
145 | },
146 | // Enable to move the crop box
147 | cropBoxMovable: {
148 | type: Boolean,
149 | default: !0
150 | },
151 | // Enable to resize the crop box
152 | cropBoxResizable: {
153 | type: Boolean,
154 | default: !0
155 | },
156 | // Toggle drag mode between "crop" and "move" when click twice on the cropper
157 | toggleDragModeOnDblclick: {
158 | type: Boolean,
159 | default: !0
160 | },
161 | // Size limitation
162 | minCanvasWidth: {
163 | type: Number,
164 | default: 0
165 | },
166 | minCanvasHeight: {
167 | type: Number,
168 | default: 0
169 | },
170 | minCropBoxWidth: {
171 | type: Number,
172 | default: 0
173 | },
174 | minCropBoxHeight: {
175 | type: Number,
176 | default: 0
177 | },
178 | minContainerWidth: {
179 | type: Number,
180 | default: 200
181 | },
182 | minContainerHeight: {
183 | type: Number,
184 | default: 100
185 | },
186 | // Shortcuts of events
187 | ready: {
188 | type: Function,
189 | default: void 0
190 | },
191 | cropstart: {
192 | type: Function,
193 | default: void 0
194 | },
195 | cropmove: {
196 | type: Function,
197 | default: void 0
198 | },
199 | cropend: {
200 | type: Function,
201 | default: void 0
202 | },
203 | crop: {
204 | type: Function,
205 | default: void 0
206 | },
207 | zoom: {
208 | type: Function,
209 | default: void 0
210 | }
211 | },
212 | setup(o, { expose: i }) {
213 | const n = o, d = {
214 | display: "block",
215 | /* This rule is very important, please don't ignore this */
216 | maxWidth: "100%"
217 | }, u = r();
218 | let e;
219 | function l() {
220 | n.src ? e = new D(u.value, b(n)) : e = void 0;
221 | }
222 | return f(l), m(
223 | () => n,
224 | () => {
225 | e == null || e.destroy(), y(l);
226 | },
227 | { deep: !0 }
228 | ), i({
229 | // Clear the crop box
230 | clear() {
231 | return e == null ? void 0 : e.clear();
232 | },
233 | // Show the crop box manually
234 | crop() {
235 | return e == null ? void 0 : e.crop();
236 | },
237 | /**
238 | * Destroy the cropper and remove the instance from the image
239 | * @returns {Cropper} this
240 | */
241 | destroy() {
242 | return e == null ? void 0 : e.destroy();
243 | },
244 | // Disable (freeze) the cropper
245 | disable() {
246 | return e == null ? void 0 : e.disable();
247 | },
248 | // Enable (unfreeze) the cropper
249 | enable() {
250 | return e == null ? void 0 : e.enable();
251 | },
252 | /**
253 | * Get the canvas position and size data.
254 | * @returns {Object} The result canvas data.
255 | */
256 | getCanvasData() {
257 | return e == null ? void 0 : e.getCanvasData();
258 | },
259 | /**
260 | * Get the container size data.
261 | * @returns {Object} The result container data.
262 | */
263 | getContainerData() {
264 | return e == null ? void 0 : e.getContainerData();
265 | },
266 | /**
267 | * Get the crop box position and size data.
268 | * @returns {Object} The result crop box data.
269 | */
270 | getCropBoxData() {
271 | return e == null ? void 0 : e.getCropBoxData();
272 | },
273 | /**
274 | * Get a canvas drawn the cropped image.
275 | * @param {Object} [options={}] - The config options.
276 | * @returns {HTMLCanvasElement} - The result canvas.
277 | */
278 | getCroppedCanvas(t) {
279 | return e == null ? void 0 : e.getCroppedCanvas(t);
280 | },
281 | /**
282 | * Get the cropped area position and size data (base on the original image)
283 | * @param {boolean} [rounded=false] - Indicate if round the data values or not.
284 | * @returns {Object} The result cropped data.
285 | */
286 | getData(t) {
287 | return e == null ? void 0 : e.getData(t);
288 | },
289 | /**
290 | * Get the image position and size data.
291 | * @returns {Object} The result image data.
292 | */
293 | getImageData() {
294 | return e == null ? void 0 : e.getImageData();
295 | },
296 | /**
297 | * Move the canvas with relative offsets
298 | * @param {number} offsetX - The relative offset distance on the x-axis.
299 | * @param {number} [offsetY=offsetX] - The relative offset distance on the y-axis.
300 | * @returns {Cropper} this
301 | */
302 | move(t, a) {
303 | return e == null ? void 0 : e.move(t, a);
304 | },
305 | /**
306 | * Move the canvas to an absolute point
307 | * @param {number} x - The x-axis coordinate.
308 | * @param {number} [y=x] - The y-axis coordinate.
309 | * @returns {Cropper} this
310 | */
311 | moveTo(t, a) {
312 | return e == null ? void 0 : e.moveTo(t, a);
313 | },
314 | /**
315 | * Replace the image's src and rebuild the cropper
316 | * @param {string} url - The new URL.
317 | * @param {boolean} [hasSameSize] - Indicate if the new image has the same size as the old one.
318 | * @returns {Cropper} this
319 | */
320 | replace(t, a) {
321 | return e == null ? void 0 : e.replace(t, a);
322 | },
323 | // Reset the image and crop box to their initial states
324 | reset() {
325 | return e == null ? void 0 : e.reset();
326 | },
327 | /**
328 | * Rotate the canvas with a relative degree
329 | * @param {number} degree - The rotate degree.
330 | * @returns {Cropper} this
331 | */
332 | rotate(t) {
333 | return e == null ? void 0 : e.rotate(t);
334 | },
335 | /**
336 | * Rotate the canvas to an absolute degree
337 | * @param {number} degree - The rotate degree.
338 | * @returns {Cropper} this
339 | */
340 | rotateTo(t) {
341 | return e == null ? void 0 : e.rotateTo(t);
342 | },
343 | /**
344 | * Scale the image
345 | * @param {number} scaleX - The scale ratio on the x-axis.
346 | * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
347 | * @returns {Cropper} this
348 | */
349 | scale(t, a) {
350 | return e == null ? void 0 : e.scale(t, a);
351 | },
352 | /**
353 | * Scale the image on the x-axis.
354 | * @param {number} scaleX - The scale ratio on the x-axis.
355 | * @returns {Cropper} this
356 | */
357 | scaleX(t) {
358 | return e == null ? void 0 : e.scaleX(t);
359 | },
360 | /**
361 | * Scale the image on the y-axis.
362 | * @param {number} scaleY - The scale ratio on the y-axis.
363 | * @returns {Cropper} this
364 | */
365 | scaleY(t) {
366 | return e == null ? void 0 : e.scaleY(t);
367 | },
368 | /**
369 | * Change the aspect ratio of the crop box.
370 | * @param {number} aspectRatio - The new aspect ratio.
371 | * @returns {Cropper} this
372 | */
373 | setAspectRatio(t) {
374 | return e == null ? void 0 : e.setAspectRatio(t);
375 | },
376 | /**
377 | * Set the canvas position and size with new data.
378 | * @param {Object} data - The new canvas data.
379 | * @returns {Cropper} this
380 | */
381 | setCanvasData(t) {
382 | return e == null ? void 0 : e.setCanvasData(t);
383 | },
384 | /**
385 | * Set the crop box position and size with new data.
386 | * @param {Object} data - The new crop box data.
387 | * @returns {Cropper} this
388 | */
389 | setCropBoxData(t) {
390 | return e == null ? void 0 : e.setCropBoxData(t);
391 | },
392 | /**
393 | * Set the cropped area position and size with new data
394 | * @param {Object} data - The new data.
395 | * @returns {Cropper} this
396 | */
397 | setData(t) {
398 | return e == null ? void 0 : e.setData(t);
399 | },
400 | /**
401 | * Change the drag mode.
402 | * @param {string} dragMode - The new drag mode.
403 | * @returns {Cropper} this
404 | */
405 | setDragMode(t) {
406 | return e == null ? void 0 : e.setDragMode(t);
407 | },
408 | /**
409 | * Zoom the canvas with a relative ratio
410 | * @param {number} ratio - The target ratio.
411 | * @returns {Cropper} this
412 | */
413 | zoom(t) {
414 | return e == null ? void 0 : e.zoom(t);
415 | },
416 | /**
417 | * Zoom the canvas to an absolute ratio
418 | * @param {number} ratio - The target ratio.
419 | * @param {Object} pivot - The zoom pivot point coordinate.
420 | * @returns {Cropper} this
421 | */
422 | zoomTo(t, a) {
423 | return e == null ? void 0 : e.zoomTo(t, a);
424 | },
425 | /**
426 | * flip the image horizontally
427 | */
428 | flipX() {
429 | if (e) {
430 | const { scaleX: t } = e.getData();
431 | e.scaleX(-t);
432 | }
433 | },
434 | /**
435 | * flip the image vertically
436 | */
437 | flipY() {
438 | if (e) {
439 | const { scaleY: t } = e.getData();
440 | e.scaleY(-t);
441 | }
442 | }
443 | }), (t, a) => (g(), v("div", null, [
444 | B("img", {
445 | ref_key: "imageRef",
446 | ref: u,
447 | src: n.src,
448 | alt: n.alt,
449 | crossorigin: o.imgCrossOrigin,
450 | style: C([d, n.imgStyle])
451 | }, null, 12, h)
452 | ]));
453 | }
454 | });
455 | export {
456 | x as default
457 | };
458 |
--------------------------------------------------------------------------------
/dist/index.umd.js:
--------------------------------------------------------------------------------
1 | (function(a,u){typeof exports=="object"&&typeof module<"u"?module.exports=u(require("vue"),require("cropperjs")):typeof define=="function"&&define.amd?define(["vue","cropperjs"],u):(a=typeof globalThis<"u"?globalThis:a||self,a.VueCropper=u(a.Vue,a.CropperJs))})(this,function(a,u){"use strict";const s=["src","alt","crossorigin"];return a.defineComponent({__name:"VueCropper",props:{src:{type:String,required:!0},alt:{type:String,default:"image"},imgStyle:{type:Object,default:()=>({})},imgCrossOrigin:{type:String,default:void 0},viewMode:{type:Number,default:0},dragMode:{type:String,default:"crop"},initialAspectRatio:{type:Number,default:NaN},aspectRatio:{type:Number,default:NaN},data:{type:Object,default:void 0},preview:{type:[String,Array,Object],default:""},responsive:{type:Boolean,default:!0},restore:{type:Boolean,default:!0},checkCrossOrigin:{type:Boolean,default:!0},checkOrientation:{type:Boolean,default:!0},modal:{type:Boolean,default:!0},guides:{type:Boolean,default:!0},center:{type:Boolean,default:!0},highlight:{type:Boolean,default:!0},background:{type:Boolean,default:!0},autoCrop:{type:Boolean,default:!0},autoCropArea:{type:Number,default:.8},movable:{type:Boolean,default:!0},rotatable:{type:Boolean,default:!0},scalable:{type:Boolean,default:!0},zoomable:{type:Boolean,default:!0},zoomOnTouch:{type:Boolean,default:!0},zoomOnWheel:{type:Boolean,default:!0},wheelZoomRatio:{type:Number,default:.1},cropBoxMovable:{type:Boolean,default:!0},cropBoxResizable:{type:Boolean,default:!0},toggleDragModeOnDblclick:{type:Boolean,default:!0},minCanvasWidth:{type:Number,default:0},minCanvasHeight:{type:Number,default:0},minCropBoxWidth:{type:Number,default:0},minCropBoxHeight:{type:Number,default:0},minContainerWidth:{type:Number,default:200},minContainerHeight:{type:Number,default:100},ready:{type:Function,default:void 0},cropstart:{type:Function,default:void 0},cropmove:{type:Function,default:void 0},cropend:{type:Function,default:void 0},crop:{type:Function,default:void 0},zoom:{type:Function,default:void 0}},setup(l,{expose:r}){const o=l,f={display:"block",maxWidth:"100%"},i=a.ref();let e;function d(){o.src?e=new u(i.value,a.toRaw(o)):e=void 0}return a.onMounted(d),a.watch(()=>o,()=>{e==null||e.destroy(),a.nextTick(d)},{deep:!0}),r({clear(){return e==null?void 0:e.clear()},crop(){return e==null?void 0:e.crop()},destroy(){return e==null?void 0:e.destroy()},disable(){return e==null?void 0:e.disable()},enable(){return e==null?void 0:e.enable()},getCanvasData(){return e==null?void 0:e.getCanvasData()},getContainerData(){return e==null?void 0:e.getContainerData()},getCropBoxData(){return e==null?void 0:e.getCropBoxData()},getCroppedCanvas(t){return e==null?void 0:e.getCroppedCanvas(t)},getData(t){return e==null?void 0:e.getData(t)},getImageData(){return e==null?void 0:e.getImageData()},move(t,n){return e==null?void 0:e.move(t,n)},moveTo(t,n){return e==null?void 0:e.moveTo(t,n)},replace(t,n){return e==null?void 0:e.replace(t,n)},reset(){return e==null?void 0:e.reset()},rotate(t){return e==null?void 0:e.rotate(t)},rotateTo(t){return e==null?void 0:e.rotateTo(t)},scale(t,n){return e==null?void 0:e.scale(t,n)},scaleX(t){return e==null?void 0:e.scaleX(t)},scaleY(t){return e==null?void 0:e.scaleY(t)},setAspectRatio(t){return e==null?void 0:e.setAspectRatio(t)},setCanvasData(t){return e==null?void 0:e.setCanvasData(t)},setCropBoxData(t){return e==null?void 0:e.setCropBoxData(t)},setData(t){return e==null?void 0:e.setData(t)},setDragMode(t){return e==null?void 0:e.setDragMode(t)},zoom(t){return e==null?void 0:e.zoom(t)},zoomTo(t,n){return e==null?void 0:e.zoomTo(t,n)},flipX(){if(e){const{scaleX:t}=e.getData();e.scaleX(-t)}},flipY(){if(e){const{scaleY:t}=e.getData();e.scaleY(-t)}}}),(t,n)=>(a.openBlock(),a.createElementBlock("div",null,[a.createElementVNode("img",{ref_key:"imageRef",ref:i,src:o.src,alt:o.alt,crossorigin:l.imgCrossOrigin,style:a.normalizeStyle([f,o.imgStyle])},null,12,s)]))}})});
2 |
--------------------------------------------------------------------------------
/dist/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { DefineComponent } from 'vue'
3 | const component: DefineComponent, Record, any>
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/example/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | require("@rushstack/eslint-patch/modern-module-resolution");
3 |
4 | module.exports = {
5 | "root": true,
6 | "extends": [
7 | "plugin:vue/vue3-essential",
8 | "eslint:recommended",
9 | "@vue/eslint-config-typescript/recommended",
10 | "@vue/eslint-config-prettier"
11 | ],
12 | "env": {
13 | "vue/setup-compiler-macros": true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | dist-ssr
14 | coverage
15 | *.local
16 |
17 | /cypress/videos/
18 | /cypress/screenshots/
19 |
20 | # Editor directories and files
21 | .vscode/*
22 | !.vscode/extensions.json
23 | .idea
24 | *.suo
25 | *.ntvs*
26 | *.njsproj
27 | *.sln
28 | *.sw?
29 |
--------------------------------------------------------------------------------
/example/.prettierignore:
--------------------------------------------------------------------------------
1 | /dist/*
2 | .local
3 | /node_modules/**
4 |
5 | /src/assets/*
6 |
--------------------------------------------------------------------------------
/example/.prettierrc:
--------------------------------------------------------------------------------
1 | semi: false # 语句末尾是否加分号
2 | singleQuote: true # 使用单引号代替双引号
3 | printWidth: 100 # 超过多长进行换行
4 | trailingComma: 'none' # 多行输入的尾逗号是否添加
5 | arrowParens: 'avoid' # (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号
6 | endOfLine: 'lf' # 格式化换行符,默认值 lf
7 | # vueIndentScriptAndStyle: true # vue script 标签的缩进开启
8 |
--------------------------------------------------------------------------------
/example/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/example/demo/assets/demo-d54cd5f0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ballcat-projects/vue-cropper/f3b54d2cd433e2641df0b96053d497814842255f/example/demo/assets/demo-d54cd5f0.jpg
--------------------------------------------------------------------------------
/example/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vue Cropper Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vue Cropper Example
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "dev": "vite",
6 | "build": "run-p type-check build-only",
7 | "preview": "vite preview --port 4173",
8 | "build-only": "vite build",
9 | "type-check": "vue-tsc --noEmit",
10 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
11 | "preinstall": "npm install ../"
12 | },
13 | "dependencies": {
14 | "@ballcat/vue-cropper": "file:..",
15 | "ant-design-vue": "^3.2.15",
16 | "vue": "^3.2.45"
17 | },
18 | "devDependencies": {
19 | "@rushstack/eslint-patch": "^1.2.0",
20 | "@types/node": "^18.11.17",
21 | "@vitejs/plugin-vue": "^4.0.0",
22 | "@vue/eslint-config-prettier": "^7.0.0",
23 | "@vue/eslint-config-typescript": "^11.0.2",
24 | "@vue/tsconfig": "^0.1.3",
25 | "eslint": "^8.30.0",
26 | "eslint-plugin-vue": "^9.8.0",
27 | "npm-run-all": "^4.1.5",
28 | "prettier": "^2.8.1",
29 | "typescript": "~4.9.4",
30 | "vite": "^4.0.5",
31 | "vue-tsc": "^1.0.14"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
65 |
71 |
77 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
258 |
259 |
260 |
261 |
262 | 16:9
263 | 4:3
264 | 1:1
265 | 2:3
266 | Free
267 |
268 |
269 |
270 | VM0
271 | VM1
272 | VM2
273 | VM3
274 |
275 |
276 |
277 |
278 |
279 |
280 | responsive
281 |
282 |
283 | restore
284 |
285 |
286 | checkCrossOrigin
289 |
290 |
291 | checkOrientation
294 |
295 |
296 | modal
297 |
298 |
299 | guides
300 |
301 |
302 | center
303 |
304 |
305 | highlight
306 |
307 |
308 | background
309 |
310 |
311 | autoCrop
312 |
313 |
314 | movable
315 |
316 |
317 | rotatable
318 |
319 |
320 | scalable
321 |
322 |
323 | zoomable
324 |
325 |
326 | zoomOnTouch
327 |
328 |
329 | zoomOnWheel
330 |
331 |
332 |
333 | cropBoxMovable
334 |
335 |
336 |
337 |
338 | cropBoxResizable
339 |
340 |
341 |
342 |
343 | toggleDragModeOnDblclick
344 |
345 |
346 |
347 |
348 |
349 | Toggle Options
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
484 |
485 |
526 |
--------------------------------------------------------------------------------
/example/src/CanvasModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
49 |
50 |
55 |
--------------------------------------------------------------------------------
/example/src/assets/app.css:
--------------------------------------------------------------------------------
1 | .header {
2 | display: flex;
3 | }
4 |
5 | .docs-demo {
6 | margin-bottom: 1rem;
7 | overflow: hidden;
8 | padding: 2px;
9 | }
10 |
11 | .img-container,
12 | .img-preview {
13 | background-color: #f7f7f7;
14 | text-align: center;
15 | width: 100%;
16 | }
17 |
18 | .img-container {
19 | max-height: 497px;
20 | min-height: 200px;
21 | }
22 |
23 | @media (min-width: 768px) {
24 | .img-container {
25 | min-height: 497px;
26 | }
27 | }
28 |
29 | .img-container > img {
30 | max-width: 100%;
31 | }
32 |
33 | .docs-preview {
34 | margin-right: -1rem;
35 | padding-top: 2px;
36 | }
37 |
38 | .img-preview {
39 | float: left;
40 | margin-bottom: 0.5rem;
41 | margin-right: 0.5rem;
42 | overflow: hidden;
43 | }
44 |
45 | .img-preview > img {
46 | max-width: 100%;
47 | }
48 |
49 | .preview-lg {
50 | height: 9rem;
51 | width: 16rem;
52 | }
53 |
54 | .preview-md {
55 | height: 4.5rem;
56 | width: 8rem;
57 | }
58 |
59 | .preview-sm {
60 | height: 2.25rem;
61 | width: 4rem;
62 | }
63 |
64 | .preview-xs {
65 | height: 1.125rem;
66 | margin-right: 0;
67 | width: 2rem;
68 | }
69 |
70 | .docs-data > .input-group {
71 | margin-bottom: 0.5rem;
72 | height: 31px;
73 | }
74 |
75 | .docs-data .ant-input-group-addon {
76 | background-color: #e9ecef;
77 | border-radius: 0.2rem;
78 | text-align: left;
79 | }
80 |
81 | .docs-data .ant-input-group-addon:first-child {
82 | min-width: 4rem;
83 | }
84 |
85 | .docs-data .ant-input-group-addon:last-child {
86 | min-width: 3rem;
87 | }
88 |
89 | .docs-buttons > .ant-btn,
90 | .docs-buttons > .ant-btn-group,
91 | .docs-buttons > .form-control {
92 | margin-bottom: 0.5rem;
93 | margin-right: 0.5rem;
94 | }
95 |
96 | .docs-buttons .ant-btn-icon-only {
97 | width: 40px;
98 | height: 40px;
99 | }
100 |
101 | .docs-buttons .ant-btn {
102 | height: 40px;
103 | }
104 |
105 |
106 | .docs-toggles > .ant-btn,
107 | .docs-toggles > .ant-radio-group,
108 | .docs-toggles > .dropdown {
109 | margin-bottom: 0.5rem;
110 | }
111 |
112 | .docs-toggles > .ant-radio-group .ant-radio-button-wrapper {
113 | padding: 0 14px;
114 | height: 40px;
115 | line-height: 38px;
116 | }
117 |
118 | .btn-group-crop > .ant-btn-primary {
119 | background: #28a745;
120 | border-color: #28a745;
121 | }
122 | .btn-group-crop > .ant-btn-primary:hover {
123 | background: #42bf5b;
124 | border-color: #42bf5b;
125 | }
126 | .btn-group-crop .ant-btn-primary:hover, .btn-group-crop .ant-btn-primary:focus {
127 | color: #fff;
128 | background: #42bf5b;
129 | border-color: #42bf5b;
130 | }
131 | .btn-group-crop .ant-btn-primary:first-child:not(:last-child) {
132 | border-right-color: #8ec59a !important;
133 | }
134 | .ant-btn-group .ant-btn-primary:not(:first-child):not(:last-child) {
135 | border-left-color: #8ec59a !important;
136 | border-right-color: #8ec59a !important;
137 | }
138 | .btn-group-crop .ant-btn-primary:last-child:not(:first-child), .btn-group-crop .ant-btn-primary + .ant-btn-primary {
139 | border-left-color: #8ec59a !important;
140 | }
141 |
142 |
143 | .btn-secondary.ant-btn-primary {
144 | background: #6c757d;
145 | border-color: #6c757d;
146 | }
147 | .btn-secondary.ant-btn-primary:hover {
148 | background: #9397a1;
149 | border-color: #9397a1;
150 | }
151 | .btn-secondary.ant-btn-primary:hover, .btn-secondary.ant-btn-primary:focus {
152 | color: #fff;
153 | background: #9397a1;
154 | border-color: #9397a1;
155 | }
156 |
--------------------------------------------------------------------------------
/example/src/assets/demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ballcat-projects/vue-cropper/f3b54d2cd433e2641df0b96053d497814842255f/example/src/assets/demo.jpg
--------------------------------------------------------------------------------
/example/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | import 'ant-design-vue/dist/antd.css'
4 |
5 | import {
6 | Button,
7 | Row,
8 | Col,
9 | Input,
10 | Radio,
11 | Modal,
12 | Dropdown,
13 | Menu,
14 | Checkbox,
15 | Upload
16 | } from 'ant-design-vue'
17 |
18 | const app = createApp(App)
19 |
20 | app
21 | .use(Button)
22 | .use(Row)
23 | .use(Col)
24 | .use(Input)
25 | .use(Radio)
26 | .use(Modal)
27 | .use(Dropdown)
28 | .use(Menu)
29 | .use(Checkbox)
30 | .use(Upload)
31 |
32 | app.mount('#app')
33 |
--------------------------------------------------------------------------------
/example/tsconfig.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.node.json",
3 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "types": ["node"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.web.json",
3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4 | "compilerOptions": {
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | },
10 |
11 | "references": [
12 | {
13 | "path": "./tsconfig.config.json"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/example/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'url'
2 |
3 | import { defineConfig } from 'vite'
4 | import vue from '@vitejs/plugin-vue'
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | plugins: [vue()],
9 | resolve: {
10 | alias: {
11 | '@': fileURLToPath(new URL('./src', import.meta.url))
12 | }
13 | },
14 | build: {
15 | outDir: 'demo'
16 | },
17 | base: './'
18 | })
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@ballcat/vue-cropper",
3 | "description": "vue image cropper by cropperjs",
4 | "version": "1.0.6",
5 | "scripts": {
6 | "build": "run-p type-check build-only",
7 | "build-only": "vite build",
8 | "type-check": "vue-tsc --noEmit",
9 | "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "types": "./dist/index.d.ts",
15 | "main": "./dist/index.umd.js",
16 | "module": "./dist/index.es.js",
17 | "exports": {
18 | ".": {
19 | "import": {
20 | "types": "./dist/index.d.ts",
21 | "default": "./dist/index.es.js"
22 | },
23 | "require": {
24 | "types": "./dist/index.d.ts",
25 | "default": "./dist/index.umd.js"
26 | }
27 | }
28 | },
29 | "keywords": [
30 | "cropper",
31 | "vue-cropper",
32 | "vue-cropperjs",
33 | "cropperjs"
34 | ],
35 | "author": "Hccake ",
36 | "license": "MIT",
37 | "dependencies": {
38 | "cropperjs": "^1.5.13"
39 | },
40 | "peerDependencies": {
41 | "vue": ">=3.2.0"
42 | },
43 | "devDependencies": {
44 | "@rushstack/eslint-patch": "^1.2.0",
45 | "@types/node": "^18.11.17",
46 | "@vitejs/plugin-vue": "^4.0.0",
47 | "@vue/eslint-config-prettier": "^7.0.0",
48 | "@vue/eslint-config-typescript": "^11.0.2",
49 | "@vue/tsconfig": "^0.1.3",
50 | "eslint": "^8.30.0",
51 | "eslint-plugin-vue": "^9.8.0",
52 | "npm-run-all": "^4.1.5",
53 | "prettier": "^2.8.1",
54 | "typescript": "~4.9.4",
55 | "vite": "^4.0.5",
56 | "vite-plugin-dts": "^1.7.1",
57 | "vue": "^3.2.45",
58 | "vue-tsc": "^1.0.14"
59 | },
60 | "engines": {
61 | "node": ">=12.0.0"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/VueCropper.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
![]()
10 |
11 |
12 |
13 |
470 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './types'
2 |
3 | import VueCropper from './VueCropper.vue'
4 | export default VueCropper
5 |
--------------------------------------------------------------------------------
/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { DefineComponent } from 'vue'
3 | const component: DefineComponent, Record, any>
4 | export default component
5 | }
6 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | import type Cropper from 'cropperjs'
2 |
3 | export interface VueCropperInstance {
4 | clear(): Cropper
5 | crop(): Cropper
6 | destroy(): Cropper
7 | disable(): Cropper
8 | enable(): Cropper
9 | getCanvasData(): Cropper.CanvasData
10 | getContainerData(): Cropper.ContainerData
11 | getCropBoxData(): Cropper.CropBoxData
12 | getCroppedCanvas(options?: Cropper.GetCroppedCanvasOptions): HTMLCanvasElement
13 | getData(rounded?: boolean): Cropper.Data
14 | getImageData(): Cropper.ImageData
15 | move(offsetX: number, offsetY?: number): Cropper
16 | moveTo(x: number, y?: number): Cropper
17 | replace(url: string, onlyColorChanged?: boolean): Cropper
18 | reset(): Cropper
19 | rotate(degree: number): Cropper
20 | rotateTo(degree: number): Cropper
21 | scale(scaleX: number, scaleY?: number): Cropper
22 | scaleX(scaleX: number): Cropper
23 | scaleY(scaleY: number): Cropper
24 | setAspectRatio(aspectRatio: number): Cropper
25 | setCanvasData(data: Cropper.SetCanvasDataOptions): Cropper
26 | setCropBoxData(data: Cropper.SetCropBoxDataOptions): Cropper
27 | setData(data: Cropper.SetDataOptions): Cropper
28 | setDragMode(dragMode: Cropper.DragMode): Cropper
29 | zoom(ratio: number): Cropper
30 | zoomTo(ratio: number, pivot?: { x: number; y: number }): Cropper
31 |
32 | flipX(): void
33 | flipY(): void
34 | }
35 |
--------------------------------------------------------------------------------
/tsconfig.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.node.json",
3 | "include": ["vite.config.*", "vitest.config.*", "cypress.config.*"],
4 | "compilerOptions": {
5 | "composite": true,
6 | "types": ["node"]
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@vue/tsconfig/tsconfig.web.json",
3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
4 | "compilerOptions": {
5 | "baseUrl": ".",
6 | "paths": {
7 | "@/*": ["./src/*"]
8 | }
9 | },
10 |
11 | "references": [
12 | {
13 | "path": "./tsconfig.config.json"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'url'
2 | import { resolve } from 'path'
3 | import { defineConfig } from 'vite'
4 | import vue from '@vitejs/plugin-vue'
5 | import dts from 'vite-plugin-dts'
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [
10 | dts({
11 | staticImport: true,
12 | // skipDiagnostics: false,
13 | // logDiagnostics: true,
14 | insertTypesEntry: true,
15 | rollupTypes: true
16 | }),
17 | vue()
18 | ],
19 | resolve: {
20 | alias: {
21 | '@': fileURLToPath(new URL('./src', import.meta.url))
22 | }
23 | },
24 | build: {
25 | lib: {
26 | entry: resolve(__dirname, 'src/index.ts'),
27 | name: 'VueCropper',
28 | fileName: format => `index.${format}.js`
29 | },
30 | rollupOptions: {
31 | // 确保外部化处理那些你不想打包进库的依赖
32 | external: ['vue', 'cropperjs'],
33 | output: {
34 | // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
35 | globals: {
36 | vue: 'Vue',
37 | cropperjs: 'CropperJs'
38 | }
39 | }
40 | }
41 | }
42 | })
43 |
--------------------------------------------------------------------------------