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 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
+ ![license](https://img.shields.io/badge/license-ISC-blue.svg)
4
+ ![node](https://img.shields.io/badge/node-22.x-339933.svg)
5
+ ![coverage](https://img.shields.io/badge/coverage-%E2%89%A585%25-brightgreen.svg)
6
+ ![tests](https://img.shields.io/badge/tests-37%20passing-brightgreen.svg)
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;