cropper-next-vue 0.1.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 +15 -0
- package/README.md +278 -0
- package/dist/cropper-next-vue.cjs +1 -0
- package/dist/cropper-next-vue.mjs +1009 -0
- package/dist/index.css +1 -0
- package/dist/types/changeImgSize.d.ts +5 -0
- package/dist/types/common.d.ts +62 -0
- package/dist/types/config.d.ts +7 -0
- package/dist/types/conversion.d.ts +8 -0
- package/dist/types/exif.d.ts +2 -0
- package/dist/types/filter/index.d.ts +4 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/interface.d.ts +74 -0
- package/dist/types/layoutBox.d.ts +3 -0
- package/dist/types/loading.d.ts +14 -0
- package/dist/types/touch/index.d.ts +25 -0
- package/dist/types/vue-cropper.vue.d.ts +66 -0
- package/dist/types/watchEvents.d.ts +12 -0
- package/package.json +84 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# cropper-next-vue
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
[中文](#中文) | [English](#english)
|
|
9
|
+
|
|
10
|
+
## 中文
|
|
11
|
+
|
|
12
|
+
`cropper-next-vue` 是一个独立发布的 Vue 3 图片裁剪库,重点处理这些能力:
|
|
13
|
+
|
|
14
|
+
- 图片旋转后的边界判断
|
|
15
|
+
- 图片限制在截图框内或容器内
|
|
16
|
+
- 高分屏导出
|
|
17
|
+
- 实时预览
|
|
18
|
+
- 独立的 npm 包构建
|
|
19
|
+
- 独立的文档站构建
|
|
20
|
+
|
|
21
|
+
在线预览:
|
|
22
|
+
|
|
23
|
+
- [https://cropper-next-vue.vercel.app/](https://cropper-next-vue.vercel.app/)
|
|
24
|
+
|
|
25
|
+
### 安装
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install cropper-next-vue
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
yarn add cropper-next-vue
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 使用
|
|
36
|
+
|
|
37
|
+
组件内引入:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import 'cropper-next-vue/style.css'
|
|
41
|
+
import { VueCropper } from 'cropper-next-vue'
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
全局注册:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { createApp } from 'vue'
|
|
48
|
+
import App from './App.vue'
|
|
49
|
+
import CropperNextVue from 'cropper-next-vue'
|
|
50
|
+
import 'cropper-next-vue/style.css'
|
|
51
|
+
|
|
52
|
+
const app = createApp(App)
|
|
53
|
+
app.use(CropperNextVue)
|
|
54
|
+
app.mount('#app')
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
基础示例:
|
|
58
|
+
|
|
59
|
+
```vue
|
|
60
|
+
<template>
|
|
61
|
+
<VueCropper
|
|
62
|
+
:img="img"
|
|
63
|
+
:crop-layout="{ width: 200, height: 200 }"
|
|
64
|
+
:center-box="true"
|
|
65
|
+
/>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
import { ref } from 'vue'
|
|
70
|
+
import { VueCropper } from 'cropper-next-vue'
|
|
71
|
+
import 'cropper-next-vue/style.css'
|
|
72
|
+
|
|
73
|
+
const img = ref('https://example.com/demo.jpg')
|
|
74
|
+
</script>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 本地开发
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# 文档站开发
|
|
81
|
+
pnpm run dev
|
|
82
|
+
|
|
83
|
+
# 只构建 npm 包
|
|
84
|
+
pnpm run build:lib
|
|
85
|
+
|
|
86
|
+
# 只构建文档站
|
|
87
|
+
pnpm run build:docs
|
|
88
|
+
|
|
89
|
+
# 同时构建 npm 包和文档站
|
|
90
|
+
pnpm run build
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 质量门
|
|
94
|
+
|
|
95
|
+
- `pnpm run typecheck`
|
|
96
|
+
- `pnpm run test:coverage`
|
|
97
|
+
- `pnpm run build:lib`
|
|
98
|
+
- `pnpm run check`
|
|
99
|
+
|
|
100
|
+
覆盖率阈值定义在 [vitest.config.ts](/Users/bytedance/projects/cropper-next/vitest.config.ts):
|
|
101
|
+
|
|
102
|
+
- `lines >= 70`
|
|
103
|
+
- `functions >= 70`
|
|
104
|
+
- `branches >= 60`
|
|
105
|
+
- `statements >= 70`
|
|
106
|
+
|
|
107
|
+
### 构建输出
|
|
108
|
+
|
|
109
|
+
- npm 包输出到 `dist/`
|
|
110
|
+
- 文档站输出到 `docs-dist/`
|
|
111
|
+
|
|
112
|
+
发布前建议执行:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pnpm run build:lib
|
|
116
|
+
pnpm pack --pack-destination /tmp
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
发布 npm 可直接使用:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pnpm run release:npm -- patch
|
|
123
|
+
pnpm run release:npm -- minor
|
|
124
|
+
pnpm run release:npm -- 0.2.0
|
|
125
|
+
pnpm run release:npm -- patch --tag next
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
这个脚本会依次执行:
|
|
129
|
+
|
|
130
|
+
- 更新 `package.json` 版本号
|
|
131
|
+
- 运行 `pnpm run check`
|
|
132
|
+
- 重新构建 lib 产物
|
|
133
|
+
- 发布到 npm
|
|
134
|
+
|
|
135
|
+
默认要求 git 工作区干净,并且当前机器已经完成 `npm login`。
|
|
136
|
+
|
|
137
|
+
### 开源协作
|
|
138
|
+
|
|
139
|
+
- 许可证:`ISC`
|
|
140
|
+
- Node 版本要求:`22.x`
|
|
141
|
+
- 提交前建议执行:`pnpm run check`
|
|
142
|
+
- 贡献说明见 [CONTRIBUTING.md](/Users/bytedance/projects/cropper-next/CONTRIBUTING.md)
|
|
143
|
+
- 行为约定见 [CODE_OF_CONDUCT.md](/Users/bytedance/projects/cropper-next/CODE_OF_CONDUCT.md)
|
|
144
|
+
|
|
145
|
+
## English
|
|
146
|
+
|
|
147
|
+
`cropper-next-vue` is a standalone Vue 3 image cropper focused on:
|
|
148
|
+
|
|
149
|
+
- boundary handling after rotation
|
|
150
|
+
- keeping the image inside the crop box or wrapper
|
|
151
|
+
- high-DPI export
|
|
152
|
+
- realtime preview
|
|
153
|
+
- standalone npm package output
|
|
154
|
+
- standalone docs site output
|
|
155
|
+
|
|
156
|
+
Live preview:
|
|
157
|
+
|
|
158
|
+
- [https://cropper-next-vue.vercel.app/](https://cropper-next-vue.vercel.app/)
|
|
159
|
+
|
|
160
|
+
### Install
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm install cropper-next-vue
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
yarn add cropper-next-vue
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Usage
|
|
171
|
+
|
|
172
|
+
Import inside a component:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import 'cropper-next-vue/style.css'
|
|
176
|
+
import { VueCropper } from 'cropper-next-vue'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Global registration:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
import { createApp } from 'vue'
|
|
183
|
+
import App from './App.vue'
|
|
184
|
+
import CropperNextVue from 'cropper-next-vue'
|
|
185
|
+
import 'cropper-next-vue/style.css'
|
|
186
|
+
|
|
187
|
+
const app = createApp(App)
|
|
188
|
+
app.use(CropperNextVue)
|
|
189
|
+
app.mount('#app')
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Basic example:
|
|
193
|
+
|
|
194
|
+
```vue
|
|
195
|
+
<template>
|
|
196
|
+
<VueCropper
|
|
197
|
+
:img="img"
|
|
198
|
+
:crop-layout="{ width: 200, height: 200 }"
|
|
199
|
+
:center-box="true"
|
|
200
|
+
/>
|
|
201
|
+
</template>
|
|
202
|
+
|
|
203
|
+
<script setup lang="ts">
|
|
204
|
+
import { ref } from 'vue'
|
|
205
|
+
import { VueCropper } from 'cropper-next-vue'
|
|
206
|
+
import 'cropper-next-vue/style.css'
|
|
207
|
+
|
|
208
|
+
const img = ref('https://example.com/demo.jpg')
|
|
209
|
+
</script>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Local development
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# docs dev server
|
|
216
|
+
pnpm run dev
|
|
217
|
+
|
|
218
|
+
# build npm package only
|
|
219
|
+
pnpm run build:lib
|
|
220
|
+
|
|
221
|
+
# build docs site only
|
|
222
|
+
pnpm run build:docs
|
|
223
|
+
|
|
224
|
+
# build both package and docs
|
|
225
|
+
pnpm run build
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Quality gates
|
|
229
|
+
|
|
230
|
+
- `pnpm run typecheck`
|
|
231
|
+
- `pnpm run test:coverage`
|
|
232
|
+
- `pnpm run build:lib`
|
|
233
|
+
- `pnpm run check`
|
|
234
|
+
|
|
235
|
+
Coverage thresholds are defined in [vitest.config.ts](/Users/bytedance/projects/cropper-next/vitest.config.ts):
|
|
236
|
+
|
|
237
|
+
- `lines >= 70`
|
|
238
|
+
- `functions >= 70`
|
|
239
|
+
- `branches >= 60`
|
|
240
|
+
- `statements >= 70`
|
|
241
|
+
|
|
242
|
+
### Build outputs
|
|
243
|
+
|
|
244
|
+
- npm package output goes to `dist/`
|
|
245
|
+
- docs site output goes to `docs-dist/`
|
|
246
|
+
|
|
247
|
+
Recommended before publishing:
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
pnpm run build:lib
|
|
251
|
+
pnpm pack --pack-destination /tmp
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Release to npm:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
pnpm run release:npm -- patch
|
|
258
|
+
pnpm run release:npm -- minor
|
|
259
|
+
pnpm run release:npm -- 0.2.0
|
|
260
|
+
pnpm run release:npm -- patch --tag next
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The release script will:
|
|
264
|
+
|
|
265
|
+
- update the version in `package.json`
|
|
266
|
+
- run `pnpm run check`
|
|
267
|
+
- rebuild the library output
|
|
268
|
+
- publish the package to npm
|
|
269
|
+
|
|
270
|
+
It requires a clean git working tree and a valid `npm login` session by default.
|
|
271
|
+
|
|
272
|
+
### Open source collaboration
|
|
273
|
+
|
|
274
|
+
- License: `ISC`
|
|
275
|
+
- Required Node version: `22.x`
|
|
276
|
+
- Recommended pre-commit check: `pnpm run check`
|
|
277
|
+
- Contribution guide: [CONTRIBUTING.md](/Users/bytedance/projects/cropper-next/CONTRIBUTING.md)
|
|
278
|
+
- Code of conduct: [CODE_OF_CONDUCT.md](/Users/bytedance/projects/cropper-next/CODE_OF_CONDUCT.md)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const h=require("vue"),At={};At.getData=n=>new Promise((t,e)=>{let r={};qt(n).then(s=>{r.arrayBuffer=s,r.orientation=Kt(s),t(r)}).catch(s=>{e(s)})});function qt(n){let t=null;return new Promise((e,r)=>{if(n.src)if(/^data\:/i.test(n.src))t=Zt(n.src),e(t);else if(/^blob\:/i.test(n.src)){var s=new FileReader;s.onload=function(l){t=l.target?.result??null,e(t)},Gt(n.src,function(l){s.readAsArrayBuffer(l)})}else{var a=new XMLHttpRequest;a.onload=function(){if(this.status==200||this.status===0)t=a.response,e(t);else throw"Could not load image";a=null},a.open("GET",n.src,!0),a.responseType="arraybuffer",a.send(null)}else r("img error")})}function Gt(n,t){var e=new XMLHttpRequest;e.open("GET",n,!0),e.responseType="blob",e.onload=function(){(this.status==200||this.status===0)&&t(this.response)},e.send()}function Zt(n){n=n.replace(/^data\:([^\;]+)\;base64,/gim,"");for(var t=atob(n),e=t.length,r=new ArrayBuffer(e),s=new Uint8Array(r),a=0;a<e;a++)s[a]=t.charCodeAt(a);return r}function Jt(n,t,e){var r="",s;for(s=t,e+=t;s<e;s++)r+=String.fromCharCode(n.getUint8(s));return r}function Kt(n){var t=new DataView(n),e=t.byteLength,r,s,a,l,u,p,d,g,o,b;if(t.getUint8(0)===255&&t.getUint8(1)===216)for(o=2;o<e;){if(t.getUint8(o)===255&&t.getUint8(o+1)===225){d=o;break}o++}if(d&&(s=d+4,a=d+10,Jt(t,s,4)==="Exif"&&(p=t.getUint16(a),u=p===18761,(u||p===19789)&&t.getUint16(a+2,u)===42&&(l=t.getUint32(a+4,u),l>=8&&(g=a+l)))),g){for(e=t.getUint16(g,u),b=0;b<e;b++)if(o=g+b*12+2,t.getUint16(o,u)===274){o+=8,r=t.getUint16(o,u);break}}return r}class Qt{ctx;img;handle;constructor(){this.ctx=null,this.img=null,this.handle={1:t=>(t&&this.ctx&&this.img&&(t.width=this.img.width,t.height=this.img.height),t),2:t=>(t&&this.ctx&&this.img&&(t.width=this.img.width,t.height=this.img.height,this.ctx.translate(this.img.width,0),this.ctx.scale(-1,1)),t),3:t=>(t&&this.ctx&&this.img&&(t.width=this.img.width,t.height=this.img.height,this.ctx.translate(this.img.width/2,this.img.height/2),this.ctx.rotate(180*Math.PI/180),this.ctx.translate(-this.img.width/2,-this.img.height/2)),t),4:t=>(t&&this.ctx&&this.img&&(t.width=this.img.width,t.height=this.img.height,this.ctx.translate(0,this.img.height),this.ctx.scale(1,-1)),t),5:t=>(t&&this.ctx&&this.img&&(t.width=this.img.height,t.height=this.img.width,this.ctx.rotate(.5*Math.PI),this.ctx.scale(1,-1)),t),6:t=>(t&&this.ctx&&this.img&&(t.width=this.img.height,t.height=this.img.width,this.ctx.translate(this.img.height/2,this.img.width/2),this.ctx.rotate(90*Math.PI/180),this.ctx.translate(-this.img.width/2,-this.img.height/2)),t),7:t=>(t&&this.ctx&&this.img&&(t.width=this.img.height,t.height=this.img.width,this.ctx.rotate(.5*Math.PI),this.ctx.translate(this.img.width,-this.img.height),this.ctx.scale(-1,1)),t),8:t=>(t&&this.ctx&&this.img&&(t.width=this.img.height,t.height=this.img.width,this.ctx.translate(this.img.height/2,this.img.width/2),this.ctx.rotate(-90*Math.PI/180),this.ctx.translate(-this.img.width/2,-this.img.height/2)),t)}}render(t,e,r){if(this.ctx=e.getContext("2d"),this.img=t,this.handle[r]){const s=this.handle[r](e);return this.ctx=s.getContext("2d"),this.ctx.drawImage(this.img,0,0,this.img.width,this.img.height),this.ctx.restore(),s}else return e.width=t.width,e.height=t.height,this.ctx.drawImage(this.img,0,0,this.img.width,this.img.height),e}}const te=(n,t,e)=>{let r="default";for(const a of Object.keys(pt))if(a===e){r=e;break}const s=pt[r];return s(n,t,e)},pt={contain:(n,t)=>{let e=1;const{width:r,height:s}={...n},a=t.width,l=t.height;return r>a&&(e=a/r),s*e>l&&(e=l/s),e},cover:(n,t)=>{let e=1;const{width:r,height:s}={...n},a=t.width,l=t.height;return e=a/r,s*e<l&&(e=l/s),e},default:(n,t,e)=>{let r=1;if(e==="original")return r;let{width:s,height:a}={...n};const l=t.width,u=t.height,p=e.split(" ");try{let d=p[0];if(d.search("px")!==-1&&(d=d.replace("px",""),s=parseFloat(d),r=s/n.width),d.search("%")!==-1&&(d=d.replace("%",""),s=parseFloat(d)/100*l,r=s/n.width),p.length===2&&d==="auto"){let g=p[1];g.search("px")!==-1&&(g=g.replace("px",""),a=parseFloat(g),r=a/n.height),g.search("%")!==-1&&(g=g.replace("%",""),a=parseFloat(g)/100*u,r=a/n.height)}}catch(d){console.error(d),r=1}return r}},ee=new Qt,K=async n=>new Promise((t,e)=>{const r=new Image;r.onload=()=>t(r),r.onerror=e,r.src=n,n.substr(0,4)!=="data"&&(r.crossOrigin="")}),ie=n=>At.getData(n),oe=(n,t,e)=>t&&ee.render(n,t,e),_=n=>{if(typeof navigator>"u")return["0","0","0"];const t=navigator.userAgent.split(" ");let e="",r=["0","0","0"];const s=new RegExp(n,"i");for(let a=0;a<t.length;a++)s.test(t[a])&&(e=t[a]);return e&&(r=e.split("/")[1].split(".")),r},re=n=>{if(Number(_("chrome")[0])>=81)return-1;if(Number(_("safari")[0])>=605){const e=_("version");if(Number(e[0])>13&&Number(e[1])>1)return-1}const t=typeof navigator<"u"?navigator.userAgent.toLowerCase().match(/cpu iphone os (.*?) like mac os/):null;if(t){const e=t[1].split("_").map(r=>Number(r));return e[0]>13||e[0]>=13&&e[1]>=4||Number(_("Firefox")[0])>=97?-1:n}return n},ne=(n,t,e)=>te(n,t,e),H=(n,t)=>{const{scale:e,imgStyle:r,layoutStyle:s,rotate:a}=n,l={width:e*r.width,height:e*r.height};let u=(s.width-l.width)/2,p=(s.height-l.height)/2;t&&(u=t.x,p=t.y);const d=((l.width-r.width)/2+u)/e,g=((l.height-r.height)/2+p)/e;return{imgExhibitionStyle:{width:`${r.width}px`,height:`${r.height}px`,transform:`scale(${e}, ${e}) translate3d(${d}px, ${g}px, 0px) rotateZ(${a}deg)`},imgAxis:{x:u,y:p,scale:e,rotate:a}}},se=(n,t,e)=>new Promise((r,s)=>{n.toBlob(a=>{if(a){r(a);return}s(new Error("canvas toBlob failed"))},`image/${t}`,e)}),ae=async n=>n?/\.(gif|jpg|jpeg|png|bmp|GIF|JPG|PNG|WEBP|webp)$/.test(n.name)?new Promise((t,e)=>{const r=new FileReader;r.onload=s=>{let a="";const l=s.target;typeof l.result=="object"&&l.result?a=window.URL.createObjectURL(new Blob([l.result])):a=l.result,t(a)},r.onerror=e,r.readAsArrayBuffer(n)}):(alert("图片类型必须是.gif,jpeg,jpg,png,bmp,webp中的一种"),""):"",he=(n,t,e=0,r=1)=>{const s=document.createElement("canvas"),a=s.getContext("2d");let{width:l,height:u}=t,p=0,d=0,g=0;return l=l*r,u=u*r,s.width=l,s.height=u,e&&(g=Math.ceil(Math.sqrt(l*l+u*u)),s.width=g,s.height=g,a.translate(g/2,g/2),a.rotate(e*Math.PI/180),p=-g/2+(g-l)/2,d=-g/2+(g-u)/2),a.drawImage(n,p,d,l,u),a.restore(),s},ft=async n=>{const{type:t="base64",url:e,imgLayout:r,imgAxis:s,cropAxis:a,cropLayout:l,outputType:u,outputSize:p=1,full:d=!0,cropping:g}=n,o=document.createElement("canvas"),b=o.getContext("2d");let v;try{v=await K(e)}catch(y){console.log(y),v=new Image}return new Promise((y,A)=>{try{const L=he(v,r,s.rotate,s.scale);let M=s.x-a.x,S=s.y-a.y,$=l.width,B=l.height;g||($=L.width,B=L.height,M=0,S=0),s.rotate&&g&&(M-=(L.width-r.width*s.scale)/2,S-=(L.height-r.height*s.scale)/2);const E=d&&typeof window<"u"?Math.max(1,window.devicePixelRatio||1):1;if(o.width=Math.round($*E),o.height=Math.round(B*E),b.scale(E,E),b.drawImage(L,M,S,L.width,L.height),b.restore(),t==="blob"){se(o,u,p).then(y).catch(A);return}const w=o.toDataURL(`image/${u}`,p);y(w)}catch(L){A(L)}})},ce=(n,t,e,r)=>{const s={left:0,right:0,top:0,bottom:0,scale:1},a=e.rotate*Math.PI/180,u=[{x:n.x,y:n.y},{x:n.x+t.width,y:n.y},{x:n.x+t.width,y:n.y+t.height},{x:n.x,y:n.y+t.height}].map(w=>W(w,-a)),p=Math.max(...u.map(w=>w.x))-Math.min(...u.map(w=>w.x)),d=Math.max(...u.map(w=>w.y))-Math.min(...u.map(w=>w.y)),g=Math.max(e.scale,p/r.width,d/r.height),o=r.width*g,b=r.height*g,v=o/2,y=b/2,A=Math.max(...u.map(w=>w.x-v)),L=Math.min(...u.map(w=>w.x+v)),M=Math.max(...u.map(w=>w.y-y)),S=Math.min(...u.map(w=>w.y+y)),$={x:e.x+v,y:e.y+y},B=W($,-a),E=W({x:wt(B.x,A,L),y:wt(B.y,M,S)},a);return s.scale=g,s.left=E.x-v,s.right=E.x-v,s.top=E.y-y,s.bottom=E.y-y,s},wt=(n,t,e)=>Math.min(Math.max(n,t),e),W=(n,t)=>({x:n.x*Math.cos(t)-n.y*Math.sin(t),y:n.x*Math.sin(t)+n.y*Math.cos(t)}),Y=(n,t,e,r)=>{let s="",a="";const l=ce(n,t,e,r),u=l.scale,p=.5;return e.x>l.left+p?s="left":e.x<l.right-p&&(s="right"),e.y>l.top+p?a="top":e.y<l.bottom-p&&(a="bottom"),{landscape:s,portrait:a,scale:u,boundary:l,imgAxis:e}},le={easeInOut:(n,t,e,r)=>(n=n/r*2,n<1?e/2*n*n+t:-e/2*(--n*(n-2)-1)+t)},k=(n,t,e,r)=>{if(e<=0||n===t)return r&&r(t),()=>0;let s=0;const a=Math.max(1,Math.ceil(e/17));let l=0;const u=()=>{const p=le.easeInOut(s,n,t-n,a);s++,s<=a?(r&&r(p),l=requestAnimationFrame(u)):r&&r(t)};return u(),()=>l};let q=!1,G=.2,xt="";const ue=typeof navigator<"u"&&navigator.userAgent.indexOf("Firefox")>-1,Et=typeof window<"u"&&(!!window.ActiveXObject||"ActiveXObject"in window),de=(n,t,e)=>{let r=n.deltaY||n.wheelDelta,s=t;ue&&(r=r*30),Et&&(r=-r);const l=G/e.width*r;l<0&&(s+=Math.abs(l)),l>0&&t>Math.abs(l)&&(s-=Math.abs(l));const u=l<0?"add":"reduce";return u!==xt&&(xt=u,G=.2),q||window.setTimeout(()=>{q=!1,G+=.01},100),q=!0,s},me=(n,t)=>n*t,Z=typeof document<"u"&&"onwheel"in document.createElement("div")?"wheel":"mousewheel",yt=.2,J=100;class ge{handlers;constructor(){this.handlers=new Map}addHandler(t,e){const r=this.handlers.get(t);let s=[];r&&(s=[...r]),s.push(e),this.handlers.set(t,s)}fire(t){const e=this.handlers.get(t.type);e&&e.forEach(r=>{r(t)})}removeHandler(t,e){const r=this.handlers.get(t);if(!r)return;let s=0;for(const a=r.length;s<a&&r[s]!==e;s++);r.splice(s,1),this.handlers.set(t,r)}}const vt=typeof window<"u"&&"ontouchstart"in window,bt=typeof window<"u"&&"onmouseup"in window;class Lt{element;pre;watcher;touches;constructor(t){this.element=t,this.touches=null,this.pre={x:0,y:0},this.watcher=new ge}fire(t){this.watcher.fire(t)}getAxis(t){return{x:t.clientX,y:t.clientY}}getTouchPair(t){return t.length<2?null:[t[0],t[1]]}start(t){t.preventDefault(),t.which===1&&(this.pre=this.getAxis(t),this.fire({type:"down",event:t}),window.addEventListener("mousemove",this.move),window.addEventListener("mouseup",this.stop))}startTouch(t){const e=t.touches[0];if(!e)return;this.pre={x:e.clientX,y:e.clientY},this.fire({type:"down",event:t});const r=this.getTouchPair(t.touches);r?(this.touches=r,window.addEventListener("touchmove",this.scaleTouch,{passive:!1}),window.removeEventListener("touchmove",this.moveTouch)):(window.addEventListener("touchmove",this.moveTouch,{passive:!1}),window.removeEventListener("touchmove",this.scaleTouch)),window.addEventListener("touchend",this.stopTouch)}move(t){t.preventDefault();const e=this.getAxis(t);this.fire({type:"down-to-move",event:t,change:{x:e.x-this.pre.x,y:e.y-this.pre.y}}),this.pre=e}moveTouch(t){t.preventDefault();const e=t.touches[0];e&&(this.fire({type:"down-to-move",event:t,change:{x:e.clientX-this.pre.x,y:e.clientY-this.pre.y}}),this.pre={x:e.clientX,y:e.clientY})}getLen(t){return Math.sqrt(t.x*t.x+t.y*t.y)}getScale(t,e){const r={x:t[1].clientX-t[0].clientX,y:t[1].clientY-t[0].clientY},s={x:e[1].clientX-e[0].clientX,y:e[1].clientY-e[0].clientY};return this.getLen(r)/this.getLen(s)}scaleTouch(t){t.preventDefault();const e=this.getTouchPair(t.touches);!e||!this.touches||(this.fire({type:"down-to-scale",event:t,scale:this.getScale(e,this.touches)}),this.touches=e)}stop(t){this.fire({type:"up",event:t}),window.removeEventListener("mousemove",this.move),window.removeEventListener("mouseup",this.stop)}stopTouch(t){this.fire({type:"up",event:t}),this.touches=null,window.removeEventListener("touchmove",this.moveTouch),window.removeEventListener("touchmove",this.scaleTouch),window.removeEventListener("touchend",this.stopTouch)}on(t,e){this.watcher.addHandler(t,e),!(t!=="down-to-move"&&t!=="down-to-scale"&&t!=="up")&&(bt&&(this.start=this.start.bind(this),this.move=this.move.bind(this),this.stop=this.stop.bind(this),this.element.addEventListener("mousedown",this.start)),vt&&(this.startTouch=this.startTouch.bind(this),this.moveTouch=this.moveTouch.bind(this),this.scaleTouch=this.scaleTouch.bind(this),this.stopTouch=this.stopTouch.bind(this),this.element.addEventListener("touchstart",this.startTouch,{passive:!1})))}off(t,e){this.watcher.removeHandler(t,e),!(t!=="down-to-move"&&t!=="down-to-scale"&&t!=="up")&&(bt&&(this.element.removeEventListener("mousedown",this.start),window.removeEventListener("mousemove",this.move),window.removeEventListener("mouseup",this.stop)),vt&&(this.element.removeEventListener("touchstart",this.startTouch),window.removeEventListener("touchmove",this.moveTouch),window.removeEventListener("touchmove",this.scaleTouch),window.removeEventListener("touchend",this.stopTouch)))}}const pe=h.defineComponent({name:"cropper-loading",props:{isVisible:{type:Boolean,default:!1}},render(){const{$slots:n}=this;return this.isVisible?h.createVNode("section",{class:"cropper-loading"},[n.default?n.default():h.createVNode("p",{class:"cropper-loading-spin"},[h.createVNode("i",null,[h.createVNode("svg",{viewBox:"0 0 1024 1024",focusable:"false","data-icon":"loading",width:"1.5em",height:"1.5em",fill:"currentColor","aria-hidden":"true"},[h.createVNode("path",{d:"M988 548c-19.9 0-36-16.1-36-36 0-59.4-11.6-117-34.6-171.3a440.45 440.45 0 0 0-94.3-139.9 437.71 437.71 0 0 0-139.9-94.3C629 83.6 571.4 72 512 72c-19.9 0-36-16.1-36-36s16.1-36 36-36c69.1 0 136.2 13.5 199.3 40.3C772.3 66 827 103 874 150c47 47 83.9 101.8 109.7 162.7 26.7 63.1 40.2 130.2 40.2 199.3.1 19.9-16 36-35.9 36z"},null)])]),h.createVNode("span",null,null)])]):""}}),fe={key:0,class:"cropper-box cropper-fade-in"},we=["src"],xe=["src"],ye={key:2,class:"drag"},Q=h.defineComponent({__name:"vue-cropper",props:{img:{default:""},wrapper:{default:()=>({width:300,height:300})},cropLayout:{default:()=>({width:200,height:200})},color:{default:"#fff"},filter:{type:[Function,null],default:null},outputType:{default:"png"},outputSize:{default:1},full:{type:Boolean,default:!0},mode:{default:"cover"},cropColor:{default:"#fff"},defaultRotate:{default:0},centerBox:{type:Boolean,default:!1},centerWrapper:{type:Boolean,default:!1},centerBoxDelay:{default:J},centerWrapperDelay:{default:J}},emits:["img-load","img-upload","real-time","realTime"],setup(n,{expose:t,emit:e}){const r=n,s=h.ref(),a=h.ref(),l=h.ref(),u=e,p=h.ref(!1),d=h.ref("");let g=null;const o=h.reactive({imgLayout:{width:0,height:0},wrapLayout:{width:0,height:0},imgAxis:{x:0,y:0,scale:0,rotate:0},imgExhibitionStyle:{width:"",height:"",transform:""},cropAxis:{x:0,y:0},cropExhibitionStyle:{div:{},img:{}}}),b=h.ref(!1),v=h.ref(!0);let y=null,A=null;const L=h.ref(null),M=h.ref(!1),{img:S,filter:$,mode:B,defaultRotate:E,outputType:w,outputSize:tt,full:et,centerBox:P,cropLayout:Ae,centerWrapper:X,centerBoxDelay:Ct,centerWrapperDelay:St}=h.toRefs(r);let N=0;const it=i=>{if(typeof i=="number")return i;const c=Number.parseFloat(i);return Number.isFinite(c)?c:0},ot=i=>typeof i=="number"?`${i}px`:i,Mt=h.computed(()=>({...r.wrapper,width:ot(r.wrapper.width),height:ot(r.wrapper.height)})),T=h.computed(()=>({width:it(r.cropLayout.width),height:it(r.cropLayout.height)})),rt=()=>P.value?Math.max(0,Ct.value):X.value?Math.max(0,St.value):J;h.watch(S,i=>{i&&i!==d.value&&F(i)}),h.watch(d,i=>{i&&(h.nextTick(()=>{Ut()}),v.value&&h.nextTick(()=>{lt()}))}),h.watch(v,i=>{i&&h.nextTick(()=>{lt()})}),h.watch($,()=>{p.value=!0,F(S.value)}),h.watch(B,()=>{p.value=!0,F(S.value)}),h.watch(E,i=>{O(i)}),h.watch(P,i=>{i&&I()}),h.watch(X,i=>{i&&I()});const U=i=>{u("img-load",i)},It=i=>{u("img-upload",i)},$t=()=>{if(!d.value||!v.value)return null;const i=o.imgAxis.scale,c=(o.imgLayout.width*(i-1)/2+(o.imgAxis.x-o.cropAxis.x))/i,m=(o.imgLayout.height*(i-1)/2+(o.imgAxis.y-o.cropAxis.y))/i,f=`scale(${i}, ${i}) translate3d(${c}px, ${m}px, 0) rotateZ(${o.imgAxis.rotate}deg)`,x=T.value.width,C=T.value.height;return{w:x,h:C,url:d.value,img:{width:`${o.imgLayout.width}px`,height:`${o.imgLayout.height}px`,transform:f},html:`<div class="show-preview" style="width: ${x}px; height: ${C}px; overflow: hidden"><div style="width: ${x}px; height: ${C}px"><img src="${d.value}" style="width: ${o.imgLayout.width}px; height: ${o.imgLayout.height}px; transform: ${f}"></div></div>`}},Bt=()=>{const i=$t();i&&(u("real-time",i),u("realTime",i))},R=()=>{N&&cancelAnimationFrame(N),N=requestAnimationFrame(()=>{N=0,Bt()})},nt=i=>{i.preventDefault();const c=i.dataTransfer;b.value=!1,ae(c.files[0]).then(m=>{m&&It(m)})},st=i=>{i.preventDefault(),b.value=!0},at=i=>{i.preventDefault(),b.value=!1},F=async i=>{p.value=!0,d.value="",g=null;let c;try{c=await K(i),U({type:"success",message:"图片加载成功"})}catch(C){return U({type:"error",message:`图片加载失败${C}`}),p.value=!1,!1}let m={orientation:-1};try{m=await ie(c)}catch{m.orientation=1}let f=m.orientation||-1;f=re(f);let x=document.createElement("canvas");try{x=await oe(c,x,f)??x}catch(C){console.error(C)}g=x,Nt()},Nt=()=>{if($.value){let i=g;i=$.value(i)??i,g=i}Rt()},Rt=()=>{if(g)try{g.toBlob(async i=>{if(i){URL.revokeObjectURL(d.value);const c=URL.createObjectURL(i);let m=1;try{m=await Pt(c)}catch(x){console.error(x)}const f=H({scale:m,imgStyle:{...o.imgLayout},layoutStyle:{...o.wrapLayout},rotate:E.value});o.imgExhibitionStyle=f.imgExhibitionStyle,o.imgAxis=f.imgAxis,d.value=c,v.value&&Ot(),I(),R(),p.value=!1}else d.value="",p.value=!1},`image/${w.value}`,1)}catch(i){console.error(i),p.value=!1}},Pt=async i=>{let c;try{c=await K(i),U({type:"success",message:"图片加载成功"})}catch(f){return U({type:"error",message:`图片加载失败${f}`}),p.value=!1,1}const m={width:0,height:0};return s.value&&(m.width=Number((window.getComputedStyle(s.value).width||"").replace("px","")),m.height=Number((window.getComputedStyle(s.value).height||"").replace("px",""))),o.imgLayout={width:c.width,height:c.height},o.wrapLayout={...m},ne({...o.imgLayout},m,B.value)},Dt=()=>{Et?window.addEventListener(Z,j):window.addEventListener(Z,j,{passive:!1})},kt=()=>{window.removeEventListener(Z,j)},j=i=>{i.preventDefault();const c=de(i,o.imgAxis.scale,o.imgLayout);z(c)};h.onMounted(()=>{r.img?F(r.img):d.value="";const i=s.value;i.addEventListener("dragover",st,!1),i.addEventListener("dragend",at,!1),i.addEventListener("drop",nt,!1)}),h.onUnmounted(()=>{N&&(cancelAnimationFrame(N),N=0),s.value?.removeEventListener("drop",nt,!1),s.value?.removeEventListener("dragover",st,!1),s.value?.removeEventListener("dragend",at,!1),ct(),ut()});const z=(i,c=!1)=>{const m={x:o.imgAxis.x,y:o.imgAxis.y};c||(m.x-=o.imgLayout.width*(i-o.imgAxis.scale)/2,m.y-=o.imgLayout.height*(i-o.imgAxis.scale)/2);const f=H({scale:i,imgStyle:{...o.imgLayout},layoutStyle:{...o.wrapLayout},rotate:o.imgAxis.rotate},m);o.imgExhibitionStyle=f.imgExhibitionStyle,o.imgAxis=f.imgAxis,R(),L.value!==null&&clearTimeout(L.value);const x=rt();L.value=setTimeout(()=>{I()},x)},ht=i=>{if(i.change){const c={x:i.change.x+o.imgAxis.x,y:i.change.y+o.imgAxis.y};if(P.value){const m=Y({...o.cropAxis},{...T.value},{...o.imgAxis},{...o.imgLayout});(m.landscape!==""||m.portrait!=="")&&(c.x=o.imgAxis.x+i.change.x*yt,c.y=o.imgAxis.y+i.change.y*yt)}D(c)}},D=i=>{const c=H({scale:o.imgAxis.scale,imgStyle:{...o.imgLayout},layoutStyle:{...o.wrapLayout},rotate:o.imgAxis.rotate},i);o.imgExhibitionStyle=c.imgExhibitionStyle,o.imgAxis=c.imgAxis,R()},I=()=>{if(M.value=!1,!P.value&&!X.value)return;const i=rt();let c;P.value?c=Y({...o.cropAxis},{...T.value},{...o.imgAxis},{...o.imgLayout}):c=Y({x:0,y:0},{...o.wrapLayout},{...o.imgAxis},{...o.imgLayout}),o.imgAxis.scale<c.scale&&k(o.imgAxis.scale,c.scale,i,m=>{z(m,!0)}),c.landscape==="left"&&k(o.imgAxis.x,c.boundary.left,i,m=>{D({x:m,y:o.imgAxis.y})}),c.landscape==="right"&&k(o.imgAxis.x,c.boundary.right,i,m=>{D({x:m,y:o.imgAxis.y})}),c.portrait==="top"&&k(o.imgAxis.y,c.boundary.top,i,m=>{D({x:o.imgAxis.x,y:m})}),c.portrait==="bottom"&&k(o.imgAxis.y,c.boundary.bottom,i,m=>{D({x:o.imgAxis.x,y:m})}),R()},V=i=>{if(M.value=!0,i.scale){const c=me(i.scale,o.imgAxis.scale);z(c)}},Ut=()=>{ct();const i=a.value;y=new Lt(i),y.on("down-to-move",ht),y.on("down-to-scale",V),y.on("up",I)},ct=()=>{y&&(y.off("down-to-move",ht),y.off("up",I),y.off("down-to-scale",V))},lt=()=>{ut();const i=l.value;A=new Lt(i),A.on("down-to-move",dt),A.on("down-to-scale",V),A.on("up",I),y=null},ut=()=>{A&&(A.off("down-to-move",dt),A.off("down-to-scale",V),A.off("up",I),A=null)},O=(i,c=!0)=>{const{x:m,y:f,scale:x}=o.imgAxis,C={x:m,y:f},gt=H({scale:x,imgStyle:{...o.imgLayout},layoutStyle:{...o.wrapLayout},rotate:i},C);o.imgExhibitionStyle=gt.imgExhibitionStyle,o.imgAxis=gt.imgAxis,R(),c&&d.value&&I()},Ft=(i="base64")=>{const c={type:i,outputType:w.value,outputSize:tt.value,full:et.value,url:d.value,imgAxis:{...o.imgAxis},imgLayout:{...o.imgLayout},cropLayout:{...T.value},cropAxis:{...o.cropAxis},cropping:v.value};return ft(c)},Vt=()=>ft({type:"blob",outputType:w.value,outputSize:tt.value,full:et.value,url:d.value,imgAxis:{...o.imgAxis},imgLayout:{...o.imgLayout},cropLayout:{...T.value},cropAxis:{...o.cropAxis},cropping:v.value}),Ot=i=>{const{width:c,height:m}=o.wrapLayout;let f=T.value.width,x=T.value.height;f=f<c?f:c,x=f<m?x:m;const C={x:(c-f)/2,y:(m-x)/2};mt(C)},dt=i=>{if(!M.value&&i.change){const c={x:i.change.x+o.cropAxis.x,y:i.change.y+o.cropAxis.y};mt(c)}},mt=i=>{const f=o.wrapLayout.width-T.value.width,x=o.wrapLayout.height-T.value.height;i.x<0&&(i.x=0),i.y<0&&(i.y=0),i.x>f&&(i.x=f),i.y>x&&(i.y=x),o.cropAxis=i,v.value=!0,R()},_t=()=>{O(o.imgAxis.rotate-90)},Ht=()=>{O(o.imgAxis.rotate+90)},Xt=()=>{O(0)},jt=()=>{const i=["cropper-drag-box"];return v.value&&i.push("cropper-modal"),i.join(" ")},zt=()=>{const i={width:`${T.value.width}px`,height:`${T.value.height}px`,transform:`translate3d(${o.cropAxis.x}px, ${o.cropAxis.y}px, 0)`};return o.cropExhibitionStyle.div=i,i},Wt=()=>{const i=o.imgAxis.scale,c=(o.imgLayout.width*(i-1)/2+(o.imgAxis.x-o.cropAxis.x))/i,m=(o.imgLayout.height*(i-1)/2+(o.imgAxis.y-o.cropAxis.y))/i,f={width:`${o.imgLayout.width}px`,height:`${o.imgLayout.height}px`,transform:`scale(${i}, ${i}) translate3d(${c}px, ${m}px, 0) rotateZ(${o.imgAxis.rotate}deg)`};return o.cropExhibitionStyle.img=f,f},Yt=h.useSlots();return t({getCropData:Ft,getCropBlob:Vt,rotateLeft:_t,rotateRight:Ht,rotateClear:Xt}),(i,c)=>(h.openBlock(),h.createElementBlock("section",{class:"vue-cropper",style:h.normalizeStyle(Mt.value),ref_key:"cropperRef",ref:s,onMouseover:Dt,onMouseout:kt},[d.value?(h.openBlock(),h.createElementBlock("section",fe,[h.createElementVNode("section",{class:"cropper-box-canvas",style:h.normalizeStyle(o.imgExhibitionStyle)},[h.createElementVNode("img",{src:d.value,alt:"cropper-next-vue"},null,8,we)],4),h.createElementVNode("section",{class:h.normalizeClass(jt()),ref_key:"cropperImg",ref:a},null,2)])):h.createCommentVNode("",!0),v.value&&d.value?(h.openBlock(),h.createElementBlock("section",{key:1,class:"cropper-crop-box cropper-fade-in",style:h.normalizeStyle(zt())},[h.createElementVNode("span",{class:"cropper-view-box",style:h.normalizeStyle({outlineColor:n.cropColor})},[h.unref(S)?(h.openBlock(),h.createElementBlock("img",{key:0,src:d.value,style:h.normalizeStyle(Wt()),alt:"cropper-img"},null,12,xe)):h.createCommentVNode("",!0)],4),h.createElementVNode("span",{class:"cropper-face cropper-move",ref_key:"cropperBox",ref:l},null,512)],4)):h.createCommentVNode("",!0),b.value?(h.openBlock(),h.createElementBlock("section",ye,[h.renderSlot(i.$slots,"drag",{},()=>[c[0]||(c[0]=h.createElementVNode("p",null,"拖动图片到此",-1))])])):h.createCommentVNode("",!0),h.createVNode(h.unref(pe),{"is-visible":p.value},h.createSlots({_:2},[h.unref(Yt).loading?{name:"default",fn:h.withCtx(()=>[h.renderSlot(i.$slots,"loading")]),key:"0"}:void 0]),1032,["is-visible"])],36))}}),ve="0.1.0",be={version:ve},Le=function(n){n.component("VueCropper",Q)};typeof window<"u"&&(window.requestAnimationFrame||(window.requestAnimationFrame=n=>window.setTimeout(n,17)),window.cancelAnimationFrame||(window.cancelAnimationFrame=n=>{window.clearTimeout(n)}));const Tt={version:be.version,install:Le,VueCropper:Q};exports.VueCropper=Q;exports.default=Tt;exports.globalCropper=Tt;
|