v3-comf-dm 1.0.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.
@@ -0,0 +1,718 @@
1
+ import { defineComponent, ref, computed, onMounted, nextTick, onBeforeUnmount, createElementBlock, openBlock, normalizeStyle, createElementVNode, renderSlot, watch, createStaticVNode, createCommentVNode, withModifiers, Fragment, renderList, normalizeClass, withDirectives, vModelText, toDisplayString } from "vue";
2
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3
+ ...{
4
+ name: "VcScaleContainer"
5
+ },
6
+ __name: "index",
7
+ props: {
8
+ designWidth: { default: 1920 },
9
+ designHeight: { default: 1080 },
10
+ keepAspectRatio: { type: Boolean, default: true },
11
+ scaleMode: { default: "fit" },
12
+ detectBrowserZoom: { type: Boolean, default: true }
13
+ },
14
+ setup(__props) {
15
+ const props = __props;
16
+ const scaleContainer = ref(null);
17
+ const scale = ref(1);
18
+ const scaleX = ref(1);
19
+ const scaleY = ref(1);
20
+ const containerWidth = ref(1920);
21
+ const containerHeight = ref(1080);
22
+ const containerStyle = computed(() => ({
23
+ width: `${containerWidth.value}px`,
24
+ height: `${containerHeight.value}px`,
25
+ position: "relative",
26
+ overflow: "hidden"
27
+ }));
28
+ const wrapperStyle = computed(() => {
29
+ const commonStyle = {
30
+ width: `${props.designWidth}px`,
31
+ height: `${props.designHeight}px`,
32
+ transformOrigin: "top left"
33
+ };
34
+ if (props.keepAspectRatio && (props.scaleMode === "fit" || props.scaleMode === "fill")) {
35
+ const sw = props.designWidth * scale.value;
36
+ const sh = props.designHeight * scale.value;
37
+ return {
38
+ ...commonStyle,
39
+ transform: `scale(${scale.value})`,
40
+ position: "absolute",
41
+ top: "50%",
42
+ left: "50%",
43
+ marginTop: `-${sh / 2}px`,
44
+ marginLeft: `-${sw / 2}px`
45
+ };
46
+ }
47
+ return {
48
+ ...commonStyle,
49
+ transform: `scale(${scaleX.value}, ${scaleY.value})`
50
+ };
51
+ });
52
+ const updateScale = () => {
53
+ const el = scaleContainer.value;
54
+ if (!el || !el.parentElement) return;
55
+ const parent = el.parentElement;
56
+ let pw = parent.clientWidth || window.innerWidth;
57
+ let ph = parent.clientHeight || window.innerHeight;
58
+ if (pw === 0) pw = window.innerWidth;
59
+ if (ph === 0) ph = window.innerHeight;
60
+ containerWidth.value = pw;
61
+ containerHeight.value = ph;
62
+ const sx = pw / props.designWidth;
63
+ const sy = ph / props.designHeight;
64
+ if (props.scaleMode === "stretch") {
65
+ scaleX.value = sx;
66
+ scaleY.value = sy;
67
+ } else if (props.keepAspectRatio) {
68
+ scale.value = props.scaleMode === "fit" ? Math.min(sx, sy) : Math.max(sx, sy);
69
+ } else {
70
+ scaleX.value = sx;
71
+ scaleY.value = sy;
72
+ }
73
+ };
74
+ let resizeObserver = null;
75
+ const handleResize = () => updateScale();
76
+ onMounted(() => {
77
+ nextTick(() => {
78
+ var _a;
79
+ updateScale();
80
+ window.addEventListener("resize", handleResize);
81
+ if (window.ResizeObserver && ((_a = scaleContainer.value) == null ? void 0 : _a.parentElement)) {
82
+ resizeObserver = new ResizeObserver(handleResize);
83
+ resizeObserver.observe(scaleContainer.value.parentElement);
84
+ }
85
+ });
86
+ });
87
+ onBeforeUnmount(() => {
88
+ window.removeEventListener("resize", handleResize);
89
+ if (resizeObserver) {
90
+ resizeObserver.disconnect();
91
+ }
92
+ });
93
+ return (_ctx, _cache) => {
94
+ return openBlock(), createElementBlock("div", {
95
+ ref_key: "scaleContainer",
96
+ ref: scaleContainer,
97
+ class: "vc-scale-container",
98
+ style: normalizeStyle(containerStyle.value)
99
+ }, [
100
+ createElementVNode("div", {
101
+ class: "vc-scale-wrapper",
102
+ style: normalizeStyle(wrapperStyle.value)
103
+ }, [
104
+ renderSlot(_ctx.$slots, "default", {}, void 0, true)
105
+ ], 4)
106
+ ], 4);
107
+ };
108
+ }
109
+ });
110
+ const _export_sfc = (sfc, props) => {
111
+ const target = sfc.__vccOpts || sfc;
112
+ for (const [key, val] of props) {
113
+ target[key] = val;
114
+ }
115
+ return target;
116
+ };
117
+ const ScaleContainer = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-7f878606"]]);
118
+ const _hoisted_1 = {
119
+ key: 0,
120
+ class: "vc-image-cropper-placeholder"
121
+ };
122
+ const _hoisted_2 = ["src"];
123
+ const _hoisted_3 = {
124
+ key: 0,
125
+ class: "vc-crop-grid"
126
+ };
127
+ const _hoisted_4 = ["onMousedown"];
128
+ const _hoisted_5 = {
129
+ key: 0,
130
+ class: "vc-cropper-toolbar"
131
+ };
132
+ const _hoisted_6 = {
133
+ key: 0,
134
+ class: "toolbar-group"
135
+ };
136
+ const _hoisted_7 = ["min", "max"];
137
+ const _hoisted_8 = { class: "toolbar-value" };
138
+ const _hoisted_9 = {
139
+ key: 1,
140
+ class: "toolbar-group"
141
+ };
142
+ const __default__ = {
143
+ name: "VcImageCropper"
144
+ };
145
+ const _sfc_main = /* @__PURE__ */ defineComponent({
146
+ ...__default__,
147
+ props: {
148
+ imageSrc: {},
149
+ cropMode: { default: "free" },
150
+ fixedWidth: {},
151
+ fixedHeight: {},
152
+ aspectRatio: {},
153
+ minWidth: { default: 50 },
154
+ minHeight: { default: 50 },
155
+ maxWidth: {},
156
+ maxHeight: {},
157
+ showGrid: { type: Boolean, default: true },
158
+ rotatable: { type: Boolean, default: true },
159
+ zoomable: { type: Boolean, default: true },
160
+ initialZoom: { default: 1 },
161
+ containerWidth: { default: "100%" },
162
+ containerHeight: { default: "600px" }
163
+ },
164
+ emits: ["crop", "cancel", "ready"],
165
+ setup(__props, { emit: __emit }) {
166
+ const props = __props;
167
+ const emit = __emit;
168
+ const containerRef = ref(null);
169
+ const contentRef = ref(null);
170
+ const imageWrapperRef = ref(null);
171
+ const imageRef = ref(null);
172
+ const cropBoxRef = ref(null);
173
+ const imageLoaded = ref(false);
174
+ const imageNaturalWidth = ref(0);
175
+ const imageNaturalHeight = ref(0);
176
+ const imageDisplayWidth = ref(0);
177
+ const imageDisplayHeight = ref(0);
178
+ const rotation = ref(0);
179
+ const zoom = ref(props.initialZoom);
180
+ const minZoom = ref(0.1);
181
+ const maxZoom = ref(5);
182
+ const cropArea = ref({
183
+ x: 0,
184
+ y: 0,
185
+ width: 0,
186
+ height: 0
187
+ });
188
+ const isDragging = ref(false);
189
+ const isResizing = ref(false);
190
+ const dragType = ref("");
191
+ const dragStart = ref({ x: 0, y: 0 });
192
+ const cropStart = ref({ x: 0, y: 0, width: 0, height: 0 });
193
+ const containerStyle = computed(() => ({
194
+ width: typeof props.containerWidth === "number" ? `${props.containerWidth}px` : props.containerWidth,
195
+ height: typeof props.containerHeight === "number" ? `${props.containerHeight}px` : props.containerHeight
196
+ }));
197
+ const imageWrapperStyle = computed(() => ({
198
+ transform: `rotate(${rotation.value}deg) scale(${zoom.value})`,
199
+ transformOrigin: "center center"
200
+ }));
201
+ const imageStyle = computed(() => ({
202
+ width: `${imageDisplayWidth.value}px`,
203
+ height: `${imageDisplayHeight.value}px`,
204
+ display: "block"
205
+ }));
206
+ const cropBoxStyle = computed(() => ({
207
+ left: `${cropArea.value.x}px`,
208
+ top: `${cropArea.value.y}px`,
209
+ width: `${cropArea.value.width}px`,
210
+ height: `${cropArea.value.height}px`
211
+ }));
212
+ const cropHandles = computed(() => {
213
+ return [
214
+ { type: "nw", class: "handle-nw", style: { top: "-4px", left: "-4px", cursor: "nwse-resize" } },
215
+ {
216
+ type: "ne",
217
+ class: "handle-ne",
218
+ style: { top: "-4px", right: "-4px", cursor: "nesw-resize" }
219
+ },
220
+ {
221
+ type: "sw",
222
+ class: "handle-sw",
223
+ style: { bottom: "-4px", left: "-4px", cursor: "nesw-resize" }
224
+ },
225
+ {
226
+ type: "se",
227
+ class: "handle-se",
228
+ style: { bottom: "-4px", right: "-4px", cursor: "nwse-resize" }
229
+ },
230
+ {
231
+ type: "n",
232
+ class: "handle-n",
233
+ style: { top: "-4px", left: "50%", transform: "translateX(-50%)", cursor: "ns-resize" }
234
+ },
235
+ {
236
+ type: "s",
237
+ class: "handle-s",
238
+ style: { bottom: "-4px", left: "50%", transform: "translateX(-50%)", cursor: "ns-resize" }
239
+ },
240
+ {
241
+ type: "w",
242
+ class: "handle-w",
243
+ style: { top: "50%", left: "-4px", transform: "translateY(-50%)", cursor: "ew-resize" }
244
+ },
245
+ {
246
+ type: "e",
247
+ class: "handle-e",
248
+ style: { top: "50%", right: "-4px", transform: "translateY(-50%)", cursor: "ew-resize" }
249
+ }
250
+ ];
251
+ });
252
+ const handleImageLoad = () => {
253
+ if (!imageRef.value) return;
254
+ imageNaturalWidth.value = imageRef.value.naturalWidth;
255
+ imageNaturalHeight.value = imageRef.value.naturalHeight;
256
+ nextTick(() => {
257
+ updateImageDisplaySize();
258
+ initCropArea();
259
+ imageLoaded.value = true;
260
+ emit("ready");
261
+ });
262
+ };
263
+ const updateImageDisplaySize = () => {
264
+ if (!contentRef.value || !imageRef.value) return;
265
+ const containerWidth = contentRef.value.clientWidth;
266
+ const containerHeight = contentRef.value.clientHeight;
267
+ if (containerWidth === 0 || containerHeight === 0) return;
268
+ const naturalAspect = imageNaturalWidth.value / imageNaturalHeight.value;
269
+ const containerAspect = containerWidth / containerHeight;
270
+ if (naturalAspect > containerAspect) {
271
+ imageDisplayWidth.value = containerWidth * 0.9;
272
+ imageDisplayHeight.value = containerWidth * 0.9 / naturalAspect;
273
+ } else {
274
+ imageDisplayHeight.value = containerHeight * 0.9;
275
+ imageDisplayWidth.value = containerHeight * 0.9 * naturalAspect;
276
+ }
277
+ };
278
+ const initCropArea = () => {
279
+ const width = imageDisplayWidth.value;
280
+ const height = imageDisplayHeight.value;
281
+ const size = Math.min(width, height) * 0.8;
282
+ let cropWidth = size;
283
+ let cropHeight = size;
284
+ if (props.cropMode === "fixed") {
285
+ cropWidth = props.fixedWidth || size;
286
+ cropHeight = props.fixedHeight || size;
287
+ } else if (props.cropMode === "ratio" && props.aspectRatio) {
288
+ if (width / height > props.aspectRatio) {
289
+ cropHeight = size;
290
+ cropWidth = size * props.aspectRatio;
291
+ } else {
292
+ cropWidth = size;
293
+ cropHeight = size / props.aspectRatio;
294
+ }
295
+ }
296
+ cropArea.value = {
297
+ x: (width - cropWidth) / 2,
298
+ y: (height - cropHeight) / 2,
299
+ width: cropWidth,
300
+ height: cropHeight
301
+ };
302
+ };
303
+ const handleMouseDown = (e) => {
304
+ if (e.target !== imageWrapperRef.value) return;
305
+ isDragging.value = true;
306
+ dragStart.value = { x: e.clientX, y: e.clientY };
307
+ document.addEventListener("mousemove", handleMouseMove);
308
+ document.addEventListener("mouseup", handleMouseUp);
309
+ e.preventDefault();
310
+ };
311
+ const handleCropBoxMouseDown = (e) => {
312
+ if (e.target === cropBoxRef.value) {
313
+ isDragging.value = true;
314
+ dragType.value = "crop-box";
315
+ dragStart.value = { x: e.clientX, y: e.clientY };
316
+ cropStart.value = { ...cropArea.value };
317
+ document.addEventListener("mousemove", handleMouseMove);
318
+ document.addEventListener("mouseup", handleMouseUp);
319
+ e.preventDefault();
320
+ }
321
+ };
322
+ const handleResizeStart = (e, type) => {
323
+ isResizing.value = true;
324
+ dragType.value = type;
325
+ dragStart.value = { x: e.clientX, y: e.clientY };
326
+ cropStart.value = { ...cropArea.value };
327
+ document.addEventListener("mousemove", handleMouseMove);
328
+ document.addEventListener("mouseup", handleMouseUp);
329
+ e.preventDefault();
330
+ };
331
+ const handleMouseMove = (e) => {
332
+ if (!contentRef.value) return;
333
+ const screenDeltaX = (e.clientX - dragStart.value.x) / zoom.value;
334
+ const screenDeltaY = (e.clientY - dragStart.value.y) / zoom.value;
335
+ const rad = -rotation.value * Math.PI / 180;
336
+ const cos = Math.cos(rad);
337
+ const sin = Math.sin(rad);
338
+ const deltaX = screenDeltaX * cos - screenDeltaY * sin;
339
+ const deltaY = screenDeltaX * sin + screenDeltaY * cos;
340
+ if (isDragging.value && dragType.value === "crop-box") {
341
+ const width = imageDisplayWidth.value;
342
+ const height = imageDisplayHeight.value;
343
+ const newX = Math.max(0, Math.min(cropStart.value.x + deltaX, width - cropArea.value.width));
344
+ const newY = Math.max(0, Math.min(cropStart.value.y + deltaY, height - cropArea.value.height));
345
+ cropArea.value.x = newX;
346
+ cropArea.value.y = newY;
347
+ } else if (isResizing.value) {
348
+ resizeCropBox(deltaX, deltaY);
349
+ }
350
+ };
351
+ const resizeCropBox = (deltaX, deltaY) => {
352
+ const width = imageDisplayWidth.value;
353
+ const height = imageDisplayHeight.value;
354
+ const { x, y, width: w, height: h } = cropStart.value;
355
+ const type = dragType.value;
356
+ let newX = x;
357
+ let newY = y;
358
+ let newWidth = w;
359
+ let newHeight = h;
360
+ if (type.includes("e")) {
361
+ newWidth = Math.max(props.minWidth, Math.min(w + deltaX, width - x, props.maxWidth || Infinity));
362
+ }
363
+ if (type.includes("w")) {
364
+ const maxWidth = x + w;
365
+ newWidth = Math.max(props.minWidth, Math.min(w - deltaX, maxWidth, props.maxWidth || Infinity));
366
+ newX = Math.max(0, x + w - newWidth);
367
+ }
368
+ if (type.includes("s")) {
369
+ newHeight = Math.max(
370
+ props.minHeight,
371
+ Math.min(h + deltaY, height - y, props.maxHeight || Infinity)
372
+ );
373
+ }
374
+ if (type.includes("n")) {
375
+ const maxHeight = y + h;
376
+ newHeight = Math.max(
377
+ props.minHeight,
378
+ Math.min(h - deltaY, maxHeight, props.maxHeight || Infinity)
379
+ );
380
+ newY = Math.max(0, y + h - newHeight);
381
+ }
382
+ if (props.cropMode === "ratio" && props.aspectRatio) {
383
+ const currentAspect = newWidth / newHeight;
384
+ if (Math.abs(currentAspect - props.aspectRatio) > 0.01) {
385
+ if (type.includes("e") || type.includes("w")) {
386
+ newHeight = newWidth / props.aspectRatio;
387
+ if (type.includes("n")) {
388
+ newY = y + h - newHeight;
389
+ }
390
+ } else {
391
+ newWidth = newHeight * props.aspectRatio;
392
+ if (type.includes("w")) {
393
+ newX = x + w - newWidth;
394
+ }
395
+ }
396
+ }
397
+ }
398
+ if (newX + newWidth > width) {
399
+ newWidth = width - newX;
400
+ if (props.cropMode === "ratio" && props.aspectRatio) {
401
+ newHeight = newWidth / props.aspectRatio;
402
+ }
403
+ }
404
+ if (newY + newHeight > height) {
405
+ newHeight = height - newY;
406
+ if (props.cropMode === "ratio" && props.aspectRatio) {
407
+ newWidth = newHeight * props.aspectRatio;
408
+ }
409
+ }
410
+ cropArea.value = { x: newX, y: newY, width: newWidth, height: newHeight };
411
+ };
412
+ const handleMouseUp = () => {
413
+ isDragging.value = false;
414
+ isResizing.value = false;
415
+ dragType.value = "";
416
+ document.removeEventListener("mousemove", handleMouseMove);
417
+ document.removeEventListener("mouseup", handleMouseUp);
418
+ };
419
+ const handleZoomChange = () => {
420
+ };
421
+ const handleWheel = (e) => {
422
+ if (!props.zoomable || !imageLoaded.value) return;
423
+ e.preventDefault();
424
+ const delta = e.deltaY > 0 ? -0.1 : 0.1;
425
+ const newZoom = Math.max(minZoom.value, Math.min(maxZoom.value, zoom.value + delta));
426
+ zoom.value = Math.round(newZoom * 10) / 10;
427
+ };
428
+ const rotateImage = (angle) => {
429
+ rotation.value = (rotation.value + angle) % 360;
430
+ nextTick(() => {
431
+ updateImageDisplaySize();
432
+ initCropArea();
433
+ });
434
+ };
435
+ const handleCrop = () => {
436
+ if (!imageRef.value || !props.imageSrc) return;
437
+ const canvas = document.createElement("canvas");
438
+ const ctx = canvas.getContext("2d");
439
+ if (!ctx) return;
440
+ const scaleX = imageNaturalWidth.value / imageDisplayWidth.value;
441
+ const scaleY = imageNaturalHeight.value / imageDisplayHeight.value;
442
+ const { x, y, width, height } = cropArea.value;
443
+ const cropW = width * scaleX;
444
+ const cropH = height * scaleY;
445
+ const is90Deg = Math.abs(rotation.value % 180) === 90;
446
+ canvas.width = is90Deg ? cropH : cropW;
447
+ canvas.height = is90Deg ? cropW : cropH;
448
+ ctx.save();
449
+ ctx.translate(canvas.width / 2, canvas.height / 2);
450
+ ctx.rotate(rotation.value * Math.PI / 180);
451
+ const imgCenterX = imageNaturalWidth.value / 2;
452
+ const imgCenterY = imageNaturalHeight.value / 2;
453
+ const cropCenterX = (x + width / 2) * scaleX;
454
+ const cropCenterY = (y + height / 2) * scaleY;
455
+ const drawOffsetX = imgCenterX - cropCenterX;
456
+ const drawOffsetY = imgCenterY - cropCenterY;
457
+ ctx.drawImage(
458
+ imageRef.value,
459
+ -imageNaturalWidth.value / 2 + drawOffsetX,
460
+ -imageNaturalHeight.value / 2 + drawOffsetY,
461
+ imageNaturalWidth.value,
462
+ imageNaturalHeight.value
463
+ );
464
+ ctx.restore();
465
+ canvas.toBlob((blob) => {
466
+ if (blob) {
467
+ const dataUrl = canvas.toDataURL("image/png");
468
+ emit("crop", {
469
+ blob,
470
+ dataUrl,
471
+ cropArea: {
472
+ x: x * scaleX,
473
+ y: y * scaleY,
474
+ width: cropW,
475
+ height: cropH
476
+ }
477
+ });
478
+ }
479
+ }, "image/png");
480
+ };
481
+ const handleCancel = () => {
482
+ emit("cancel");
483
+ };
484
+ watch(
485
+ () => props.imageSrc,
486
+ (newSrc) => {
487
+ if (newSrc) {
488
+ imageLoaded.value = false;
489
+ rotation.value = 0;
490
+ zoom.value = props.initialZoom;
491
+ }
492
+ }
493
+ );
494
+ const resizeObserver = ref(null);
495
+ const handleKeyDown = (e) => {
496
+ if (!imageLoaded.value || !props.imageSrc) return;
497
+ const target = e.target;
498
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.isContentEditable)
499
+ return;
500
+ if (e.ctrlKey || e.metaKey) {
501
+ if (e.key === "=" || e.key === "+") {
502
+ e.preventDefault();
503
+ const newZoom = Math.min(maxZoom.value, zoom.value + 0.1);
504
+ zoom.value = Math.round(newZoom * 10) / 10;
505
+ } else if (e.key === "-" || e.key === "_") {
506
+ e.preventDefault();
507
+ const newZoom = Math.max(minZoom.value, zoom.value - 0.1);
508
+ zoom.value = Math.round(newZoom * 10) / 10;
509
+ }
510
+ }
511
+ if (props.rotatable && (e.key === "r" || e.key === "R")) {
512
+ e.preventDefault();
513
+ rotateImage(90);
514
+ }
515
+ if (e.key === "Enter" && !e.shiftKey) {
516
+ e.preventDefault();
517
+ handleCrop();
518
+ }
519
+ if (e.key === "Escape") {
520
+ e.preventDefault();
521
+ handleCancel();
522
+ }
523
+ };
524
+ onMounted(() => {
525
+ if (containerRef.value && window.ResizeObserver) {
526
+ resizeObserver.value = new ResizeObserver(() => {
527
+ updateImageDisplaySize();
528
+ });
529
+ resizeObserver.value.observe(containerRef.value);
530
+ }
531
+ document.addEventListener("keydown", handleKeyDown);
532
+ });
533
+ onBeforeUnmount(() => {
534
+ handleMouseUp();
535
+ if (resizeObserver.value) {
536
+ resizeObserver.value.disconnect();
537
+ }
538
+ document.removeEventListener("keydown", handleKeyDown);
539
+ });
540
+ return (_ctx, _cache) => {
541
+ return openBlock(), createElementBlock("div", {
542
+ ref_key: "containerRef",
543
+ ref: containerRef,
544
+ class: "vc-image-cropper",
545
+ style: normalizeStyle(containerStyle.value),
546
+ tabindex: "0"
547
+ }, [
548
+ !__props.imageSrc ? (openBlock(), createElementBlock("div", _hoisted_1, [..._cache[3] || (_cache[3] = [
549
+ createStaticVNode('<div class="placeholder-content" data-v-a3fe50be><svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" data-v-a3fe50be><rect x="3" y="3" width="18" height="18" rx="2" ry="2" data-v-a3fe50be></rect><circle cx="8.5" cy="8.5" r="1.5" data-v-a3fe50be></circle><polyline points="21 15 16 10 5 21" data-v-a3fe50be></polyline></svg><p data-v-a3fe50be>请选择图片</p></div>', 1)
550
+ ])])) : (openBlock(), createElementBlock("div", {
551
+ key: 1,
552
+ class: "vc-image-cropper-content",
553
+ ref_key: "contentRef",
554
+ ref: contentRef
555
+ }, [
556
+ createElementVNode("div", {
557
+ ref_key: "imageWrapperRef",
558
+ ref: imageWrapperRef,
559
+ class: "vc-image-wrapper",
560
+ style: normalizeStyle(imageWrapperStyle.value),
561
+ onMousedown: handleMouseDown,
562
+ onWheel: withModifiers(handleWheel, ["prevent"])
563
+ }, [
564
+ createElementVNode("img", {
565
+ ref_key: "imageRef",
566
+ ref: imageRef,
567
+ src: __props.imageSrc,
568
+ style: normalizeStyle(imageStyle.value),
569
+ onLoad: handleImageLoad,
570
+ draggable: "false"
571
+ }, null, 44, _hoisted_2),
572
+ imageLoaded.value ? (openBlock(), createElementBlock("div", {
573
+ key: 0,
574
+ ref_key: "cropBoxRef",
575
+ ref: cropBoxRef,
576
+ class: "vc-crop-box",
577
+ style: normalizeStyle(cropBoxStyle.value),
578
+ onMousedown: withModifiers(handleCropBoxMouseDown, ["stop"])
579
+ }, [
580
+ __props.showGrid ? (openBlock(), createElementBlock("div", _hoisted_3, [..._cache[4] || (_cache[4] = [
581
+ createElementVNode("div", { class: "grid-line grid-line-h1" }, null, -1),
582
+ createElementVNode("div", { class: "grid-line grid-line-h2" }, null, -1),
583
+ createElementVNode("div", { class: "grid-line grid-line-v1" }, null, -1),
584
+ createElementVNode("div", { class: "grid-line grid-line-v2" }, null, -1)
585
+ ])])) : createCommentVNode("", true),
586
+ (openBlock(true), createElementBlock(Fragment, null, renderList(cropHandles.value, (handle, index2) => {
587
+ return openBlock(), createElementBlock("div", {
588
+ key: index2,
589
+ class: normalizeClass(["vc-crop-handle", handle.class]),
590
+ style: normalizeStyle(handle.style),
591
+ onMousedown: withModifiers(($event) => handleResizeStart($event, handle.type), ["stop"])
592
+ }, null, 46, _hoisted_4);
593
+ }), 128))
594
+ ], 36)) : createCommentVNode("", true)
595
+ ], 36),
596
+ imageLoaded.value ? (openBlock(), createElementBlock("div", _hoisted_5, [
597
+ __props.zoomable ? (openBlock(), createElementBlock("div", _hoisted_6, [
598
+ _cache[5] || (_cache[5] = createElementVNode("label", { class: "toolbar-label" }, "缩放:", -1)),
599
+ withDirectives(createElementVNode("input", {
600
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => zoom.value = $event),
601
+ type: "range",
602
+ min: minZoom.value,
603
+ max: maxZoom.value,
604
+ step: 0.1,
605
+ class: "toolbar-slider",
606
+ onInput: handleZoomChange
607
+ }, null, 40, _hoisted_7), [
608
+ [
609
+ vModelText,
610
+ zoom.value,
611
+ void 0,
612
+ { number: true }
613
+ ]
614
+ ]),
615
+ createElementVNode("span", _hoisted_8, toDisplayString(Math.round(zoom.value * 100)) + "%", 1)
616
+ ])) : createCommentVNode("", true),
617
+ __props.rotatable ? (openBlock(), createElementBlock("div", _hoisted_9, [
618
+ createElementVNode("button", {
619
+ class: "toolbar-btn",
620
+ onClick: _cache[1] || (_cache[1] = ($event) => rotateImage(-90)),
621
+ title: "逆时针旋转"
622
+ }, "↺"),
623
+ createElementVNode("button", {
624
+ class: "toolbar-btn",
625
+ onClick: _cache[2] || (_cache[2] = ($event) => rotateImage(90)),
626
+ title: "顺时针旋转"
627
+ }, "↻")
628
+ ])) : createCommentVNode("", true),
629
+ createElementVNode("div", { class: "toolbar-group" }, [
630
+ createElementVNode("button", {
631
+ class: "toolbar-btn toolbar-btn-primary",
632
+ onClick: handleCrop
633
+ }, "确认裁剪"),
634
+ createElementVNode("button", {
635
+ class: "toolbar-btn",
636
+ onClick: handleCancel
637
+ }, "取消")
638
+ ])
639
+ ])) : createCommentVNode("", true)
640
+ ], 512))
641
+ ], 4);
642
+ };
643
+ }
644
+ });
645
+ const ImageCropper = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-a3fe50be"]]);
646
+ const components = [
647
+ ScaleContainer,
648
+ ImageCropper
649
+ ];
650
+ const install$1 = (app) => {
651
+ components.forEach((component) => {
652
+ const name = component.name || component.__name;
653
+ if (name) {
654
+ app.component(name, component);
655
+ }
656
+ });
657
+ };
658
+ const isString = (val) => typeof val === "string";
659
+ const isNumber = (val) => typeof val === "number";
660
+ const isBoolean = (val) => typeof val === "boolean";
661
+ const isObject = (val) => val !== null && typeof val === "object";
662
+ const isArray = (val) => Array.isArray(val);
663
+ const isFunction = (val) => typeof val === "function";
664
+ function debounce(func, wait) {
665
+ let timeout = null;
666
+ return function(...args) {
667
+ const context = this;
668
+ if (timeout) clearTimeout(timeout);
669
+ timeout = setTimeout(() => {
670
+ func.apply(context, args);
671
+ }, wait);
672
+ };
673
+ }
674
+ function throttle(func, wait) {
675
+ let timeout = null;
676
+ let previous = 0;
677
+ return function(...args) {
678
+ const now = Date.now();
679
+ const remaining = wait - (now - previous);
680
+ if (remaining <= 0 || remaining > wait) {
681
+ if (timeout) {
682
+ clearTimeout(timeout);
683
+ timeout = null;
684
+ }
685
+ previous = now;
686
+ func.apply(this, args);
687
+ } else if (!timeout) {
688
+ timeout = setTimeout(() => {
689
+ previous = Date.now();
690
+ timeout = null;
691
+ func.apply(this, args);
692
+ }, remaining);
693
+ }
694
+ };
695
+ }
696
+ const install = (app) => {
697
+ install$1(app);
698
+ };
699
+ const index = {
700
+ install
701
+ };
702
+ const version = "1.0.0";
703
+ export {
704
+ ImageCropper,
705
+ ScaleContainer,
706
+ debounce,
707
+ index as default,
708
+ install,
709
+ isArray,
710
+ isBoolean,
711
+ isFunction,
712
+ isNumber,
713
+ isObject,
714
+ isString,
715
+ throttle,
716
+ version
717
+ };
718
+ //# sourceMappingURL=index.esm.js.map