vanilla-jui 1.7.41 → 1.7.43
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/dist/index.d.ts +2 -59
- package/dist/index.js +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/swiper.ts +6 -6
- package/src/css/icon.css +29 -4
- package/src/css/layout.css +18 -22
- package/src/css/themes/default/accordion.css +0 -1
- package/src/css/themes/default/avatar.css +0 -1
- package/src/css/themes/default/badge.css +3 -5
- package/src/css/themes/default/breadcrumb.css +0 -1
- package/src/css/themes/default/button.css +0 -1
- package/src/css/themes/default/editor.css +5 -7
- package/src/css/themes/default/form.css +0 -8
- package/src/css/themes/default/menu.css +0 -2
- package/src/css/themes/default/offcanvas.css +1 -1
- package/src/css/themes/default/swiper.css +2 -5
- package/src/css/themes/default/toast.css +0 -1
- package/src/css/themes/default/tooltip.css +0 -1
- package/src/css/tokens.css +8 -9
- package/src/index.ts +0 -1
- package/src/utilities/compress.ts +0 -167
package/package.json
CHANGED
package/src/components/swiper.ts
CHANGED
|
@@ -31,7 +31,7 @@ const AUTOPLAY_DELAY_FLOOR = 16;
|
|
|
31
31
|
|
|
32
32
|
export interface SwiperClassNames {
|
|
33
33
|
root: string;
|
|
34
|
-
|
|
34
|
+
wrap: string;
|
|
35
35
|
slide: string;
|
|
36
36
|
image: string;
|
|
37
37
|
title: string;
|
|
@@ -174,7 +174,7 @@ export type Swiper = SwiperBase & {
|
|
|
174
174
|
|
|
175
175
|
const DEFAULT_CLASS_NAMES: SwiperClassNames = {
|
|
176
176
|
root: 'j-swiper',
|
|
177
|
-
|
|
177
|
+
wrap: 'swiper-wrap',
|
|
178
178
|
slide: 'swiper-slide',
|
|
179
179
|
image: 'swiper-image',
|
|
180
180
|
title: 'swiper-slide-title',
|
|
@@ -908,8 +908,8 @@ export function createSwiper(input: SwiperProps = {}): Swiper {
|
|
|
908
908
|
return view;
|
|
909
909
|
};
|
|
910
910
|
wrapper = jsx('div', {
|
|
911
|
-
className: props.className.
|
|
912
|
-
'data-swiper-
|
|
911
|
+
className: props.className.wrap,
|
|
912
|
+
'data-swiper-wrap': '',
|
|
913
913
|
'aria-live': 'polite',
|
|
914
914
|
children: () =>
|
|
915
915
|
state.loading
|
|
@@ -974,7 +974,7 @@ export function createSwiper(input: SwiperProps = {}): Swiper {
|
|
|
974
974
|
),
|
|
975
975
|
'data-action': 'prev',
|
|
976
976
|
'data-swiper-navigation': 'prev',
|
|
977
|
-
'aria-label': 'Previous
|
|
977
|
+
'aria-label': 'Previous Slide',
|
|
978
978
|
disabled: () => !props.loop && state.index <= 0,
|
|
979
979
|
children: icon('arrow-left'),
|
|
980
980
|
})
|
|
@@ -992,7 +992,7 @@ export function createSwiper(input: SwiperProps = {}): Swiper {
|
|
|
992
992
|
),
|
|
993
993
|
'data-action': 'next',
|
|
994
994
|
'data-swiper-navigation': 'next',
|
|
995
|
-
'aria-label': 'Next
|
|
995
|
+
'aria-label': 'Next Slide',
|
|
996
996
|
disabled: () => !props.loop && state.index >= realCount() - 1,
|
|
997
997
|
children: icon('arrow-right'),
|
|
998
998
|
})
|
package/src/css/icon.css
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
@layer icon {
|
|
2
|
-
svg {
|
|
2
|
+
/* svg {
|
|
3
3
|
display: block;
|
|
4
|
+
} */
|
|
5
|
+
|
|
6
|
+
/* icon size */
|
|
7
|
+
[class^='icon-'] {
|
|
8
|
+
--icon-size: var(--font-size-lg);
|
|
9
|
+
width: var(--icon-size);
|
|
10
|
+
height: var(--icon-size);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.is-xs [class^='icon-'] {
|
|
14
|
+
--icon-size: var(--font-size-sm);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.is-sm [class^='icon-'] {
|
|
18
|
+
--icon-size: var(--font-size-md);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.is-md [class^='icon-'] {
|
|
22
|
+
--icon-size: var(--font-size-lg);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.is-lg [class^='icon-'] {
|
|
26
|
+
--icon-size: var(--font-size-xl);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.is-xl [class^='icon-'] {
|
|
30
|
+
--icon-size: var(--font-size-2xl);
|
|
4
31
|
}
|
|
5
32
|
|
|
6
33
|
/* close icon */
|
|
7
34
|
.icon-close {
|
|
8
35
|
position: relative;
|
|
9
36
|
display: block;
|
|
10
|
-
width: var(--font-size-xl);
|
|
11
|
-
height: var(--font-size-xl);
|
|
12
37
|
color: inherit;
|
|
13
38
|
}
|
|
14
39
|
|
|
@@ -18,7 +43,7 @@
|
|
|
18
43
|
position: absolute;
|
|
19
44
|
top: 50%;
|
|
20
45
|
left: 50%;
|
|
21
|
-
width:
|
|
46
|
+
width: 100%;
|
|
22
47
|
height: 1.5px;
|
|
23
48
|
background: currentColor;
|
|
24
49
|
border-radius: 1px;
|
package/src/css/layout.css
CHANGED
|
@@ -109,7 +109,6 @@
|
|
|
109
109
|
transparent 300%
|
|
110
110
|
);
|
|
111
111
|
pointer-events: none;
|
|
112
|
-
user-select: none;
|
|
113
112
|
}
|
|
114
113
|
|
|
115
114
|
.dark .j-background-grid {
|
|
@@ -892,14 +891,13 @@
|
|
|
892
891
|
Skeleton
|
|
893
892
|
============================================ */
|
|
894
893
|
.j-skeleton {
|
|
895
|
-
display: block;
|
|
896
|
-
width: 100
|
|
897
|
-
|
|
898
|
-
border-radius: var(--radius-sm);
|
|
899
|
-
background: var(--ui-bg-muted);
|
|
894
|
+
display: var(--skeleton-display, block);
|
|
895
|
+
width: var(--skeleton-width, 100%);
|
|
896
|
+
height: var(--skeleton-height, var(--el-height-2xs));
|
|
897
|
+
border-radius: var(--skeleton-radius, var(--radius-sm));
|
|
898
|
+
background: var(--skeleton-bg, var(--ui-bg-muted));
|
|
900
899
|
color: transparent;
|
|
901
900
|
pointer-events: none;
|
|
902
|
-
user-select: none;
|
|
903
901
|
animation: animate-pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
|
904
902
|
}
|
|
905
903
|
|
|
@@ -909,41 +907,39 @@
|
|
|
909
907
|
}
|
|
910
908
|
|
|
911
909
|
.j-skeleton.skeleton-inline {
|
|
912
|
-
display: inline-block;
|
|
913
|
-
width: 8rem;
|
|
910
|
+
--skeleton-display: inline-block;
|
|
911
|
+
--skeleton-width: 8rem;
|
|
912
|
+
--skeleton-height: var(--el-height-xs);
|
|
914
913
|
vertical-align: middle;
|
|
915
914
|
}
|
|
916
915
|
|
|
917
916
|
.j-skeleton.skeleton-text {
|
|
918
|
-
height: var(--el-height-sm);
|
|
919
|
-
min-height: var(--el-height-sm);
|
|
917
|
+
--skeleton-height: var(--el-height-sm);
|
|
920
918
|
}
|
|
921
919
|
|
|
922
920
|
.j-skeleton.skeleton-title {
|
|
923
|
-
width: 60%;
|
|
924
|
-
height: var(--el-height
|
|
925
|
-
min-height: var(--el-height-md);
|
|
921
|
+
--skeleton-width: 60%;
|
|
922
|
+
--skeleton-height: var(--el-height);
|
|
926
923
|
}
|
|
927
924
|
|
|
928
925
|
.j-skeleton.skeleton-block {
|
|
929
|
-
height: calc(var(--el-height
|
|
930
|
-
min-height: calc(var(--el-height-md) * 4);
|
|
926
|
+
--skeleton-height: calc(var(--el-height) * 2);
|
|
931
927
|
}
|
|
932
928
|
|
|
933
929
|
.j-skeleton.skeleton-avatar,
|
|
934
930
|
.j-skeleton.skeleton-circle {
|
|
931
|
+
--skeleton-radius: 999px;
|
|
935
932
|
aspect-ratio: 1;
|
|
936
|
-
border-radius: 999px;
|
|
937
933
|
}
|
|
938
934
|
|
|
939
935
|
.j-skeleton.skeleton-avatar {
|
|
940
|
-
width: var(--el-height
|
|
941
|
-
|
|
936
|
+
--skeleton-width: var(--el-height);
|
|
937
|
+
--skeleton-height: var(--el-height);
|
|
942
938
|
}
|
|
943
939
|
|
|
944
940
|
.j-skeleton.skeleton-button {
|
|
945
|
-
width:
|
|
946
|
-
|
|
941
|
+
--skeleton-width: 4rem;
|
|
942
|
+
--skeleton-height: var(--el-height);
|
|
947
943
|
}
|
|
948
944
|
|
|
949
945
|
.j-skeleton.skeleton-image {
|
|
@@ -966,7 +962,7 @@
|
|
|
966
962
|
Popup
|
|
967
963
|
============================================ */
|
|
968
964
|
.j-popup-layout {
|
|
969
|
-
background: var(--ui-popup
|
|
965
|
+
background: var(--ui-popup);
|
|
970
966
|
backdrop-filter: blur(4px);
|
|
971
967
|
padding: 16px;
|
|
972
968
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
@layer components {
|
|
2
2
|
.j-badge {
|
|
3
3
|
gap: var(--badge-gap, 4px);
|
|
4
|
-
padding: var(--badge-padding, 0
|
|
5
|
-
min-width: var(--badge-min-width,
|
|
6
|
-
height: var(--badge-height,
|
|
4
|
+
padding: var(--badge-padding, 0 4px);
|
|
5
|
+
min-width: var(--badge-min-width, 20px);
|
|
6
|
+
height: var(--badge-height, 20px);
|
|
7
7
|
font-size: var(--badge-font-size, var(--font-size-sm));
|
|
8
8
|
font-weight: 500;
|
|
9
9
|
border-radius: 48px;
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
display: inline-flex;
|
|
16
16
|
justify-content: center;
|
|
17
17
|
align-items: center;
|
|
18
|
-
user-select: none;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
.j-badge svg {
|
|
@@ -27,7 +26,6 @@
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
.j-badge.is-sm {
|
|
30
|
-
--badge-padding: 0 4px;
|
|
31
29
|
--badge-min-width: 1rem;
|
|
32
30
|
--badge-height: 1rem;
|
|
33
31
|
--badge-font-size: var(--font-size-xs);
|
|
@@ -163,9 +163,8 @@
|
|
|
163
163
|
.j-editor u {
|
|
164
164
|
text-decoration: underline;
|
|
165
165
|
text-decoration-thickness: 4px;
|
|
166
|
-
|
|
167
|
-
text-
|
|
168
|
-
text-underline-offset: 0.2em;
|
|
166
|
+
text-decoration-color: var(--ui-yellow);
|
|
167
|
+
text-underline-offset: 3px;
|
|
169
168
|
}
|
|
170
169
|
|
|
171
170
|
.j-editor s,
|
|
@@ -175,8 +174,8 @@
|
|
|
175
174
|
}
|
|
176
175
|
|
|
177
176
|
.j-editor mark {
|
|
178
|
-
background: var(--
|
|
179
|
-
color: var(--
|
|
177
|
+
background: var(--ui-highlight);
|
|
178
|
+
color: var(--ui-highlight-fg);
|
|
180
179
|
padding: 0.1em 0.3em;
|
|
181
180
|
border-radius: 0.2em;
|
|
182
181
|
}
|
|
@@ -344,7 +343,6 @@
|
|
|
344
343
|
|
|
345
344
|
.j-editor ul[data-type='taskList'] li > label {
|
|
346
345
|
flex: none;
|
|
347
|
-
user-select: none;
|
|
348
346
|
}
|
|
349
347
|
|
|
350
348
|
.j-editor ul[data-type='taskList'] li div {
|
|
@@ -427,7 +425,7 @@
|
|
|
427
425
|
border: 1px solid var(--editor-border-subtle);
|
|
428
426
|
border-radius: var(--radius-sm);
|
|
429
427
|
word-break: break-word;
|
|
430
|
-
background: var(--ui-code
|
|
428
|
+
background: var(--ui-code);
|
|
431
429
|
color: var(--ui-code-fg);
|
|
432
430
|
}
|
|
433
431
|
|
|
@@ -133,7 +133,6 @@
|
|
|
133
133
|
font-size: var(--font-size-md);
|
|
134
134
|
padding: 8px;
|
|
135
135
|
border-radius: var(--radius-md);
|
|
136
|
-
user-select: none;
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
.j-radio label:not(:has(input[type='radio']:disabled)):hover,
|
|
@@ -200,7 +199,6 @@
|
|
|
200
199
|
justify-content: center;
|
|
201
200
|
align-items: center;
|
|
202
201
|
text-align: center;
|
|
203
|
-
user-select: none;
|
|
204
202
|
transition-property: box-shadow, color, background-color;
|
|
205
203
|
transition-duration: 0.2s;
|
|
206
204
|
transition-timing-function: ease;
|
|
@@ -460,11 +458,6 @@
|
|
|
460
458
|
border-radius: var(--radius-md);
|
|
461
459
|
}
|
|
462
460
|
|
|
463
|
-
.input-group .el-addon .icon-close,
|
|
464
|
-
.input-inner-group .el-addon .icon-close {
|
|
465
|
-
width: var(--input-height);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
461
|
.input-group > *:not(:first-child) {
|
|
469
462
|
margin-left: -1px;
|
|
470
463
|
}
|
|
@@ -551,7 +544,6 @@
|
|
|
551
544
|
.input-inner-group .el-addon > .el-text {
|
|
552
545
|
display: flex;
|
|
553
546
|
align-items: center;
|
|
554
|
-
user-select: none;
|
|
555
547
|
padding: 0 8px;
|
|
556
548
|
font-size: var(--font-size-md);
|
|
557
549
|
color: var(--ui-fg-muted);
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
scroll-behavior: smooth;
|
|
14
14
|
-ms-overflow-style: none;
|
|
15
15
|
scrollbar-width: none;
|
|
16
|
-
user-select: none;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
.j-menu::-webkit-scrollbar {
|
|
@@ -468,7 +467,6 @@
|
|
|
468
467
|
padding: 4px 8px;
|
|
469
468
|
text-decoration: none;
|
|
470
469
|
color: var(--ui-fg-muted);
|
|
471
|
-
user-select: none;
|
|
472
470
|
border-radius: var(--radius-sm);
|
|
473
471
|
transition: none;
|
|
474
472
|
}
|
|
@@ -8,13 +8,12 @@
|
|
|
8
8
|
display: block;
|
|
9
9
|
overflow: hidden;
|
|
10
10
|
touch-action: pan-y;
|
|
11
|
-
user-select: none;
|
|
12
11
|
width: 100%;
|
|
13
12
|
min-height: var(--swiper-min-height, 120px);
|
|
14
13
|
border-radius: var(--radius-md);
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
.j-swiper .swiper-
|
|
16
|
+
.j-swiper .swiper-wrap {
|
|
18
17
|
display: flex;
|
|
19
18
|
will-change: transform;
|
|
20
19
|
position: relative;
|
|
@@ -30,8 +29,7 @@
|
|
|
30
29
|
overflow: hidden;
|
|
31
30
|
color: inherit;
|
|
32
31
|
text-decoration: none;
|
|
33
|
-
|
|
34
|
-
width: 100%;
|
|
32
|
+
width: var(--swiper-slide-width, 100%);
|
|
35
33
|
min-width: 100%;
|
|
36
34
|
aspect-ratio: inherit;
|
|
37
35
|
border-radius: inherit;
|
|
@@ -178,7 +176,6 @@
|
|
|
178
176
|
.j-swiper .swiper-navigation svg {
|
|
179
177
|
width: 16px;
|
|
180
178
|
height: 16px;
|
|
181
|
-
|
|
182
179
|
pointer-events: none;
|
|
183
180
|
}
|
|
184
181
|
|
package/src/css/tokens.css
CHANGED
|
@@ -84,9 +84,11 @@
|
|
|
84
84
|
--ui-raised: #ffffff;
|
|
85
85
|
|
|
86
86
|
--ui-reverse: #000;
|
|
87
|
-
--ui-popup
|
|
88
|
-
--ui-code
|
|
87
|
+
--ui-popup: rgba(0, 0, 0, 0.0625);
|
|
88
|
+
--ui-code: #f3f4f6;
|
|
89
89
|
--ui-code-fg: #374151;
|
|
90
|
+
--ui-highlight: #fef08a;
|
|
91
|
+
--ui-highlight-fg: #854d0e;
|
|
90
92
|
|
|
91
93
|
/* --- Fixed Theme Colors (15 themes) --- */
|
|
92
94
|
--ui-gray: #374151;
|
|
@@ -154,9 +156,6 @@
|
|
|
154
156
|
--tone-fg: #ffffff;
|
|
155
157
|
--tone-ring: rgba(99, 102, 241, 0.4);
|
|
156
158
|
|
|
157
|
-
--tone-highlight: #fef08a;
|
|
158
|
-
--tone-highlight-fg: #854d0e;
|
|
159
|
-
|
|
160
159
|
/* Typography */
|
|
161
160
|
--tone-heading: #111827;
|
|
162
161
|
--tone-subtitle: #374151;
|
|
@@ -250,9 +249,11 @@
|
|
|
250
249
|
--ui-raised: #151b24;
|
|
251
250
|
|
|
252
251
|
--ui-reverse: #fff;
|
|
253
|
-
--ui-popup
|
|
254
|
-
--ui-code
|
|
252
|
+
--ui-popup: rgba(0, 0, 0, 0.0375);
|
|
253
|
+
--ui-code: #181a1b;
|
|
255
254
|
--ui-code-fg: #e5e7eb;
|
|
255
|
+
--ui-highlight: #fbbf24;
|
|
256
|
+
--ui-highlight-fg: #78350f;
|
|
256
257
|
|
|
257
258
|
/* --- Fixed Theme Colors (15 themes) --- */
|
|
258
259
|
--ui-gray: #d1d5db;
|
|
@@ -319,8 +320,6 @@
|
|
|
319
320
|
--tone-text-hover: #c7d2fe;
|
|
320
321
|
--tone-fg: #1e1b4b;
|
|
321
322
|
--tone-ring: rgba(129, 140, 248, 0.4);
|
|
322
|
-
--tone-highlight: #fbbf24;
|
|
323
|
-
--tone-highlight-fg: #78350f;
|
|
324
323
|
|
|
325
324
|
/* Typography */
|
|
326
325
|
--tone-heading: #f9fafb;
|
package/src/index.ts
CHANGED
|
@@ -18,7 +18,6 @@ export * from './utilities/config.ts';
|
|
|
18
18
|
export * from './utilities/state.ts';
|
|
19
19
|
export * from './utilities/refs.ts';
|
|
20
20
|
export * from './utilities/validator.ts';
|
|
21
|
-
export * from './utilities/compress.ts';
|
|
22
21
|
export * from './core/scheduler.ts';
|
|
23
22
|
export * from './core/view.ts';
|
|
24
23
|
export * from './core/motion.ts';
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
export interface CompressOptions {
|
|
2
|
-
/** 压缩质量,范围 0-1,默认 0.7 */
|
|
3
|
-
quality?: number;
|
|
4
|
-
/** 目标宽度(像素),不指定则按原图比例缩放 */
|
|
5
|
-
width?: number;
|
|
6
|
-
/** 目标高度(像素),不指定则按原图比例缩放 */
|
|
7
|
-
height?: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface ImgCompressInstance {
|
|
11
|
-
/**
|
|
12
|
-
* 将图片路径转换为压缩后的 base64 数据
|
|
13
|
-
* @param path - 图片文件路径或 base64 字符串
|
|
14
|
-
* @param callback - 压缩完成后的回调函数,接收 base64 字符串作为参数
|
|
15
|
-
*/
|
|
16
|
-
canvasDataURL: (path: string, callback: (base64: string) => void) => void;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* 压缩文件对象
|
|
20
|
-
* @param file - 文件对象(Blob 或 File)
|
|
21
|
-
* @param callback - 压缩完成后的回调函数,接收 base64 字符串作为参数
|
|
22
|
-
*/
|
|
23
|
-
photoCompress: (file: Blob, callback: (base64: string) => void) => void;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* 将 base64 字符串转换为 Blob 对象
|
|
27
|
-
* @param urlData - base64 数据(包含 data:image 前缀)
|
|
28
|
-
* @returns Blob 对象,如果解析失败则返回 undefined
|
|
29
|
-
*/
|
|
30
|
-
convertBase64UrlToBlob: (urlData: string) => Blob | undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** 单例缓存 */
|
|
34
|
-
let singletonInstance: ImgCompressInstance | null = null;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* 将 base64 字符串转换为 Blob 对象
|
|
38
|
-
* @param urlData - base64 数据(包含 data:image 前缀)
|
|
39
|
-
* @returns Blob 对象,如果解析失败则返回 undefined
|
|
40
|
-
*/
|
|
41
|
-
function convertBase64UrlToBlob(urlData: string): Blob | undefined {
|
|
42
|
-
const arr = urlData.split(',');
|
|
43
|
-
const mimeMatch = arr[0].match(/:(.*?);/);
|
|
44
|
-
if (!mimeMatch) return;
|
|
45
|
-
|
|
46
|
-
const mime = mimeMatch[1];
|
|
47
|
-
const binaryString = atob(arr[1]);
|
|
48
|
-
const length = binaryString.length;
|
|
49
|
-
const uint8Array = new Uint8Array(length);
|
|
50
|
-
|
|
51
|
-
for (let i = 0; i < length; i++) {
|
|
52
|
-
uint8Array[i] = binaryString.charCodeAt(i);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return new Blob([uint8Array], { type: mime });
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* 创建压缩实例的核心工厂函数
|
|
60
|
-
*/
|
|
61
|
-
function createCompressInstance(
|
|
62
|
-
options?: CompressOptions
|
|
63
|
-
): ImgCompressInstance {
|
|
64
|
-
const { quality = 0.7, width, height } = options ?? {};
|
|
65
|
-
const config = { quality, width, height };
|
|
66
|
-
|
|
67
|
-
return {
|
|
68
|
-
canvasDataURL(path: string, callback: (base64: string) => void): void {
|
|
69
|
-
const img = new Image();
|
|
70
|
-
img.src = path;
|
|
71
|
-
|
|
72
|
-
img.onload = (e: Event): void => {
|
|
73
|
-
const target = e.target as HTMLImageElement;
|
|
74
|
-
const { width: originalWidth, height: originalHeight } = target;
|
|
75
|
-
const scale = originalWidth / originalHeight;
|
|
76
|
-
|
|
77
|
-
const w = config.width ?? originalWidth;
|
|
78
|
-
const h = config.height ?? w / scale;
|
|
79
|
-
|
|
80
|
-
const canvas = document.createElement('canvas');
|
|
81
|
-
const ctx = canvas.getContext('2d');
|
|
82
|
-
|
|
83
|
-
canvas.width = w;
|
|
84
|
-
canvas.height = h;
|
|
85
|
-
|
|
86
|
-
ctx?.drawImage(target, 0, 0, w, h);
|
|
87
|
-
|
|
88
|
-
const base64 = canvas.toDataURL('image/jpeg', config.quality);
|
|
89
|
-
callback(base64);
|
|
90
|
-
};
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
photoCompress(file: Blob, callback: (base64: string) => void): void {
|
|
94
|
-
const reader = new FileReader();
|
|
95
|
-
|
|
96
|
-
reader.readAsDataURL(file);
|
|
97
|
-
|
|
98
|
-
reader.onload = (event: ProgressEvent<FileReader>): void => {
|
|
99
|
-
const result = event.target?.result as string;
|
|
100
|
-
this.canvasDataURL(result, callback);
|
|
101
|
-
};
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
convertBase64UrlToBlob,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ==================== public api ====================
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* 创建图片压缩实例
|
|
112
|
-
* @param options - 压缩配置选项
|
|
113
|
-
* @param singleton - 是否返回单例,默认 true
|
|
114
|
-
* @returns 图片压缩实例
|
|
115
|
-
* @example
|
|
116
|
-
* ```ts
|
|
117
|
-
* // 获取单例实例(默认)
|
|
118
|
-
* const imgzip = createImgCompress({ quality: 0.8, width: 800 });
|
|
119
|
-
*
|
|
120
|
-
* // 创建新实例
|
|
121
|
-
* const imgzip2 = createImgCompress({ quality: 0.9 }, false);
|
|
122
|
-
* ```
|
|
123
|
-
*/
|
|
124
|
-
function createImgCompress(
|
|
125
|
-
options?: CompressOptions,
|
|
126
|
-
singleton: boolean = true
|
|
127
|
-
): ImgCompressInstance {
|
|
128
|
-
if (singleton && singletonInstance) {
|
|
129
|
-
return singletonInstance;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const instance = createCompressInstance(options);
|
|
133
|
-
|
|
134
|
-
if (singleton) {
|
|
135
|
-
singletonInstance = instance;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return instance;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* 获取单例实例(如果不存在则创建)
|
|
143
|
-
* @param options - 压缩配置选项(仅在首次创建时生效)
|
|
144
|
-
* @returns 单例实例
|
|
145
|
-
* @example
|
|
146
|
-
* ```ts
|
|
147
|
-
* const imgzip = getImgCompressInstance({ quality: 0.8 });
|
|
148
|
-
* ```
|
|
149
|
-
*/
|
|
150
|
-
function getImgCompressInstance(
|
|
151
|
-
options?: CompressOptions
|
|
152
|
-
): ImgCompressInstance {
|
|
153
|
-
if (!singletonInstance) {
|
|
154
|
-
singletonInstance = createCompressInstance(options);
|
|
155
|
-
}
|
|
156
|
-
return singletonInstance;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* 重置单例实例(用于测试或重新配置)
|
|
161
|
-
*/
|
|
162
|
-
function resetImgCompressInstance(): void {
|
|
163
|
-
singletonInstance = null;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export { createImgCompress, getImgCompressInstance, resetImgCompressInstance };
|
|
167
|
-
export default createImgCompress;
|