images-viewer-js 1.1.1 → 1.1.5
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/README.en.md +65 -18
- package/README.md +66 -19
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/index.d.ts +140 -62
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -49,16 +49,49 @@ const viewer4 = new ImagesViewer({
|
|
|
49
49
|
url: 'https://example.com/image1.jpg',
|
|
50
50
|
title: 'Landscape Image',
|
|
51
51
|
thumbnail: 'https://example.com/thumb1.jpg',
|
|
52
|
-
category: 'nature'
|
|
53
52
|
},
|
|
54
53
|
{
|
|
55
54
|
url: 'https://example.com/image2.jpg',
|
|
56
55
|
title: 'Architecture Image',
|
|
57
56
|
thumbnail: 'https://example.com/thumb2.jpg',
|
|
58
|
-
category: 'architecture'
|
|
59
57
|
}
|
|
60
58
|
]
|
|
61
59
|
});
|
|
60
|
+
|
|
61
|
+
// Custom property mapping
|
|
62
|
+
const viewer5 = new ImagesViewer({
|
|
63
|
+
images: [
|
|
64
|
+
{
|
|
65
|
+
url2: 'https://example.com/image1.jpg',
|
|
66
|
+
title2: 'Landscape Image',
|
|
67
|
+
thumbnail2: 'https://example.com/thumb1.jpg'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
url2: 'https://example.com/image2.jpg',
|
|
71
|
+
title2: 'Architecture Image',
|
|
72
|
+
thumbnail2: 'https://example.com/thumb2.jpg'
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
props: {
|
|
76
|
+
url: 'url2',
|
|
77
|
+
title: 'title2',
|
|
78
|
+
thumbnail: 'thumbnail2'
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Function form property mapping
|
|
83
|
+
const viewer6 = new ImagesViewer({
|
|
84
|
+
images: [
|
|
85
|
+
{ id: 1, path: 'photos/1.jpg', alt: 'Landscape' },
|
|
86
|
+
{ id: 2, path: 'photos/2.jpg', alt: 'Portrait' }
|
|
87
|
+
],
|
|
88
|
+
props: {
|
|
89
|
+
url: (item, index) => `https://example.com/${item.path}`,
|
|
90
|
+
title: (item, index) => `${item.alt} (ID: ${item.id})`,
|
|
91
|
+
thumbnail: (item, index) => `https://example.com/thumbnails/${item.id}.jpg`
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
62
95
|
```
|
|
63
96
|
|
|
64
97
|
### npm
|
|
@@ -136,6 +169,9 @@ const viewer = new ImagesViewer({
|
|
|
136
169
|
// Maximum cache size
|
|
137
170
|
maxCacheSize: 30,
|
|
138
171
|
|
|
172
|
+
// Whether to retry after failure
|
|
173
|
+
retryOnError: false,
|
|
174
|
+
|
|
139
175
|
// Button configuration
|
|
140
176
|
buttons: {
|
|
141
177
|
zoomIn: true, // Zoom in
|
|
@@ -162,32 +198,46 @@ const viewer = new ImagesViewer({
|
|
|
162
198
|
// Initial image index
|
|
163
199
|
initialIndex: 0,
|
|
164
200
|
|
|
201
|
+
// Custom property mapping
|
|
202
|
+
props: {
|
|
203
|
+
url: 'url', // Property name or getter function for image URL
|
|
204
|
+
title: 'title', // Property name or getter function for image title
|
|
205
|
+
thumbnail: 'thumbnail' // Property name or getter function for thumbnail URL
|
|
206
|
+
},
|
|
207
|
+
|
|
165
208
|
// Event callbacks
|
|
166
209
|
onShow: function(container) {
|
|
167
|
-
console.log('Viewer shown');
|
|
210
|
+
console.log('Viewer shown:', container);
|
|
168
211
|
},
|
|
169
212
|
|
|
170
213
|
onClose: function() {
|
|
171
214
|
console.log('Viewer closed');
|
|
172
215
|
},
|
|
173
216
|
|
|
174
|
-
onChange: function(
|
|
175
|
-
console.log('
|
|
217
|
+
onChange: function(data) {
|
|
218
|
+
console.log('Switched to image:', data.index);
|
|
219
|
+
console.log('From image:', data.oldIndex);
|
|
220
|
+
console.log('Direction:', data.direction);
|
|
176
221
|
},
|
|
177
222
|
|
|
178
|
-
// Rotation event
|
|
179
223
|
onRotate: function(data) {
|
|
180
|
-
console.log('Image rotated:', data);
|
|
224
|
+
console.log('Image rotated:', data.rotation);
|
|
181
225
|
},
|
|
182
226
|
|
|
183
|
-
// Drag event
|
|
184
227
|
onDrag: function(data) {
|
|
185
|
-
console.log('Image dragged:', data);
|
|
228
|
+
console.log('Image dragged:', data.translateX, data.translateY);
|
|
186
229
|
},
|
|
187
230
|
|
|
188
|
-
// Zoom event
|
|
189
231
|
onZoom: function(data) {
|
|
190
|
-
console.log('Image zoomed:', data);
|
|
232
|
+
console.log('Image zoomed:', data.scale);
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
onImageError: function(data) {
|
|
236
|
+
console.log('Image load failed:', data.url);
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
onThumbnailError: function(data) {
|
|
240
|
+
console.log('Thumbnail load failed:', data.url);
|
|
191
241
|
},
|
|
192
242
|
|
|
193
243
|
// Custom info bar function
|
|
@@ -454,13 +504,12 @@ const viewer = new ImagesViewer({
|
|
|
454
504
|
},
|
|
455
505
|
],
|
|
456
506
|
],
|
|
457
|
-
onChange: (
|
|
458
|
-
|
|
459
|
-
console.log(index, direction);
|
|
507
|
+
onChange: (data) => {
|
|
508
|
+
console.log('Image changed:', data.index, data.direction);
|
|
460
509
|
},
|
|
461
|
-
onShow:
|
|
510
|
+
onShow: (container) => {
|
|
462
511
|
// Custom button
|
|
463
|
-
const toolbar =
|
|
512
|
+
const toolbar = container.querySelector('.images-viewer-toolbar');
|
|
464
513
|
const button = document.createElement('button');
|
|
465
514
|
button.className = 'images-viewer-tool-btn';
|
|
466
515
|
|
|
@@ -470,10 +519,8 @@ const viewer = new ImagesViewer({
|
|
|
470
519
|
|
|
471
520
|
button.addEventListener('click', e => {
|
|
472
521
|
console.log('test');
|
|
473
|
-
// e.stopPropagation();
|
|
474
522
|
});
|
|
475
523
|
toolbar.appendChild(button);
|
|
476
|
-
console.log('onShow', dom);
|
|
477
524
|
},
|
|
478
525
|
onClose: () => {
|
|
479
526
|
console.log('close');
|
package/README.md
CHANGED
|
@@ -49,16 +49,49 @@ const viewer4 = new ImagesViewer({
|
|
|
49
49
|
url: 'https://example.com/image1.jpg',
|
|
50
50
|
title: '风景图片',
|
|
51
51
|
thumbnail: 'https://example.com/thumb1.jpg',
|
|
52
|
-
category: 'nature'
|
|
53
52
|
},
|
|
54
53
|
{
|
|
55
54
|
url: 'https://example.com/image2.jpg',
|
|
56
55
|
title: '建筑图片',
|
|
57
56
|
thumbnail: 'https://example.com/thumb2.jpg',
|
|
58
|
-
category: 'architecture'
|
|
59
57
|
}
|
|
60
58
|
]
|
|
61
59
|
});
|
|
60
|
+
|
|
61
|
+
// 自定义属性映射
|
|
62
|
+
const viewer5 = new ImagesViewer({
|
|
63
|
+
images: [
|
|
64
|
+
{
|
|
65
|
+
url2: 'https://example.com/image1.jpg',
|
|
66
|
+
title2: '风景图片',
|
|
67
|
+
thumbnail2: 'https://example.com/thumb1.jpg'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
url2: 'https://example.com/image2.jpg',
|
|
71
|
+
title2: '建筑图片',
|
|
72
|
+
thumbnail2: 'https://example.com/thumb2.jpg'
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
props: {
|
|
76
|
+
url: 'url2',
|
|
77
|
+
title: 'title2',
|
|
78
|
+
thumbnail: 'thumbnail2'
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// 函数形式的属性映射
|
|
83
|
+
const viewer6 = new ImagesViewer({
|
|
84
|
+
images: [
|
|
85
|
+
{ id: 1, path: 'photos/1.jpg', alt: '风景' },
|
|
86
|
+
{ id: 2, path: 'photos/2.jpg', alt: '人物' }
|
|
87
|
+
],
|
|
88
|
+
props: {
|
|
89
|
+
url: (item, index) => `https://example.com/${item.path}`,
|
|
90
|
+
title: (item, index) => `${item.alt} (ID: ${item.id})`,
|
|
91
|
+
thumbnail: (item, index) => `https://example.com/thumbnails/${item.id}.jpg`
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
62
95
|
```
|
|
63
96
|
|
|
64
97
|
### npm
|
|
@@ -136,6 +169,9 @@ const viewer = new ImagesViewer({
|
|
|
136
169
|
// 最大缓存数量
|
|
137
170
|
maxCacheSize: 30,
|
|
138
171
|
|
|
172
|
+
// 是否在失败后重新请求
|
|
173
|
+
retryOnError: false,
|
|
174
|
+
|
|
139
175
|
// 按钮配置
|
|
140
176
|
buttons: {
|
|
141
177
|
zoomIn: true, // 放大
|
|
@@ -154,7 +190,7 @@ const viewer = new ImagesViewer({
|
|
|
154
190
|
originalSize: true, // 原始尺寸
|
|
155
191
|
},
|
|
156
192
|
|
|
157
|
-
|
|
193
|
+
// 自定义按钮
|
|
158
194
|
customButtons: [
|
|
159
195
|
['🔍', function() { console.log('自定义按钮点击'); }]
|
|
160
196
|
],
|
|
@@ -162,32 +198,46 @@ const viewer = new ImagesViewer({
|
|
|
162
198
|
// 初始图片索引
|
|
163
199
|
initialIndex: 0,
|
|
164
200
|
|
|
201
|
+
// 自定义属性映射
|
|
202
|
+
props: {
|
|
203
|
+
url: 'url', // 图片 URL 的属性名或获取函数
|
|
204
|
+
title: 'title', // 图片标题的属性名或获取函数
|
|
205
|
+
thumbnail: 'thumbnail' // 缩略图 URL 的属性名或获取函数
|
|
206
|
+
},
|
|
207
|
+
|
|
165
208
|
// 事件回调
|
|
166
209
|
onShow: function(container) {
|
|
167
|
-
console.log('
|
|
210
|
+
console.log('查看器显示:', container);
|
|
168
211
|
},
|
|
169
212
|
|
|
170
213
|
onClose: function() {
|
|
171
214
|
console.log('查看器关闭');
|
|
172
215
|
},
|
|
173
216
|
|
|
174
|
-
onChange: function(
|
|
175
|
-
console.log('
|
|
217
|
+
onChange: function(data) {
|
|
218
|
+
console.log('图片切换到:', data.index);
|
|
219
|
+
console.log('从图片:', data.oldIndex);
|
|
220
|
+
console.log('切换方向:', data.direction);
|
|
176
221
|
},
|
|
177
222
|
|
|
178
|
-
// 旋转事件
|
|
179
223
|
onRotate: function(data) {
|
|
180
|
-
console.log('图片旋转:', data);
|
|
224
|
+
console.log('图片旋转:', data.rotation);
|
|
181
225
|
},
|
|
182
226
|
|
|
183
|
-
// 拖动事件
|
|
184
227
|
onDrag: function(data) {
|
|
185
|
-
console.log('图片拖动:', data);
|
|
228
|
+
console.log('图片拖动:', data.translateX, data.translateY);
|
|
186
229
|
},
|
|
187
230
|
|
|
188
|
-
// 缩放事件
|
|
189
231
|
onZoom: function(data) {
|
|
190
|
-
console.log('图片缩放:', data);
|
|
232
|
+
console.log('图片缩放:', data.scale);
|
|
233
|
+
},
|
|
234
|
+
|
|
235
|
+
onImageError: function(data) {
|
|
236
|
+
console.log('图片加载失败:', data.url);
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
onThumbnailError: function(data) {
|
|
240
|
+
console.log('缩略图加载失败:', data.url);
|
|
191
241
|
},
|
|
192
242
|
|
|
193
243
|
// 信息栏自定义函数
|
|
@@ -454,13 +504,12 @@ const viewer = new ImagesViewer({
|
|
|
454
504
|
},
|
|
455
505
|
],
|
|
456
506
|
],
|
|
457
|
-
onChange: (
|
|
458
|
-
|
|
459
|
-
console.log(index, direction);
|
|
507
|
+
onChange: (data) => {
|
|
508
|
+
console.log('图片切换:', data.index, data.direction);
|
|
460
509
|
},
|
|
461
|
-
onShow:
|
|
510
|
+
onShow: (container) => {
|
|
462
511
|
// 自定义按钮
|
|
463
|
-
const toolbar =
|
|
512
|
+
const toolbar = container.querySelector('.images-viewer-toolbar');
|
|
464
513
|
const button = document.createElement('button');
|
|
465
514
|
button.className = 'images-viewer-tool-btn';
|
|
466
515
|
|
|
@@ -470,10 +519,8 @@ const viewer = new ImagesViewer({
|
|
|
470
519
|
|
|
471
520
|
button.addEventListener('click', e => {
|
|
472
521
|
console.log('test');
|
|
473
|
-
// e.stopPropagation();
|
|
474
522
|
});
|
|
475
523
|
toolbar.appendChild(button);
|
|
476
|
-
console.log('onShow', dom);
|
|
477
524
|
},
|
|
478
525
|
onClose: () => {
|
|
479
526
|
console.log('close');
|
package/dist/index.esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class t{constructor(t){if(this.defaultOptions={initialIndex:0,closeOnMaskClick:!1,loop:!0,preloadCount:3,maxCacheSize:30,minScale:.1,maxScale:5,buttons:{zoomIn:!0,zoomOut:!0,rotateLeft:!0,rotateRight:!0,reset:!0,download:!0,fullscreen:!0,prev:!0,next:!0,close:!0,topClose:!0,thumbnails:!0,info:!0,originalSize:!0},imageInfo:{visible:!1,showName:!0,showDimensions:!0},i18n:{info:{name:"\u540d\u79f0:",dimensions:"\u5c3a\u5bf8:",shortcuts:"\u5feb\u6377\u952e",zoomIn:"\u653e\u5927:",zoomOut:"\u7f29\u5c0f:",prev:"\u4e0a\u4e00\u5f20:",next:"\u4e0b\u4e00\u5f20:",reset:"\u91cd\u7f6e:",fullscreen:"\u5168\u5c4f:",info:"\u4fe1\u606f:",close:"\u5173\u95ed:"},buttons:{prev:"\u4e0a\u4e00\u5f20 (\u2190)",next:"\u4e0b\u4e00\u5f20 (\u2192)",close:"\u5173\u95ed (Esc)",loading:"\u52a0\u8f7d\u4e2d..."}},theme:{viewerBgColor:"rgba(0, 0, 0, 0.4)",toolbarBgColor:"rgba(150, 150, 150, 0.7)",toolbarBorderRadius:"30px",toolbarPadding:"8px 12px",toolbarBottom:"20px",buttonBgColor:"rgba(150, 150, 150, 0.7)",buttonHoverBg:"rgba(200, 200, 200, 0.4)",buttonSize:"40px",buttonFontSize:"20px",buttonBorderRadius:"50%",navButtonBgColor:"rgba(150, 150, 150, 0.7)",navButtonHoverBg:"rgba(200, 200, 200, 0.4)",navButtonSize:"50px",navButtonFontSize:"20px",navButtonBorderRadius:"50%",topCloseBtnSize:"50px",topCloseBtnTop:"20px",topCloseBtnRight:"20px",topCloseBtnFontSize:"24px",topCloseBtnBgColor:"rgba(150, 150, 150, 0.7)",topCloseBtnHoverBg:"rgba(200, 200, 200, 0.4)",infoBgColor:"rgba(150, 150, 150, 0.7)",infoBorderRadius:"12px",infoPadding:"10px 15px",infoFontSize:"13px",infoTop:"70px",infoLeft:"20px",zoomIndicatorBg:"rgba(150, 150, 150, 0.7)",zoomIndicatorBorderRadius:"18px",zoomIndicatorPadding:"6px 12px",zoomIndicatorFontSize:"14px",zoomIndicatorTop:"20px",zoomIndicatorLeft:"20px",activeColor:"rgba(100, 150, 255, 0.8)",textColor:"rgba(255, 255, 255, 0.9)",shadowColor:"rgba(0, 0, 0, 0.2)",transitionSpeed:"0.3s",thumbItemWidth:"70px",thumbItemHeight:"45px",thumbGap:"10px",thumbPadding:"15px",thumbMaxWidth:"90%"}},this.options={...this.defaultOptions,...t,buttons:{...this.defaultOptions.buttons,...t?.buttons||{}},imageInfo:{...this.defaultOptions.imageInfo,...t?.imageInfo||{}},i18n:{...this.defaultOptions.i18n,...t?.i18n||{},info:{...this.defaultOptions.i18n.info,...t?.i18n?.info||{}},buttons:{...this.defaultOptions.i18n.buttons,...t?.i18n?.buttons||{}}},theme:{...this.defaultOptions.theme,...t?.theme||{}}},this.parseImageOptions(t),0===this.images.length)throw new Error("\u672a\u63d0\u4f9b\u6709\u6548\u7684\u56fe\u7247URL");const i=Math.max(0,Math.min(this.options.initialIndex||0,this.images.length-1));this.currentIndex=i,this.scale=1,this.rotation=0,this.translateX=0,this.translateY=0,this.isDragging=!1,this.startX=0,this.startY=0,this.startTranslateX=0,this.startTranslateY=0,this.isFullscreen=!1,this.imageInfoVisible=this.options.imageInfo.visible,this.imageMetadata=[],this.loadedImages=new Map,this.loadingImages=new Map,this.loadedThumbnails=new Map,this.loadingThumbnails=new Map,this.lastTapTime=0,this.lastScale=1,this.lastTranslateX=0,this.lastTranslateY=0,this.hasPreviousState=!1,this.isToggledState=!1,this.touchState={isDragging:!1,isPinching:!1,initialDistance:null,initialScale:null,initialTranslateX:null,initialTranslateY:null,centerX:null,centerY:null,relativeCenterX:null,relativeCenterY:null,lastTouchTime:0,startX:0,startY:0,startTranslateX:0,startTranslateY:0,minScaleChange:.005,scaleRatio:1,stabilizationThreshold:3,movementCount:0},this.eventListeners=new Map,this.injectStyles(),this.preloadImages(),this.createOptimizedElements(),this.bindEvents(),this.onShow(),this.loadCurrentImage()}injectStyles(){const t=document.createElement("style");t.id="images-viewer-styles",t.textContent=`\n :root {\n /* \u80cc\u666f\u76f8\u5173\u53d8\u91cf */\n --viewer-bg-color: ${this.options.theme.viewerBgColor};\n \n /* \u5de5\u5177\u680f\u76f8\u5173\u53d8\u91cf */\n --toolbar-bg-color: ${this.options.theme.toolbarBgColor};\n --toolbar-border-radius: ${this.options.theme.toolbarBorderRadius};\n --toolbar-padding: ${this.options.theme.toolbarPadding};\n --toolbar-bottom: ${this.options.theme.toolbarBottom};\n \n /* \u6309\u94ae\u76f8\u5173\u53d8\u91cf */\n --button-bg-color: ${this.options.theme.buttonBgColor};\n --button-hover-bg: ${this.options.theme.buttonHoverBg};\n --button-size: ${this.options.theme.buttonSize};\n --button-font-size: ${this.options.theme.buttonFontSize};\n --button-border-radius: ${this.options.theme.buttonBorderRadius};\n\n /* \u5bfc\u822a\u6309\u94ae\u76f8\u5173\u53d8\u91cf */\n --nav-button-bg-color: ${this.options.theme.navButtonBgColor};\n --nav-button-hover-bg: ${this.options.theme.navButtonHoverBg};\n --nav-button-size: ${this.options.theme.navButtonSize};\n --nav-button-font-size: ${this.options.theme.navButtonFontSize};\n --nav-button-border-radius: ${this.options.theme.navButtonBorderRadius};\n \n /* \u53f3\u4e0a\u89d2\u5173\u95ed\u6309\u94ae\u53d8\u91cf */\n --top-close-btn-size: ${this.options.theme.topCloseBtnSize};\n --top-close-btn-top: ${this.options.theme.topCloseBtnTop};\n --top-close-btn-right: ${this.options.theme.topCloseBtnRight};\n --top-close-btn-font-size: ${this.options.theme.topCloseBtnFontSize};\n --top-close-btn-bg-color: ${this.options.theme.topCloseBtnBgColor};\n --top-close-btn-hover-bg: ${this.options.theme.topCloseBtnHoverBg};\n \n /* \u4fe1\u606f\u680f\u76f8\u5173\u53d8\u91cf */\n --info-bg-color: ${this.options.theme.infoBgColor};\n --info-border-radius: ${this.options.theme.infoBorderRadius};\n --info-padding: ${this.options.theme.infoPadding};\n --info-font-size: ${this.options.theme.infoFontSize};\n --info-top: ${this.options.theme.infoTop};\n --info-left: ${this.options.theme.infoLeft};\n \n /* \u7f29\u653e\u6307\u793a\u5668\u53d8\u91cf */\n --zoom-indicator-bg: ${this.options.theme.zoomIndicatorBg};\n --zoom-indicator-border-radius: ${this.options.theme.zoomIndicatorBorderRadius};\n --zoom-indicator-padding: ${this.options.theme.zoomIndicatorPadding};\n --zoom-indicator-font-size: ${this.options.theme.zoomIndicatorFontSize};\n --zoom-indicator-top: ${this.options.theme.zoomIndicatorTop};\n --zoom-indicator-left: ${this.options.theme.zoomIndicatorLeft};\n \n /* \u901a\u7528\u53d8\u91cf */\n --active-color: ${this.options.theme.activeColor};\n --text-color: ${this.options.theme.textColor};\n --shadow-color: ${this.options.theme.shadowColor};\n --transition-speed: ${this.options.theme.transitionSpeed};\n \n /* \u7f29\u7565\u56fe\u76f8\u5173\u53d8\u91cf */\n --thumb-max-width: ${this.options.theme.thumbMaxWidth};\n --thumb-gap: ${this.options.theme.thumbGap};\n --thumb-padding: ${this.options.theme.thumbPadding};\n --thumb-item-width: ${this.options.theme.thumbItemWidth};\n --thumb-item-height: ${this.options.theme.thumbItemHeight};\n }\n\n .images-viewer-container {\n position: fixed;\n left: 0;\n top: 0;\n inset: 0;\n width: 100%;\n height: 100%;\n z-index: 9999;\n opacity: 0;\n transition: opacity var(--transition-speed) ease;\n touch-action: none;\n -webkit-user-select: none;\n user-select: none;\n display: none;\n background-color: var(--viewer-bg-color);\n overflow: hidden;\n }\n\n .images-viewer-container::backdrop,\n .images-viewer-container:fullscreen {\n background-color: var(--viewer-bg-color);\n }\n\n /* \u56fe\u7247\u5bb9\u5668\u6837\u5f0f */\n .images-viewer-image-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n }\n\n /* \u56fe\u7247\u6837\u5f0f */\n .images-viewer-image {\n position: relative;\n object-fit: contain;\n cursor: grab;\n transition: transform 0.1s ease-out, opacity var(--transition-speed) ease;\n transform-origin: center center;\n opacity: 0;\n box-shadow: 0 8px 25px var(--shadow-color);\n border-radius: 4px;\n user-select: none;\n touch-action: none;\n }\n\n .images-viewer-image.loaded {\n opacity: 1;\n }\n\n .images-viewer-image.dragging {\n cursor: grabbing;\n transition: none;\n }\n\n /* \u52a0\u8f7d\u6307\u793a\u5668 */\n .images-viewer-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background-color: rgba(127, 127, 127, 0.7);\n padding: 20px 30px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n gap: 15px;\n color: var(--text-color);\n font-size: 18px;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--transition-speed) ease;\n z-index: 3;\n }\n\n .images-viewer-loading.active {\n opacity: 1;\n }\n\n .images-viewer-loading-spinner {\n width: 40px;\n height: 40px;\n border: 4px solid rgba(255, 255, 255, 0.2);\n border-top-color: var(--active-color);\n border-radius: 50%;\n animation: imageViewerSpin 1s linear infinite;\n }\n\n @keyframes imageViewerSpin {\n to {\n transform: rotate(360deg);\n }\n }\n\n /* \u53f3\u4e0a\u89d2\u5173\u95ed\u6309\u94ae\u6837\u5f0f */\n .images-viewer-top-close-btn {\n position: absolute;\n top: var(--top-close-btn-top);\n right: var(--top-close-btn-right);\n width: var(--top-close-btn-size);\n height: var(--top-close-btn-size);\n border-radius: 50%;\n background-color: var(--button-bg-color);\n color: var(--text-color);\n border: none;\n font-size: var(--top-close-btn-font-size);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all var(--transition-speed);\n z-index: 10;\n box-shadow: 0 2px 8px var(--shadow-color);\n }\n\n .images-viewer-top-close-btn:hover {\n background-color: var(--top-close-btn-hover-bg);\n transform: scale(1.1);\n }\n\n /* \u7f29\u653e\u6307\u793a\u5668\u6837\u5f0f */\n .images-viewer-zoom-indicator {\n position: absolute;\n top: var(--zoom-indicator-top);\n left: var(--zoom-indicator-left);\n color: var(--text-color);\n background-color: var(--zoom-indicator-bg);\n padding: var(--zoom-indicator-padding);\n border-radius: var(--zoom-indicator-border-radius);\n font-size: var(--zoom-indicator-font-size);\n z-index: 10;\n min-width: 60px;\n text-align: center;\n box-shadow: 0 2px 8px var(--shadow-color);\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n /* \u4fe1\u606f\u680f\u6837\u5f0f */\n .images-viewer-image-info {\n position: absolute;\n top: var(--info-top);\n left: var(--info-left);\n color: var(--text-color);\n background-color: var(--info-bg-color);\n padding: var(--info-padding);\n border-radius: var(--info-border-radius);\n font-size: var(--info-font-size);\n z-index: 10;\n max-width: calc(100% - 40px);\n box-shadow: 0 4px 12px var(--shadow-color);\n display: none;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .images-viewer-image-info.visible {\n display: block;\n animation: imageViewerFadeIn 0.3s ease;\n }\n\n .images-viewer-image-info p {\n margin: 4px 0;\n line-height: 1.4;\n }\n\n .images-viewer-image-info .info-label {\n color: #ddd;\n margin-right: 5px;\n }\n\n .images-viewer-shortcuts-title {\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n font-weight: bold;\n margin-bottom: 5px;\n }\n\n @keyframes imageViewerFadeIn {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* \u56fe\u7247\u8ba1\u6570\u5668 */\n .images-viewer-image-counter {\n position: absolute;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n color: var(--text-color);\n background-color: var(--info-bg-color);\n padding: 6px 12px;\n border-radius: 18px;\n font-size: 14px;\n z-index: 10;\n box-shadow: 0 2px 8px var(--shadow-color);\n }\n\n /* \u56fe\u7247\u6807\u9898\u663e\u793a */\n .images-viewer-image-title {\n position: absolute;\n top: 60px;\n left: 50%;\n transform: translateX(-50%);\n color: var(--text-color);\n padding: 4px 12px;\n border-radius: 18px;\n font-size: 12px;\n z-index: 10;\n max-width: 80%;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* \u5bfc\u822a\u6309\u94ae */\n .images-viewer-nav-buttons {\n position: absolute;\n top: 50%;\n left: 0;\n right: 0;\n transform: translateY(-50%);\n display: flex;\n justify-content: space-between;\n pointer-events: none;\n z-index: 5;\n padding: 0 10px;\n }\n\n .images-viewer-nav-btn {\n width: var(--nav-button-size);\n height: var(--nav-button-size);\n border-radius: var(--nav-button-border-radius);\n background-color: var(--nav-button-bg-color);\n color: var(--text-color);\n border: none;\n font-size: var(--nav-button-font-size);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n pointer-events: auto;\n opacity: 0.9;\n box-shadow: 0 2px 8px var(--shadow-color);\n z-index: 6;\n }\n\n .images-viewer-nav-btn:hover {\n background-color: var(--nav-button-hover-bg);\n opacity: 1;\n transform: scale(1.1);\n }\n\n .images-viewer-nav-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n transform: none;\n }\n\n /* \u5de5\u5177\u680f\u6837\u5f0f */\n .images-viewer-toolbar {\n position: absolute;\n bottom: var(--toolbar-bottom);\n left: 50%;\n transform: translateX(-50%);\n background-color: var(--toolbar-bg-color);\n padding: var(--toolbar-padding);\n border-radius: var(--toolbar-border-radius);\n display: flex;\n gap: 2px;\n z-index: 10;\n box-shadow: 0 6px 25px var(--shadow-color);\n max-width: calc(100% - 40px);\n overflow-x: auto;\n overflow-y: hidden;\n border: 1px solid rgba(255, 255, 255, 0.1);\n scrollbar-width: none;\n -ms-overflow-style: none;\n -webkit-overflow-scrolling: touch;\n }\n\n .images-viewer-toolbar::-webkit-scrollbar {\n display: none;\n }\n\n .images-viewer-tool-btn {\n width: var(--button-size);\n height: var(--button-size);\n background-color: transparent;\n border: none;\n color: var(--text-color);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--button-font-size);\n transition: all 0.2s;\n flex-shrink: 0;\n position: relative;\n border-radius: var(--button-border-radius);\n margin: 0 2px;\n z-index: 11;\n line-height: 1;\n }\n\n .images-viewer-tool-btn:hover {\n background-color: var(--button-hover-bg);\n transform: translateY(-2px);\n box-shadow: 0 4px 10px var(--shadow-color);\n }\n\n .images-viewer-tool-btn:active {\n background-color: rgba(255, 255, 255, 0.3);\n transform: translateY(0);\n }\n\n .images-viewer-tool-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* \u7f29\u7565\u56fe\u5bb9\u5668 */\n .images-viewer-thumbnails-container {\n position: absolute;\n bottom: 90px;\n left: 50%;\n max-width: var(--thumb-max-width);\n transform: translateX(-50%);\n padding: 10px var(--thumb-padding);\n background-color: var(--toolbar-bg-color);\n border-radius: 12px;\n display: flex;\n gap: var(--thumb-gap);\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n z-index: 10;\n box-shadow: 0 3px 15px var(--shadow-color);\n -webkit-overflow-scrolling: touch;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .images-viewer-thumbnails-container::-webkit-scrollbar {\n display: none;\n }\n\n .images-viewer-thumbnail-item {\n width: var(--thumb-item-width);\n height: var(--thumb-item-height);\n border: 2px solid transparent;\n border-radius: 6px;\n overflow: hidden;\n cursor: pointer;\n flex-shrink: 0;\n transition: all 0.2s;\n z-index: 11;\n position: relative;\n opacity: 0.6;\n }\n\n .images-viewer-thumbnail-item img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .images-viewer-thumbnail-item.active {\n border-color: var(--active-color);\n transform: scale(1.2);\n opacity: 1;\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n }\n\n /* \u7f29\u7565\u56fe\u52a0\u8f7d\u72b6\u6001 */\n .images-viewer-thumbnail-loading {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.3);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1;\n }\n\n .loading-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top: 2px solid var(--active-color);\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .loading-error {\n color: #ff6b6b;\n font-size: 10px;\n text-align: center;\n padding: 2px;\n }\n\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .images-viewer-thumbnail-item:hover {\n transform: scale(1.2);\n opacity: 0.9;\n }\n `,document.head.appendChild(t)}createOptimizedElements(){this.container=document.createElement("div"),this.container.className="images-viewer-container",document.body.appendChild(this.container),this.imageContainer=document.createElement("div"),this.imageContainer.className="images-viewer-image-container",this.container.appendChild(this.imageContainer),this.image=document.createElement("img"),this.image.className="images-viewer-image",this.image.alt="Preview image",this.image.crossOrigin="anonymous",this.imageContainer.appendChild(this.image),this.loading=document.createElement("div"),this.loading.className="images-viewer-loading",this.loading.innerHTML=`\n <div class="images-viewer-loading-spinner"></div>\n <div>${this.options.i18n.buttons.loading}</div>\n `,this.imageContainer.appendChild(this.loading),this.options.buttons.topClose&&(this.topCloseBtn=document.createElement("button"),this.topCloseBtn.className="images-viewer-top-close-btn",this.topCloseBtn.textContent="\xd7",this.topCloseBtn.title=this.options.i18n.buttons.close,this.container.appendChild(this.topCloseBtn)),this.zoomIndicator=document.createElement("div"),this.zoomIndicator.className="images-viewer-zoom-indicator",this.zoomIndicator.textContent="100%",this.container.appendChild(this.zoomIndicator),this.options.buttons.info&&(this.imageInfoPanel=document.createElement("div"),this.imageInfoPanel.className="images-viewer-image-info "+(this.imageInfoVisible?"visible":""),this.container.appendChild(this.imageInfoPanel)),this.images.length>1&&(this.counter=document.createElement("div"),this.counter.className="images-viewer-image-counter",this.container.appendChild(this.counter)),this.imageTitle=document.createElement("div"),this.imageTitle.className="images-viewer-image-title",this.container.appendChild(this.imageTitle),this.images.length>1&&(this.options.buttons.prev||this.options.buttons.next)&&this.createNavButtons(),this.images.length>1&&this.options.buttons.thumbnails&&this.createThumbnails(),this.createToolbar()}createNavButtons(){const t=document.createElement("div");t.className="images-viewer-nav-buttons",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.options.buttons.prev&&(this.prevBtn=document.createElement("button"),this.prevBtn.className="images-viewer-nav-btn images-viewer-prev-btn",this.prevBtn.textContent="\u2190",this.prevBtn.title=this.options.i18n.buttons.prev,this.addEvent(this.prevBtn,"click",t=>{t.stopPropagation(),this.prev()}),t.appendChild(this.prevBtn)),this.options.buttons.next&&(this.nextBtn=document.createElement("button"),this.nextBtn.className="images-viewer-nav-btn images-viewer-next-btn",this.nextBtn.textContent="\u2192",this.nextBtn.title=this.options.i18n.buttons.next,this.addEvent(this.nextBtn,"click",t=>{t.stopPropagation(),this.next()}),t.appendChild(this.nextBtn)),this.container.appendChild(t)}createToolbar(){const t=document.createElement("div");t.className="images-viewer-toolbar",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.addEvent(t,"wheel",i=>{i.preventDefault(),i.stopPropagation(),t.scrollLeft+=i.deltaY}),this.images.length>1&&(this.options.buttons.prev&&(this.toolbarPrevBtn=this.createToolButton("\u2190",()=>this.prev()),t.appendChild(this.toolbarPrevBtn)),this.options.buttons.next&&(this.toolbarNextBtn=this.createToolButton("\u2192",()=>this.next()),t.appendChild(this.toolbarNextBtn))),this.options.buttons.zoomOut&&(this.zoomOutBtn=this.createToolButton("\u2212",()=>this.zoom(-.1)),t.appendChild(this.zoomOutBtn)),this.options.buttons.zoomIn&&(this.zoomInBtn=this.createToolButton("+",()=>this.zoom(.1)),t.appendChild(this.zoomInBtn)),this.options.buttons.rotateLeft&&(this.rotateLeftBtn=this.createToolButton("\u21ba",()=>this.rotate(-90)),t.appendChild(this.rotateLeftBtn)),this.options.buttons.rotateRight&&(this.rotateRightBtn=this.createToolButton("\u21bb",()=>this.rotate(90)),t.appendChild(this.rotateRightBtn)),this.options.buttons.reset&&(this.resetBtn=this.createToolButton("\u27f3",()=>this.reset()),t.appendChild(this.resetBtn)),this.options.buttons.originalSize&&(this.originalSizeBtn=this.createToolButton("1:1",()=>this.showOriginalSize()),t.appendChild(this.originalSizeBtn)),this.options.buttons.info&&(this.infoBtn=this.createToolButton("\u24d8",()=>this.toggleImageInfo()),t.appendChild(this.infoBtn)),this.options.buttons.download&&(this.downloadBtn=this.createToolButton("\u21a1",()=>this.downloadImage()),t.appendChild(this.downloadBtn)),this.options.buttons.fullscreen&&(this.fullscreenBtn=this.createToolButton("\u26f6",()=>this.toggleFullscreen()),t.appendChild(this.fullscreenBtn)),this.options.buttons.close&&(this.closeBtn=this.createToolButton("\xd7",()=>this.close()),t.appendChild(this.closeBtn)),this.options.customButtons&&this.options.customButtons.forEach(i=>{t.appendChild(this.createToolButton(i[0],i[1]))}),this.container.appendChild(t)}createToolButton(t,i){const s=document.createElement("button");s.className="images-viewer-tool-btn";const n=document.createElement("span");return n.textContent=t,s.appendChild(n),this.addEvent(s,"click",t=>{t.stopPropagation(),i()}),s}createThumbnails(){const t=document.createElement("div");t.className="images-viewer-thumbnails-container",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.addEvent(t,"wheel",i=>{i.preventDefault(),i.stopPropagation(),t.scrollLeft+=i.deltaY});const i=parseInt(this.options.theme.thumbItemWidth),s=parseInt(this.options.theme.thumbGap),n=parseInt(this.options.theme.thumbPadding),e=this.options.preloadCount,o=window.innerWidth-2*n,h=Math.ceil(o/(i+s)),a=Math.min(h+e,this.images.length);for(let i=0;i<a;i++)this.createThumbnailItem(t,i);let r=a;this.addEvent(t,"scroll",()=>{const i=t.scrollLeft,s=t.clientWidth;if(i>t.scrollWidth-s-s&&r<this.images.length){const i=Math.min(h,this.images.length-r);for(let s=r;s<r+i;s++)this.createThumbnailItem(t,s);r+=i}}),this.container.appendChild(t),this.thumbContainer=t}createThumbnailItem(t,i){const s=this.getThumbnailUrl(i),n=this.getImageUrl(i),e=document.createElement("div");e.className="images-viewer-thumbnail-item "+(i===this.currentIndex?"active":""),e.dataset.index=i;const o=new Image;o.crossOrigin="anonymous";const h=document.createElement("div");h.className="images-viewer-thumbnail-loading",h.innerHTML='<div class="loading-spinner"></div>',e.appendChild(h),this.loadedThumbnails.has(s)?(o.src=this.loadedThumbnails.get(s).src,h.remove()):this.loadingThumbnails.has(s)?(h.style.display="flex",o.onload=()=>{h.remove(),o.onload=null,o.onerror=null},o.onerror=()=>{h.remove(),o.onload=null,o.onerror=null},o.src=s):this.loadedImages.has(n)?(o.src=this.loadedImages.get(n).src,h.remove()):(this.loadingThumbnails.set(s,o),h.style.display="flex",o.onload=()=>{this.loadedThumbnails.set(s,o),this.loadingThumbnails.delete(s),h.remove(),o.onload=null,o.onerror=null},o.onerror=()=>{this.loadingThumbnails.delete(s),h.remove(),o.onload=null,o.onerror=null},o.src=s),e.appendChild(o),this.addEvent(e,"click",t=>{t.stopPropagation();const i=parseInt(e.dataset.index);i!==this.currentIndex&&(this.currentIndex=i,this.loadCurrentImage(),this.updateThumbnails())}),t.appendChild(e)}updateThumbnailDisplay(t){if(!this.thumbContainer)return;const i=this.thumbContainer.querySelector(`[data-index="${t}"]`);if(i){const s=this.getThumbnailUrl(t),n=i.querySelector("img");if(n&&this.loadedThumbnails.has(s)){const t=this.loadedThumbnails.get(s);n.src=t.src;const e=i.querySelector(".images-viewer-thumbnail-loading");e&&e.remove()}}}updateImageTransform(){const t=`\n translate(${this.translateX}px, ${this.translateY}px)\n scale(${this.scale})\n rotate(${this.rotation}deg)\n `;this.image.style.transform=t}fitImageToScreen(t,i){this.scale=1,this.translateX=0,this.translateY=0;const s=this.imageContainer.clientWidth,n=this.imageContainer.clientHeight,e=this.rotation%360;let o=t,h=i;if(90!==e&&270!==e||(o=i,h=t),o>s||h>n){const t=s/o,i=n/h;this.scale=Math.min(t,i)}this.scale=Math.max(.1,this.scale),this.updateImageTransform(),this.updateZoomIndicator()}parseImageOptions(t){this.images=[],this.thumbnails=[],"string"==typeof t?this.images=[t]:Array.isArray(t)?this.images=t.filter(t=>"string"==typeof t&&""!==t.trim()):"object"==typeof t&&t.images&&Array.isArray(t.images)&&(this.images=t.images.map(t=>"string"==typeof t?t:"object"==typeof t&&t.url?(t.thumbnail&&this.thumbnails.push(t.thumbnail),t):null).filter(Boolean))}getImageUrl(t){const i=this.images[t];if("string"==typeof i)return i;if("object"==typeof i){if("function"==typeof i.url)return i.url(i,t);if(i.url)return i.url}return""}getThumbnailUrl(t){if(this.thumbnails[t]){const i=this.thumbnails[t];return"function"==typeof i?i(this.images[t],t):i}return this.getImageUrl(t)}getImageTitle(t){const i=this.images[t];if("object"==typeof i){if("function"==typeof i.title)return i.title(i,t);if(i.title)return i.title}return""}preloadImages(){const t=this.options.preloadCount;if(t<=0)return;const i=Math.max(0,this.currentIndex-t),s=Math.min(this.images.length-1,this.currentIndex+t);for(let t=i;t<=s;t++)this.loadImageAtIndex(t)}addToCache(t,i){this.loadedImages.set(t,i);const s=this.options.maxCacheSize;if(s>0&&this.loadedImages.size>s){const t=this.loadedImages.keys().next().value;this.loadedImages.delete(t)}}loadImageAtIndex(t){const i=this.getImageUrl(t);if(!i||this.loadedImages.has(i)||this.loadingImages.has(i))return;const s=new Image;s.onload=()=>{this.addToCache(i,s),this.loadingImages.delete(i),this.imageMetadata[t]={name:this.extractFileName(i),width:s.naturalWidth||s.width,height:s.naturalHeight||s.height},s.onload=null,s.onerror=null},s.onerror=()=>{this.loadingImages.delete(i),s.onload=null,s.onerror=null},s.crossOrigin="anonymous",s.src=i,this.loadingImages.set(i,s)}extractFileName(t){try{const i=new URL(t).pathname.split("/");let s=i[i.length-1];const n=s.indexOf("?");return n>-1&&(s=s.substring(0,n)),s||"unknown-image"}catch(t){return"unknown-image"}}loadCurrentImage(t){if(void 0!==t&&(this.currentIndex=t),this.hasPreviousState=!1,this.isToggledState=!1,this.images.length>1&&this.counter){let t=null;this.options.onCounter&&(t=this.options.onCounter({image:this.images[this.currentIndex],index:this.currentIndex,currentPage:this.currentIndex+1,totalPages:this.images.length,scale:this.scale,rotation:this.rotation})),t?this.counter.innerHTML=t:this.counter.textContent=`${this.currentIndex+1} / ${this.images.length}`}const i=this.getImageTitle(this.currentIndex);this.imageTitle&&i?(this.imageTitle.innerHTML=i,this.imageTitle.style.display="block"):this.imageTitle.style.display="none",this.updateNavButtons(),this.updateThumbnails(),this.scale=1,this.rotation=0,this.translateX=0,this.translateY=0;const s=this.getImageUrl(this.currentIndex);this.loadedImages.has(s)||this.loadingImages.has(s)||this.loadImageAtIndex(this.currentIndex),this.preloadAdjacentImages(),this.isImageLoaded()}preloadAdjacentImages(){const t=Math.max(0,this.currentIndex-2),i=Math.min(this.images.length-1,this.currentIndex+2);for(let s=t;s<=i;s++)this.loadThumbnailAtIndex(s);for(let s=t;s<=i;s++)this.loadImageAtIndex(s)}loadThumbnailAtIndex(t){const i=this.getThumbnailUrl(t);if(!i||this.loadedThumbnails.has(i)||this.loadingThumbnails.has(i))return;const s=new Image;s.onload=()=>{this.loadedThumbnails.set(i,s),this.loadingThumbnails.delete(i),this.updateThumbnailDisplay(t),s.onload=null,s.onerror=null},s.onerror=()=>{this.loadingThumbnails.delete(i),s.onload=null,s.onerror=null},s.crossOrigin="anonymous",s.src=i,this.loadingThumbnails.set(i,s)}isImageLoaded(){const t=this.getImageUrl(this.currentIndex),i=this.loadedImages.has(t),s=this.loadingImages.has(t);if(this.showLoading(),this.image.classList.remove("loaded"),i){this.image.src="";const i=this.loadedImages.get(t),s=document.createElement("canvas"),n=s.getContext("2d");s.width=i.naturalWidth||i.width,s.height=i.naturalHeight||i.height,n.drawImage(i,0,0),s.toBlob(t=>{if(t){const i=URL.createObjectURL(t);this.image.src=i,this.t&&(URL.revokeObjectURL(this.t),this.t=null),this.t=i}});const e=this.imageMetadata[this.currentIndex];return e&&(this.fitImageToScreen(e.width,e.height),this.updateImageInfo()),this.image.classList.add("loaded"),void this.hideLoading()}if(s){const i=()=>{this.loadedImages.has(t)?this.isImageLoaded():this.loadingImages.has(t)?setTimeout(i,100):this.hideLoading()};return void setTimeout(i,100)}const n=new Image;n.onload=()=>{this.addToCache(t,n),this.loadingImages.delete(t),this.imageMetadata[this.currentIndex]={name:this.extractFileName(t),width:n.naturalWidth||n.width,height:n.naturalHeight||n.height},this.isImageLoaded()},n.onerror=()=>{this.loadingImages.delete(t),this.hideLoading()},n.crossOrigin="anonymous",n.src=t,this.loadingImages.set(t,n)}updateZoomIndicator(){const t=Math.round(100*this.scale);if(this.options.onZoomIndicator){const i=this.options.onZoomIndicator({image:this.images[this.currentIndex],index:this.currentIndex,scale:this.scale,percentage:t,rotation:this.rotation});if(i)return void(this.zoomIndicator.innerHTML=i)}this.zoomIndicator.textContent=`${t}%`}updateImageInfo(){if(!this.options.buttons.info||!this.imageInfoPanel)return;const t=this.imageMetadata[this.currentIndex];if(!t)return;const i=this.options.i18n;let s="";this.options.onInfo&&(s=this.options.onInfo({image:this.images[this.currentIndex],index:this.currentIndex,metadata:t,scale:this.scale,rotation:this.rotation}),s)||(this.options.imageInfo.showName&&(s+=`<p><span class="info-label">${i.info.name}</span> ${t.name}</p>`),this.options.imageInfo.showDimensions&&(s+=`<p><span class="info-label">${i.info.dimensions}</span> ${t.width} \xd7 ${t.height}</p>`),s+=`\n <div class="images-viewer-shortcuts-title">${i.info.shortcuts}</div>\n <p><span class="info-label">${i.info.zoomIn}</span> \u2191 +</p>\n <p><span class="info-label">${i.info.zoomOut}</span> \u2193 -</p>\n <p><span class="info-label">${i.info.prev}</span> \u2190</p>\n <p><span class="info-label">${i.info.next}</span> \u2192</p>\n <p><span class="info-label">${i.info.reset}</span> 0</p>\n <p><span class="info-label">${i.info.fullscreen}</span> F</p>\n <p><span class="info-label">${i.info.info}</span> I</p>\n <p><span class="info-label">${i.info.close}</span> ESC</p>\n `),this.imageInfoPanel.innerHTML=s}toggleImageInfo(){this.options.buttons.info&&this.imageInfoPanel&&(this.imageInfoVisible=!this.imageInfoVisible,this.imageInfoVisible?this.imageInfoPanel.classList.add("visible"):this.imageInfoPanel.classList.remove("visible"))}updateNavButtons(){if(this.images.length<=1)return;const t=!!this.options.loop||this.currentIndex>0,i=!!this.options.loop||this.currentIndex<this.images.length-1;this.prevBtn&&(this.prevBtn.disabled=!t),this.nextBtn&&(this.nextBtn.disabled=!i),this.toolbarPrevBtn&&(this.toolbarPrevBtn.disabled=!t),this.toolbarNextBtn&&(this.toolbarNextBtn.disabled=!i)}updateThumbnails(){if(this.images.length<=1)return;document.querySelectorAll(".images-viewer-thumbnail-item").forEach(t=>{t.classList.remove("active")});const t=document.querySelector(`.images-viewer-thumbnail-item[data-index="${this.currentIndex}"]`);t&&(t.classList.add("active"),t.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center"}))}showLoading(){this.loading&&this.loading.classList.add("active")}hideLoading(){this.loading&&this.loading.classList.remove("active")}prev(){if(this.images.length<=1)return;let t=this.currentIndex-1;t<0&&(t=this.options.loop?this.images.length-1:0),t!==this.currentIndex&&(this.currentIndex=t,this.loadCurrentImage()),this.options.onChange&&this.options.onChange(this.currentIndex,"prev")}next(){if(this.images.length<=1)return;let t=this.currentIndex+1;t>=this.images.length&&(t=this.options.loop?0:this.images.length-1),t!==this.currentIndex&&(this.currentIndex=t,this.loadCurrentImage()),this.options.onChange&&this.options.onChange(this.currentIndex,"next")}bindEvents(){this.topCloseBtn&&this.addEvent(this.topCloseBtn,"click",()=>this.close()),this.options.closeOnMaskClick&&this.addEvent(this.imageContainer,"click",t=>{t.target==this.imageContainer&&this.close()}),this.addEvent(document,"keydown",t=>this.handleKeydown(t));const t=function(t){let i,s=0;return function(...n){const e=Date.now(),o=e-s;o>300?(t.apply(this,n),s=e):(clearTimeout(i),i=setTimeout(()=>{t.apply(this,n),s=e},300-o))}}(()=>{this.handleResize()});this.addEvent(window,"resize",t),this.bindDragEvents(),this.bindTouchEvents()}addEvent(t,i,s,n){t.addEventListener(i,s,n);const e=`${i}-${Date.now()}-${Math.random()}`;this.eventListeners.set(e,{element:t,event:i,handler:s})}removeAllEvents(){this.eventListeners.forEach(({element:t,event:i,handler:s})=>{t.removeEventListener(i,s)}),this.eventListeners.clear()}rotatePoint(t,i,s){const n=s*Math.PI,e=Math.cos(n),o=Math.sin(n);return{x:t*e-i*o,y:t*o+i*e}}bindDragEvents(){this.addEvent(this.image,"mousedown",t=>{0===t.button&&(this.isDragging=!0,this.image.classList.add("dragging"),this.startX=t.clientX,this.startY=t.clientY,this.startTranslateX=this.translateX,this.startTranslateY=this.translateY,t.preventDefault())}),this.addEvent(document,"mousemove",t=>{if(!this.isDragging)return;const i=t.clientX-this.startX,s=t.clientY-this.startY,n=this.rotatePoint(i,s,-this.rotation);if(this.translateX=this.startTranslateX+n.x,this.translateY=this.startTranslateY+n.y,this.updateImageTransform(),this.options.onDrag){const t=this.images[this.currentIndex];this.options.onDrag({image:t,index:this.currentIndex,translateX:this.translateX,translateY:this.translateY})}t.preventDefault()}),this.addEvent(document,"mouseup",()=>{this.isDragging&&(this.isDragging=!1,this.image.classList.remove("dragging"))}),this.addEvent(document,"mouseleave",()=>{this.isDragging&&(this.isDragging=!1,this.image.classList.remove("dragging"))}),this.addEvent(this.imageContainer,"wheel",t=>{t.preventDefault();const i=this.imageContainer.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top,e=t.deltaY>0?-.05:.05;this.zoomAtPoint(e,s,n)}),this.addEvent(this.image,"dblclick",t=>{t.preventDefault();const i=this.imageContainer.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top;if(Math.abs(this.scale-1)<.01)if(this.hasPreviousState)this.scale=this.lastScale,this.translateX=this.lastTranslateX,this.translateY=this.lastTranslateY,this.hasPreviousState=!1;else{this.lastScale=this.scale,this.lastTranslateX=this.translateX,this.lastTranslateY=this.translateY,this.hasPreviousState=!0;const t=1.5,i=t/this.scale,e=this.imageContainer.clientWidth,o=this.imageContainer.clientHeight;this.translateX=this.translateX*i+s-e/2-i*(s-e/2),this.translateY=this.translateY*i+n-o/2-i*(n-o/2),this.scale=t}else{this.lastScale=this.scale,this.lastTranslateX=this.translateX,this.lastTranslateY=this.translateY,this.hasPreviousState=!0;const t=1,i=t/this.scale,e=this.imageContainer.clientWidth,o=this.imageContainer.clientHeight;this.translateX=this.translateX*i+s-e/2-i*(s-e/2),this.translateY=this.translateY*i+n-o/2-i*(n-o/2),this.scale=t}this.updateImageTransform(),this.updateZoomIndicator()})}bindTouchEvents(){this.addEvent(this.image,"touchstart",t=>{if(this.touchState.lastTouchTime=Date.now(),1===t.touches.length)this.touchState.isPinching||(this.touchState.isDragging=!0,this.touchState.startX=t.touches[0].clientX,this.touchState.startY=t.touches[0].clientY,this.touchState.startTranslateX=this.translateX,this.touchState.startTranslateY=this.translateY,this.image.classList.add("dragging"));else if(2===t.touches.length){this.touchState.isPinching=!0,this.touchState.isDragging=!1,this.image.classList.remove("dragging");const i=t.touches[0],s=t.touches[1];this.touchState.initialDistance=this.getDistance(i,s),this.touchState.initialScale=this.scale,this.touchState.initialTranslateX=this.translateX,this.touchState.initialTranslateY=this.translateY,this.touchState.centerX=(i.clientX+s.clientX)/2,this.touchState.centerY=(i.clientY+s.clientY)/2;const n=this.imageContainer.getBoundingClientRect(),e=this.touchState.centerX-n.left,o=this.touchState.centerY-n.top;this.calculateRelativeCenter(e,o),this.touchState.movementCount=0,this.touchState.scaleRatio=1}t.preventDefault()}),this.addEvent(this.image,"touchmove",t=>{if(!(Date.now()-this.touchState.lastTouchTime<16)){if(this.touchState.lastTouchTime=Date.now(),1===t.touches.length&&this.touchState.isDragging&&!this.touchState.isPinching){const i=t.touches[0].clientX-this.touchState.startX,s=t.touches[0].clientY-this.touchState.startY,n=this.rotatePoint(i,s,-this.rotation);if(this.touchState.movementCount++,(this.touchState.movementCount>this.touchState.stabilizationThreshold||Math.abs(i)>5||Math.abs(s)>5)&&(this.translateX=this.touchState.startTranslateX+n.x,this.translateY=this.touchState.startTranslateY+n.y,this.updateImageTransform(),this.options.drag)){const t=this.images[this.currentIndex];this.options.drag({image:t,index:this.currentIndex,translateX:this.translateX,translateY:this.translateY})}}else if(2===t.touches.length&&this.touchState.isPinching){const i=t.touches[0],s=t.touches[1],n=this.getDistance(i,s);this.touchState.scaleRatio=n/this.touchState.initialDistance;const e=this.touchState.initialScale*this.touchState.scaleRatio,o=this.options.minScale,h=this.options.maxScale,a=Math.max(o,Math.min(h,e));if(Math.abs(a-this.scale)>this.touchState.minScaleChange){const t=a/this.touchState.initialScale,i=this.imageContainer.getBoundingClientRect(),s=i.width,n=i.height;if(this.translateX=this.touchState.initialTranslateX*t+this.touchState.centerX-s/2-t*(this.touchState.centerX-s/2),this.translateY=this.touchState.initialTranslateY*t+this.touchState.centerY-n/2-t*(this.touchState.centerY-n/2),this.scale=a,this.updateImageTransform(),this.updateZoomIndicator(),this.options.onZoom){const t=this.images[this.currentIndex];this.options.onZoom({image:t,index:this.currentIndex,scale:this.scale,oldScale:this.touchState.initialScale})}}}t.preventDefault()}}),this.addEvent(this.image,"touchend",t=>{0===t.touches.length?(this.touchState.isDragging=!1,this.touchState.isPinching=!1,this.image.classList.remove("dragging")):1===t.touches.length&&this.touchState.isPinching&&(this.touchState.isPinching=!1,this.touchState.isDragging=!0,this.touchState.startX=t.touches[0].clientX,this.touchState.startY=t.touches[0].clientY,this.touchState.startTranslateX=this.translateX,this.touchState.startTranslateY=this.translateY,this.image.classList.add("dragging")),t.preventDefault()}),this.addEvent(this.image,"touchcancel",()=>{this.touchState.isDragging=!1,this.touchState.isPinching=!1,this.image.classList.remove("dragging")})}getDistance(t,i){const s=t.clientX-i.clientX,n=t.clientY-i.clientY;return Math.sqrt(s*s+n*n)}calculateRelativeCenter(t,i){if(!this.imageMetadata[this.currentIndex])return;const s=this.imageContainer.clientWidth,n=this.imageContainer.clientHeight/2,e=t-s/2-this.translateX,o=i-n-this.translateY;this.touchState.relativeCenterX=e/this.scale,this.touchState.relativeCenterY=o/this.scale}zoomAtPoint(t,i,s){const n=this.scale,e=this.options.maxScale,o=this.options.minScale,h=Math.max(o,Math.min(e,this.scale+t));if(h===this.scale)return;const a=h/n,r=this.imageContainer.clientWidth,l=this.imageContainer.clientHeight;if(this.translateX=this.translateX*a+i-r/2-a*(i-r/2),this.translateY=this.translateY*a+s-l/2-a*(s-l/2),this.scale=h,this.updateImageTransform(),this.updateZoomIndicator(),this.options.zoom){const t=this.images[this.currentIndex];this.options.zoom({image:t,index:this.currentIndex,scale:this.scale,oldScale:n})}}zoom(t){const i=this.imageContainer.clientWidth,s=this.imageContainer.clientHeight;this.zoomAtPoint(t,i/2,s/2)}rotate(t){const i=this.rotation,s=i+t;if(i===s)return;const n=this.imageContainer.getBoundingClientRect(),e=n.width/2,o=n.height/2,h=e+this.translateX,a=o+this.translateY;this.rotation=s,this.translateX=0,this.translateY=0;const r=this.imageMetadata[this.currentIndex];if(r){const t=this.calculateBoundingBox(r.width,r.height,s),i=t.width*this.scale,n=t.height*this.scale;this.translateX=(h-e)*(i/(i-this.translateX)),this.translateY=(a-o)*(n/(n-this.translateY))}if(this.updateImageTransform(),this.updateZoomIndicator(),this.options.onRotate){const t=this.images[this.currentIndex];this.options.onRotate({image:t,index:this.currentIndex,rotation:this.rotation,oldRotation:i})}}calculateBoundingBox(t,i,s){const n=s*Math.PI/180,e=Math.abs(Math.cos(n)),o=Math.abs(Math.sin(n));return{width:t*e+i*o,height:t*o+i*e}}reset(){this.rotation=0;const t=this.imageMetadata[this.currentIndex];t&&this.fitImageToScreen(t.width,t.height)}showOriginalSize(){this.rotation=0,this.scale=1,this.translateX=0,this.translateY=0,this.updateImageTransform(),this.updateZoomIndicator()}downloadImage(){const t=this.getImageUrl(this.currentIndex),i=this.imageMetadata[this.currentIndex];if(this.loadedImages.has(t)){const s=this.loadedImages.get(t);this.downloadFromImage(s,i)}}downloadFromImage(t,i){try{const s=document.createElement("canvas");s.width=t.naturalWidth||t.width,s.height=t.naturalHeight||t.height,s.getContext("2d").drawImage(t,0,0);const n=s.toDataURL("image/jpeg");this.downloadedImage(n,i)}catch(t){const s=this.getImageUrl(this.currentIndex);this.downloadedImage(s,i)}}downloadedImage(t,i){try{const s=document.createElement("a");s.href=t,s.download=i?i.name:"image.jpg",document.body.appendChild(s),s.click(),document.body.removeChild(s)}catch(t){}}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen&&(document.exitFullscreen(),this.isFullscreen=!1):(this.container.requestFullscreen().catch(t=>{}),this.isFullscreen=!0)}handleKeydown(t){if("block"===this.container.style.display){switch(t.key){case"Escape":this.close();break;case"ArrowLeft":this.prev();break;case"ArrowRight":this.next();break;case"ArrowUp":case"+":case"=":this.zoom(.1);break;case"ArrowDown":case"-":this.zoom(-.1);break;case"0":this.reset();break;case"f":case"F":this.toggleFullscreen();break;case"i":case"I":this.toggleImageInfo()}t.preventDefault()}}handleResize(){const t=this.imageMetadata[this.currentIndex];if(!t)return;const i=this.imageContainer.clientWidth,s=this.imageContainer.clientHeight,n=this.rotation%360;let e=t.width,o=t.height;90!==n&&270!==n||(e=t.height,o=t.width);const h=Math.min(i/e,s/o),a=e*this.scale,r=o*this.scale,l=a>i||r>s;let c=this.scale;c=l?Math.max(.1,h):h>=1?1:h,Math.abs(c-this.scale)>.01&&(this.scale=c,this.translateX=0,this.translateY=0,this.updateImageTransform(),this.updateZoomIndicator()),this.updateThumbnailsOnResize()}updateThumbnailsOnResize(){if(!this.thumbContainer)return;const t=parseInt(this.options.theme.thumbItemWidth),i=parseInt(this.options.theme.thumbGap),s=parseInt(this.options.theme.thumbPadding),n=this.options.preloadCount,e=window.innerWidth-2*s,o=Math.ceil(e/(t+i)),h=Math.min(o+n,this.images.length),a=this.thumbContainer.querySelectorAll(".images-viewer-thumbnail-item").length;if(a<h){const t=h-a;for(let i=a;i<a+t;i++)i<this.images.length&&this.createThumbnailItem(this.thumbContainer,i)}}onShow(){this.container.style.display="block",setTimeout(()=>{this.container.style.opacity="1",this.options.onShow&&this.options.onShow(this.container)},10)}close(){this.removeAllEvents(),this.cleanup(),this.container.style.opacity="0",setTimeout(()=>{this.container.style.display="none";const t=document.getElementById("images-viewer-styles");t&&t.remove(),this.container&&this.container.remove(),this.options.onClose&&this.options.onClose()},300)}cleanup(){for(const[t,i]of this.loadingImages.entries())i.onload=null,i.onerror=null,i.src="";this.loadedImages.clear(),this.loadingImages.clear(),this.imageMetadata=[],this.t&&(URL.revokeObjectURL(this.t),this.t=null)}}export{t as default};
|
|
1
|
+
class t{constructor(t){if(this.defaultOptions={initialIndex:0,closeOnMaskClick:!1,loop:!0,preloadCount:3,maxCacheSize:30,minScale:.1,maxScale:5,retryOnError:!1,props:{url:"url",title:"title",thumbnail:"thumbnail"},buttons:{zoomIn:!0,zoomOut:!0,rotateLeft:!0,rotateRight:!0,reset:!0,download:!0,fullscreen:!0,prev:!0,next:!0,close:!0,topClose:!0,thumbnails:!0,info:!0,originalSize:!0},imageInfo:{visible:!1,showName:!0,showDimensions:!0},i18n:{info:{name:"\u540d\u79f0:",dimensions:"\u5c3a\u5bf8:",shortcuts:"\u5feb\u6377\u952e",zoomIn:"\u653e\u5927:",zoomOut:"\u7f29\u5c0f:",prev:"\u4e0a\u4e00\u5f20:",next:"\u4e0b\u4e00\u5f20:",reset:"\u91cd\u7f6e:",fullscreen:"\u5168\u5c4f:",info:"\u4fe1\u606f:",close:"\u5173\u95ed:"},buttons:{prev:"\u4e0a\u4e00\u5f20 (\u2190)",next:"\u4e0b\u4e00\u5f20 (\u2192)",close:"\u5173\u95ed (Esc)",loading:"\u52a0\u8f7d\u4e2d..."}},theme:{viewerBgColor:"rgba(0, 0, 0, 0.4)",toolbarBgColor:"rgba(150, 150, 150, 0.7)",toolbarBorderRadius:"30px",toolbarPadding:"8px 12px",toolbarBottom:"20px",buttonBgColor:"rgba(150, 150, 150, 0.7)",buttonHoverBg:"rgba(200, 200, 200, 0.4)",buttonSize:"40px",buttonFontSize:"20px",buttonBorderRadius:"50%",navButtonBgColor:"rgba(150, 150, 150, 0.7)",navButtonHoverBg:"rgba(200, 200, 200, 0.4)",navButtonSize:"50px",navButtonFontSize:"20px",navButtonBorderRadius:"50%",topCloseBtnSize:"50px",topCloseBtnTop:"20px",topCloseBtnRight:"20px",topCloseBtnFontSize:"24px",topCloseBtnBgColor:"rgba(150, 150, 150, 0.7)",topCloseBtnHoverBg:"rgba(200, 200, 200, 0.4)",infoBgColor:"rgba(150, 150, 150, 0.7)",infoBorderRadius:"12px",infoPadding:"10px 15px",infoFontSize:"13px",infoTop:"70px",infoLeft:"20px",zoomIndicatorBg:"rgba(150, 150, 150, 0.7)",zoomIndicatorBorderRadius:"18px",zoomIndicatorPadding:"6px 12px",zoomIndicatorFontSize:"14px",zoomIndicatorTop:"20px",zoomIndicatorLeft:"20px",activeColor:"rgba(100, 150, 255, 0.8)",textColor:"rgba(255, 255, 255, 0.9)",shadowColor:"rgba(0, 0, 0, 0.2)",transitionSpeed:"0.3s",thumbItemWidth:"70px",thumbItemHeight:"45px",thumbGap:"10px",thumbPadding:"15px",thumbMaxWidth:"90%"}},this.options={...this.defaultOptions,...t,buttons:{...this.defaultOptions.buttons,...t?.buttons||{}},imageInfo:{...this.defaultOptions.imageInfo,...t?.imageInfo||{}},props:{...this.defaultOptions.props,...t?.props||{}},i18n:{...this.defaultOptions.i18n,...t?.i18n||{},info:{...this.defaultOptions.i18n.info,...t?.i18n?.info||{}},buttons:{...this.defaultOptions.i18n.buttons,...t?.i18n?.buttons||{}}},theme:{...this.defaultOptions.theme,...t?.theme||{}}},this.parseImageOptions(t),0===this.images.length)throw new Error("No images URL");const i=Math.max(0,Math.min(this.options.initialIndex||0,this.images.length-1));this.currentIndex=i,this.scale=1,this.rotation=0,this.translateX=0,this.translateY=0,this.isDragging=!1,this.startX=0,this.startY=0,this.startTranslateX=0,this.startTranslateY=0,this.isFullscreen=!1,this.imageInfoVisible=this.options.imageInfo.visible,this.imageMetadata=[],this.loadedImages=new Map,this.loadingImages=new Map,this.failedImages=new Set,this.loadedThumbnails=new Map,this.loadingThumbnails=new Map,this.failedThumbnails=new Set,this.lastTapTime=0,this.lastScale=1,this.lastTranslateX=0,this.lastTranslateY=0,this.hasPreviousState=!1,this.isToggledState=!1,this.touchState={isDragging:!1,isPinching:!1,initialDistance:null,initialScale:null,initialTranslateX:null,initialTranslateY:null,centerX:null,centerY:null,relativeCenterX:null,relativeCenterY:null,lastTouchTime:0,startX:0,startY:0,startTranslateX:0,startTranslateY:0,minScaleChange:.005,scaleRatio:1,stabilizationThreshold:3,movementCount:0},this.eventListeners=new Map,this.injectStyles(),this.preloadImages(),this.createOptimizedElements(),this.bindEvents(),this.onShow(),this.loadCurrentImage()}injectStyles(){const t=document.createElement("style");t.id="images-viewer-styles",t.textContent=`\n :root {\n /* \u80cc\u666f\u76f8\u5173\u53d8\u91cf */\n --viewer-bg-color: ${this.options.theme.viewerBgColor};\n \n /* \u5de5\u5177\u680f\u76f8\u5173\u53d8\u91cf */\n --toolbar-bg-color: ${this.options.theme.toolbarBgColor};\n --toolbar-border-radius: ${this.options.theme.toolbarBorderRadius};\n --toolbar-padding: ${this.options.theme.toolbarPadding};\n --toolbar-bottom: ${this.options.theme.toolbarBottom};\n \n /* \u6309\u94ae\u76f8\u5173\u53d8\u91cf */\n --button-bg-color: ${this.options.theme.buttonBgColor};\n --button-hover-bg: ${this.options.theme.buttonHoverBg};\n --button-size: ${this.options.theme.buttonSize};\n --button-font-size: ${this.options.theme.buttonFontSize};\n --button-border-radius: ${this.options.theme.buttonBorderRadius};\n\n /* \u5bfc\u822a\u6309\u94ae\u76f8\u5173\u53d8\u91cf */\n --nav-button-bg-color: ${this.options.theme.navButtonBgColor};\n --nav-button-hover-bg: ${this.options.theme.navButtonHoverBg};\n --nav-button-size: ${this.options.theme.navButtonSize};\n --nav-button-font-size: ${this.options.theme.navButtonFontSize};\n --nav-button-border-radius: ${this.options.theme.navButtonBorderRadius};\n \n /* \u53f3\u4e0a\u89d2\u5173\u95ed\u6309\u94ae\u53d8\u91cf */\n --top-close-btn-size: ${this.options.theme.topCloseBtnSize};\n --top-close-btn-top: ${this.options.theme.topCloseBtnTop};\n --top-close-btn-right: ${this.options.theme.topCloseBtnRight};\n --top-close-btn-font-size: ${this.options.theme.topCloseBtnFontSize};\n --top-close-btn-bg-color: ${this.options.theme.topCloseBtnBgColor};\n --top-close-btn-hover-bg: ${this.options.theme.topCloseBtnHoverBg};\n \n /* \u4fe1\u606f\u680f\u76f8\u5173\u53d8\u91cf */\n --info-bg-color: ${this.options.theme.infoBgColor};\n --info-border-radius: ${this.options.theme.infoBorderRadius};\n --info-padding: ${this.options.theme.infoPadding};\n --info-font-size: ${this.options.theme.infoFontSize};\n --info-top: ${this.options.theme.infoTop};\n --info-left: ${this.options.theme.infoLeft};\n \n /* \u7f29\u653e\u6307\u793a\u5668\u53d8\u91cf */\n --zoom-indicator-bg: ${this.options.theme.zoomIndicatorBg};\n --zoom-indicator-border-radius: ${this.options.theme.zoomIndicatorBorderRadius};\n --zoom-indicator-padding: ${this.options.theme.zoomIndicatorPadding};\n --zoom-indicator-font-size: ${this.options.theme.zoomIndicatorFontSize};\n --zoom-indicator-top: ${this.options.theme.zoomIndicatorTop};\n --zoom-indicator-left: ${this.options.theme.zoomIndicatorLeft};\n \n /* \u901a\u7528\u53d8\u91cf */\n --active-color: ${this.options.theme.activeColor};\n --text-color: ${this.options.theme.textColor};\n --shadow-color: ${this.options.theme.shadowColor};\n --transition-speed: ${this.options.theme.transitionSpeed};\n \n /* \u7f29\u7565\u56fe\u76f8\u5173\u53d8\u91cf */\n --thumb-max-width: ${this.options.theme.thumbMaxWidth};\n --thumb-gap: ${this.options.theme.thumbGap};\n --thumb-padding: ${this.options.theme.thumbPadding};\n --thumb-item-width: ${this.options.theme.thumbItemWidth};\n --thumb-item-height: ${this.options.theme.thumbItemHeight};\n }\n\n .images-viewer-container {\n position: fixed;\n left: 0;\n top: 0;\n inset: 0;\n width: 100%;\n height: 100%;\n z-index: 9999;\n opacity: 0;\n transition: opacity var(--transition-speed) ease;\n touch-action: none;\n -webkit-user-select: none;\n user-select: none;\n display: none;\n background-color: var(--viewer-bg-color);\n overflow: hidden;\n }\n\n .images-viewer-container::backdrop,\n .images-viewer-container:fullscreen {\n background-color: var(--viewer-bg-color);\n }\n\n /* \u56fe\u7247\u5bb9\u5668\u6837\u5f0f */\n .images-viewer-image-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n }\n\n /* \u56fe\u7247\u6837\u5f0f */\n .images-viewer-image {\n position: relative;\n object-fit: contain;\n cursor: grab;\n opacity: 0;\n box-shadow: 0 8px 25px var(--shadow-color);\n border-radius: 4px;\n user-select: none;\n touch-action: none;\n }\n\n .images-viewer-image.loaded {\n opacity: 1;\n transition: transform 0.1s ease-out, opacity var(--transition-speed) ease;\n transform-origin: center center;\n }\n\n .images-viewer-image.dragging {\n cursor: grabbing;\n transition: none;\n }\n\n /* \u52a0\u8f7d\u6307\u793a\u5668 */\n .images-viewer-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background-color: rgba(127, 127, 127, 0.7);\n padding: 20px 30px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n gap: 15px;\n color: var(--text-color);\n font-size: 18px;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--transition-speed) ease;\n z-index: 3;\n }\n\n .images-viewer-loading.active {\n opacity: 1;\n }\n\n .images-viewer-loading-spinner {\n width: 40px;\n height: 40px;\n border: 4px solid rgba(255, 255, 255, 0.2);\n border-top-color: var(--active-color);\n border-radius: 50%;\n animation: imageViewerSpin 1s linear infinite;\n }\n\n @keyframes imageViewerSpin {\n to {\n transform: rotate(360deg);\n }\n }\n\n /* \u53f3\u4e0a\u89d2\u5173\u95ed\u6309\u94ae\u6837\u5f0f */\n .images-viewer-top-close-btn {\n position: absolute;\n top: var(--top-close-btn-top);\n right: var(--top-close-btn-right);\n width: var(--top-close-btn-size);\n height: var(--top-close-btn-size);\n border-radius: 50%;\n background-color: var(--button-bg-color);\n color: var(--text-color);\n border: none;\n font-size: var(--top-close-btn-font-size);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all var(--transition-speed);\n z-index: 10;\n box-shadow: 0 2px 8px var(--shadow-color);\n }\n\n .images-viewer-top-close-btn:hover {\n background-color: var(--top-close-btn-hover-bg);\n transform: scale(1.1);\n }\n\n /* \u7f29\u653e\u6307\u793a\u5668\u6837\u5f0f */\n .images-viewer-zoom-indicator {\n position: absolute;\n top: var(--zoom-indicator-top);\n left: var(--zoom-indicator-left);\n color: var(--text-color);\n background-color: var(--zoom-indicator-bg);\n padding: var(--zoom-indicator-padding);\n border-radius: var(--zoom-indicator-border-radius);\n font-size: var(--zoom-indicator-font-size);\n z-index: 10;\n min-width: 60px;\n text-align: center;\n box-shadow: 0 2px 8px var(--shadow-color);\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n /* \u4fe1\u606f\u680f\u6837\u5f0f */\n .images-viewer-image-info {\n position: absolute;\n top: var(--info-top);\n left: var(--info-left);\n color: var(--text-color);\n background-color: var(--info-bg-color);\n padding: var(--info-padding);\n border-radius: var(--info-border-radius);\n font-size: var(--info-font-size);\n z-index: 10;\n max-width: calc(100% - 40px);\n box-shadow: 0 4px 12px var(--shadow-color);\n display: none;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .images-viewer-image-info.visible {\n display: block;\n animation: imageViewerFadeIn 0.3s ease;\n }\n\n .images-viewer-image-info p {\n margin: 4px 0;\n line-height: 1.4;\n }\n\n .images-viewer-image-info .info-label {\n color: #ddd;\n margin-right: 5px;\n }\n\n .images-viewer-shortcuts-title {\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n font-weight: bold;\n margin-bottom: 5px;\n }\n\n @keyframes imageViewerFadeIn {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* \u56fe\u7247\u8ba1\u6570\u5668 */\n .images-viewer-image-counter {\n position: absolute;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n color: var(--text-color);\n background-color: var(--info-bg-color);\n padding: 6px 12px;\n border-radius: 18px;\n font-size: 14px;\n z-index: 10;\n box-shadow: 0 2px 8px var(--shadow-color);\n }\n\n /* \u56fe\u7247\u6807\u9898\u663e\u793a */\n .images-viewer-image-title {\n position: absolute;\n top: 60px;\n left: 50%;\n transform: translateX(-50%);\n color: var(--text-color);\n padding: 4px 12px;\n border-radius: 18px;\n font-size: 12px;\n z-index: 10;\n max-width: 80%;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* \u5bfc\u822a\u6309\u94ae */\n .images-viewer-nav-buttons {\n position: absolute;\n top: 50%;\n left: 0;\n right: 0;\n transform: translateY(-50%);\n display: flex;\n justify-content: space-between;\n pointer-events: none;\n z-index: 5;\n padding: 0 10px;\n }\n\n .images-viewer-nav-btn {\n width: var(--nav-button-size);\n height: var(--nav-button-size);\n border-radius: var(--nav-button-border-radius);\n background-color: var(--nav-button-bg-color);\n color: var(--text-color);\n border: none;\n font-size: var(--nav-button-font-size);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n pointer-events: auto;\n opacity: 0.9;\n box-shadow: 0 2px 8px var(--shadow-color);\n z-index: 6;\n }\n\n .images-viewer-nav-btn:hover {\n background-color: var(--nav-button-hover-bg);\n opacity: 1;\n transform: scale(1.1);\n }\n\n .images-viewer-nav-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n transform: none;\n }\n\n /* \u5de5\u5177\u680f\u6837\u5f0f */\n .images-viewer-toolbar {\n position: absolute;\n bottom: var(--toolbar-bottom);\n left: 50%;\n transform: translateX(-50%);\n background-color: var(--toolbar-bg-color);\n padding: var(--toolbar-padding);\n border-radius: var(--toolbar-border-radius);\n display: flex;\n gap: 2px;\n z-index: 10;\n box-shadow: 0 6px 25px var(--shadow-color);\n max-width: calc(100% - 40px);\n overflow-x: auto;\n overflow-y: hidden;\n border: 1px solid rgba(255, 255, 255, 0.1);\n scrollbar-width: none;\n -ms-overflow-style: none;\n -webkit-overflow-scrolling: touch;\n }\n\n .images-viewer-toolbar::-webkit-scrollbar {\n display: none;\n }\n\n .images-viewer-tool-btn {\n width: var(--button-size);\n height: var(--button-size);\n background-color: transparent;\n border: none;\n color: var(--text-color);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--button-font-size);\n transition: all 0.2s;\n flex-shrink: 0;\n position: relative;\n border-radius: var(--button-border-radius);\n margin: 0 2px;\n z-index: 11;\n line-height: 1;\n }\n\n .images-viewer-tool-btn:hover {\n background-color: var(--button-hover-bg);\n transform: translateY(-2px);\n box-shadow: 0 4px 10px var(--shadow-color);\n }\n\n .images-viewer-tool-btn:active {\n background-color: rgba(255, 255, 255, 0.3);\n transform: translateY(0);\n }\n\n .images-viewer-tool-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* \u7f29\u7565\u56fe\u5bb9\u5668 */\n .images-viewer-thumbnails-container {\n position: absolute;\n bottom: 90px;\n left: 50%;\n max-width: var(--thumb-max-width);\n transform: translateX(-50%);\n padding: 10px var(--thumb-padding);\n background-color: var(--toolbar-bg-color);\n border-radius: 12px;\n display: flex;\n gap: var(--thumb-gap);\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n z-index: 10;\n box-shadow: 0 3px 15px var(--shadow-color);\n -webkit-overflow-scrolling: touch;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .images-viewer-thumbnails-container::-webkit-scrollbar {\n display: none;\n }\n\n .images-viewer-thumbnail-item {\n width: var(--thumb-item-width);\n height: var(--thumb-item-height);\n border: 2px solid transparent;\n border-radius: 6px;\n overflow: hidden;\n cursor: pointer;\n flex-shrink: 0;\n transition: all 0.2s;\n z-index: 11;\n position: relative;\n opacity: 0.6;\n }\n\n .images-viewer-thumbnail-item img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .images-viewer-thumbnail-item.active {\n border-color: var(--active-color);\n transform: scale(1.2);\n opacity: 1;\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n }\n\n /* \u7f29\u7565\u56fe\u52a0\u8f7d\u72b6\u6001 */\n .images-viewer-thumbnail-loading {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.3);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1;\n }\n\n .loading-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top: 2px solid var(--active-color);\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .loading-error {\n color: #ff6b6b;\n font-size: 10px;\n text-align: center;\n padding: 2px;\n }\n\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .images-viewer-thumbnail-item:hover {\n transform: scale(1.2);\n opacity: 0.9;\n }\n `,document.head.appendChild(t)}createOptimizedElements(){this.container=document.createElement("div"),this.container.className="images-viewer-container",document.body.appendChild(this.container),this.imageContainer=document.createElement("div"),this.imageContainer.className="images-viewer-image-container",this.container.appendChild(this.imageContainer),this.image=document.createElement("img"),this.image.className="images-viewer-image",this.image.crossOrigin="anonymous",this.image.alt=" ",this.imageContainer.appendChild(this.image),this.loading=document.createElement("div"),this.loading.className="images-viewer-loading",this.loading.innerHTML=`\n <div class="images-viewer-loading-spinner"></div>\n <div>${this.options.i18n.buttons.loading}</div>\n `,this.imageContainer.appendChild(this.loading),this.options.buttons.topClose&&(this.topCloseBtn=document.createElement("button"),this.topCloseBtn.className="images-viewer-top-close-btn",this.topCloseBtn.textContent="\xd7",this.topCloseBtn.title=this.options.i18n.buttons.close,this.container.appendChild(this.topCloseBtn)),this.zoomIndicator=document.createElement("div"),this.zoomIndicator.className="images-viewer-zoom-indicator",this.zoomIndicator.textContent="100%",this.container.appendChild(this.zoomIndicator),this.options.buttons.info&&(this.imageInfoPanel=document.createElement("div"),this.imageInfoPanel.className="images-viewer-image-info "+(this.imageInfoVisible?"visible":""),this.container.appendChild(this.imageInfoPanel)),this.images.length>1&&(this.counter=document.createElement("div"),this.counter.className="images-viewer-image-counter",this.container.appendChild(this.counter)),this.imageTitle=document.createElement("div"),this.imageTitle.className="images-viewer-image-title",this.container.appendChild(this.imageTitle),this.images.length>1&&(this.options.buttons.prev||this.options.buttons.next)&&this.createNavButtons(),this.images.length>1&&this.options.buttons.thumbnails&&this.createThumbnails(),this.createToolbar()}createNavButtons(){const t=document.createElement("div");t.className="images-viewer-nav-buttons",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.options.buttons.prev&&(this.prevBtn=document.createElement("button"),this.prevBtn.className="images-viewer-nav-btn images-viewer-prev-btn",this.prevBtn.textContent="\u2190",this.prevBtn.title=this.options.i18n.buttons.prev,this.addEvent(this.prevBtn,"click",t=>{t.stopPropagation(),this.prev()}),t.appendChild(this.prevBtn)),this.options.buttons.next&&(this.nextBtn=document.createElement("button"),this.nextBtn.className="images-viewer-nav-btn images-viewer-next-btn",this.nextBtn.textContent="\u2192",this.nextBtn.title=this.options.i18n.buttons.next,this.addEvent(this.nextBtn,"click",t=>{t.stopPropagation(),this.next()}),t.appendChild(this.nextBtn)),this.container.appendChild(t)}createToolbar(){const t=document.createElement("div");t.className="images-viewer-toolbar",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.addEvent(t,"wheel",i=>{i.preventDefault(),i.stopPropagation(),t.scrollLeft+=i.deltaY}),this.images.length>1&&(this.options.buttons.prev&&(this.toolbarPrevBtn=this.createToolButton("\u2190",()=>this.prev()),t.appendChild(this.toolbarPrevBtn)),this.options.buttons.next&&(this.toolbarNextBtn=this.createToolButton("\u2192",()=>this.next()),t.appendChild(this.toolbarNextBtn))),this.options.buttons.zoomOut&&(this.zoomOutBtn=this.createToolButton("\u2212",()=>this.zoom(-.1)),t.appendChild(this.zoomOutBtn)),this.options.buttons.zoomIn&&(this.zoomInBtn=this.createToolButton("+",()=>this.zoom(.1)),t.appendChild(this.zoomInBtn)),this.options.buttons.rotateLeft&&(this.rotateLeftBtn=this.createToolButton("\u21ba",()=>this.rotate(-90)),t.appendChild(this.rotateLeftBtn)),this.options.buttons.rotateRight&&(this.rotateRightBtn=this.createToolButton("\u21bb",()=>this.rotate(90)),t.appendChild(this.rotateRightBtn)),this.options.buttons.reset&&(this.resetBtn=this.createToolButton("\u27f3",()=>this.reset()),t.appendChild(this.resetBtn)),this.options.buttons.originalSize&&(this.originalSizeBtn=this.createToolButton("1:1",()=>this.showOriginalSize()),t.appendChild(this.originalSizeBtn)),this.options.buttons.info&&(this.infoBtn=this.createToolButton("\u24d8",()=>this.toggleImageInfo()),t.appendChild(this.infoBtn)),this.options.buttons.download&&(this.downloadBtn=this.createToolButton("\u21a1",()=>this.downloadImage()),t.appendChild(this.downloadBtn)),this.options.buttons.fullscreen&&(this.fullscreenBtn=this.createToolButton("\u26f6",()=>this.toggleFullscreen()),t.appendChild(this.fullscreenBtn)),this.options.buttons.close&&(this.closeBtn=this.createToolButton("\xd7",()=>this.close()),t.appendChild(this.closeBtn)),this.options.customButtons&&this.options.customButtons.forEach(i=>{t.appendChild(this.createToolButton(i[0],i[1]))}),this.container.appendChild(t)}createToolButton(t,i){const s=document.createElement("button");s.className="images-viewer-tool-btn";const n=document.createElement("span");return n.textContent=t,s.appendChild(n),this.addEvent(s,"click",t=>{t.stopPropagation(),i()}),s}createThumbnails(){const t=document.createElement("div");t.className="images-viewer-thumbnails-container",this.addEvent(t,"click",t=>{t.stopPropagation();const i=t.target.closest(".images-viewer-thumbnail-item");if(i){const t=parseInt(i.dataset.index);if(!isNaN(t)&&t!==this.currentIndex){const i=this.currentIndex;this.currentIndex=t,this.loadCurrentImage(),this.updateThumbnails(),this.options.onChange&&this.options.onChange({index:this.currentIndex,oldIndex:i,direction:this.currentIndex>i?"next":"prev",data:this.images[this.currentIndex],img:this.image,dom:this.container})}}}),this.addEvent(t,"wheel",i=>{i.preventDefault(),i.stopPropagation(),t.scrollLeft+=i.deltaY});const i=parseInt(this.options.theme.thumbItemWidth),s=parseInt(this.options.theme.thumbGap),n=parseInt(this.options.theme.thumbPadding),e=this.options.preloadCount,h=window.innerWidth-2*n,o=Math.ceil(h/(i+s)),a=Math.min(o+e,this.images.length);for(let i=0;i<a;i++)this.createThumbnailItem(t,i);let r=a;this.addEvent(t,"scroll",()=>{const i=t.scrollLeft,s=t.clientWidth;if(i>t.scrollWidth-s-s&&r<this.images.length){const i=Math.min(o,this.images.length-r);for(let s=r;s<r+i;s++)this.createThumbnailItem(t,s);r+=i}}),this.container.appendChild(t),this.thumbContainer=t}createThumbnailItem(t,i){const s=this.getThumbnailUrl(i),n=this.getImageUrl(i),e=document.createElement("div");e.className="images-viewer-thumbnail-item "+(i===this.currentIndex?"active":""),e.dataset.index=i;const h=new Image;h.crossOrigin="anonymous",h.src="";const o=document.createElement("div");o.className="images-viewer-thumbnail-loading",o.innerHTML='<div class="loading-spinner"></div>',e.appendChild(o),this.loadedThumbnails.has(s)?(h.src=this.loadedThumbnails.get(s).src,o.remove()):this.loadingThumbnails.has(s)?(o.style.display="flex",h.onload=()=>{o.remove(),h.onload=null,h.onerror=null},h.onerror=()=>{o.remove(),this.failedThumbnails.add(s),h.onload=null,h.onerror=null,this.options.onThumbnailError&&this.options.onThumbnailError({data:this.images[i],index:i,url:s,img:h})},h.src=s):this.failedThumbnails.has(s)&&!this.options.retryOnError?o.remove():this.loadedImages.has(n)?(h.src=this.loadedImages.get(n).src,o.remove()):(this.loadingThumbnails.set(s,h),o.style.display="flex",h.onload=()=>{this.loadedThumbnails.set(s,h),this.loadingThumbnails.delete(s),this.failedThumbnails.delete(s),o.remove(),h.onload=null,h.onerror=null},h.onerror=()=>{this.loadingThumbnails.delete(s),this.failedThumbnails.add(s),o.remove(),h.onload=null,h.onerror=null,this.options.onThumbnailError&&this.options.onThumbnailError({data:this.images[i],index:i,url:s,img:h})},h.src=s),e.appendChild(h),t.appendChild(e)}updateThumbnailDisplay(t){if(!this.thumbContainer)return;const i=this.thumbContainer.querySelector(`[data-index="${t}"]`);if(i){const s=this.getThumbnailUrl(t),n=i.querySelector("img");if(n&&this.loadedThumbnails.has(s)){const t=this.loadedThumbnails.get(s);n.src=t.src,n.style.display="block";const e=i.querySelector(".images-viewer-thumbnail-loading");e&&e.remove()}}}updateImageTransform(){const t=`\n translate(${this.translateX}px, ${this.translateY}px)\n scale(${this.scale})\n rotate(${this.rotation}deg)\n `;this.image.style.transform=t}fitImageToScreen(t,i){this.scale=1,this.translateX=0,this.translateY=0;const s=this.imageContainer.clientWidth,n=this.imageContainer.clientHeight,e=this.rotation%360;let h=t,o=i;if(90!==e&&270!==e||(h=i,o=t),h>s||o>n){const t=s/h,i=n/o;this.scale=Math.min(t,i)}this.scale=Math.max(.1,this.scale),this.updateImageTransform(),this.updateZoomIndicator()}parseImageOptions(t){this.images=[],"string"==typeof t?this.images=[t]:Array.isArray(t)?this.images=t.filter(t=>""!==t):"object"==typeof t&&t.images&&Array.isArray(t.images)&&(this.images=t.images.filter(t=>""!==t))}getImageUrl(t){const i=this.images[t];if("string"==typeof i)return i;if("object"==typeof i){const s=this.options.props.url;if("function"==typeof s)return s(i,t);if("string"==typeof s&&i[s])return i[s]}return""}getThumbnailUrl(t){const i=this.images[t];if("object"==typeof i){const s=this.options.props.thumbnail;if("function"==typeof s)return s(i,t);if("string"==typeof s&&i[s])return i[s]}return this.getImageUrl(t)}getImageTitle(t){const i=this.images[t];if("object"==typeof i){const s=this.options.props.title;if("function"==typeof s)return s(i,t);if("string"==typeof s&&i[s])return i[s]}return""}preloadImages(){const t=this.options.preloadCount;if(t<=0)return;const i=Math.max(0,this.currentIndex-t),s=Math.min(this.images.length-1,this.currentIndex+t);for(let t=i;t<=s;t++)this.loadImageAtIndex(t)}addToCache(t,i){this.loadedImages.set(t,i);const s=this.options.maxCacheSize;if(s>0&&this.loadedImages.size>s){const t=this.loadedImages.keys().next().value;this.loadedImages.delete(t)}}loadImageAtIndex(t){const i=this.getImageUrl(t);if(!i||this.loadedImages.has(i)||this.loadingImages.has(i)||!this.options.retryOnError&&this.failedImages.has(i))return;const s=new Image;s.onload=()=>{this.addToCache(i,s),this.loadingImages.delete(i),this.failedImages.delete(i),this.imageMetadata[t]={name:this.extractFileName(i),width:s.naturalWidth||s.width,height:s.naturalHeight||s.height},s.onload=null,s.onerror=null},s.onerror=()=>{this.loadingImages.delete(i),this.failedImages.add(i),s.onload=null,s.onerror=null;const n=this.getImageUrl(this.currentIndex);i===n&&(this.hideLoading(),this.image.classList.add("loaded"),this.options.onImageError&&this.options.onImageError({data:this.images[t],index:t,url:i,img:this.image}))},s.crossOrigin="anonymous",s.src=i,this.loadingImages.set(i,s)}extractFileName(t){try{const i=new URL(t).pathname.split("/");let s=i[i.length-1];const n=s.indexOf("?");return n>-1&&(s=s.substring(0,n)),s||"unknown-image"}catch(t){return"unknown-image"}}loadCurrentImage(t){if(void 0!==t&&(this.currentIndex=t),this.hasPreviousState=!1,this.isToggledState=!1,this.images.length>1&&this.counter){let t=null;this.options.onCounter&&(t=this.options.onCounter({data:this.images[this.currentIndex],index:this.currentIndex,currentPage:this.currentIndex+1,totalPages:this.images.length,scale:this.scale,rotation:this.rotation})),t?this.counter.innerHTML=t:this.counter.textContent=`${this.currentIndex+1} / ${this.images.length}`}const i=this.getImageTitle(this.currentIndex);this.imageTitle&&i?(this.imageTitle.innerHTML=i,this.imageTitle.style.display="block"):this.imageTitle.style.display="none",this.updateNavButtons(),this.updateThumbnails(),this.scale=1,this.rotation=0,this.translateX=0,this.translateY=0;const s=this.getImageUrl(this.currentIndex);this.loadedImages.has(s)||this.loadingImages.has(s)||this.loadImageAtIndex(this.currentIndex),this.preloadAdjacentImages(),this.isImageLoaded()}preloadAdjacentImages(){const t=Math.max(0,this.currentIndex-2),i=Math.min(this.images.length-1,this.currentIndex+2);for(let s=t;s<=i;s++)this.loadThumbnailAtIndex(s);for(let s=t;s<=i;s++)this.loadImageAtIndex(s)}loadThumbnailAtIndex(t){const i=this.getThumbnailUrl(t);if(!i||this.loadedThumbnails.has(i)||this.loadingThumbnails.has(i)||!this.options.retryOnError&&this.failedThumbnails.has(i))return;const s=new Image;s.onload=()=>{this.loadedThumbnails.set(i,s),this.loadingThumbnails.delete(i),this.failedThumbnails.delete(i),this.updateThumbnailDisplay(t),s.onload=null,s.onerror=null},s.onerror=()=>{this.loadingThumbnails.delete(i),this.failedThumbnails.add(i),s.onload=null,s.onerror=null;let n=null;if(this.thumbContainer){const i=this.thumbContainer.querySelector(`[data-index="${t}"]`);if(i){n=i.querySelector("img");const t=i.querySelector(".images-viewer-thumbnail-loading");t&&t.remove()}}this.options.onThumbnailError&&this.options.onThumbnailError({data:this.images[t],index:t,url:i,img:n})},s.crossOrigin="anonymous",s.src=i,this.loadingThumbnails.set(i,s)}isImageLoaded(){const t=this.getImageUrl(this.currentIndex),i=this.loadedImages.has(t),s=this.loadingImages.has(t),n=this.failedImages.has(t);if(this.showLoading(),this.image.classList.remove("loaded"),this.image.src="",i){const i=this.loadedImages.get(t),s=document.createElement("canvas"),n=s.getContext("2d");s.width=i.naturalWidth||i.width,s.height=i.naturalHeight||i.height,n.drawImage(i,0,0),s.toBlob(t=>{if(t){const i=URL.createObjectURL(t);this.image.onload=()=>{this.image.onload=null},this.image.src=i,this.t&&(URL.revokeObjectURL(this.t),this.t=null),this.t=i}});const e=this.imageMetadata[this.currentIndex];return e&&(this.fitImageToScreen(e.width,e.height),this.updateImageInfo()),this.image.classList.add("loaded"),void this.hideLoading()}if(n&&!this.options.retryOnError)return this.hideLoading(),this.image.classList.add("loaded"),void(this.options.onImageError&&this.options.onImageError({data:this.images[this.currentIndex],index:this.currentIndex,url:t,img:this.image}));if(s){const i=()=>{const s=this.getImageUrl(this.currentIndex);this.loadedImages.has(t)?t===s&&this.isImageLoaded():this.loadingImages.has(t)?setTimeout(i,100):(this.hideLoading(),t===s&&(this.image.classList.add("loaded"),this.options.onImageError&&this.options.onImageError({data:this.images[this.currentIndex],index:this.currentIndex,url:t,img:this.image})))};return void setTimeout(i,100)}const e=new Image;e.onload=()=>{this.addToCache(t,e),this.loadingImages.delete(t),this.imageMetadata[this.currentIndex]={name:this.extractFileName(t),width:e.naturalWidth||e.width,height:e.naturalHeight||e.height},this.isImageLoaded()},e.onerror=()=>{this.loadingImages.delete(t),this.hideLoading();const i=this.getImageUrl(this.currentIndex);t===i&&(this.image.classList.add("loaded"),this.options.onImageError&&this.options.onImageError({data:this.images[this.currentIndex],index:this.currentIndex,url:t,img:this.image}))},e.crossOrigin="anonymous",e.src=t,this.loadingImages.set(t,e)}updateZoomIndicator(){const t=Math.round(100*this.scale);if(this.options.onZoomIndicator){const i=this.options.onZoomIndicator({data:this.images[this.currentIndex],index:this.currentIndex,scale:this.scale,percentage:t,rotation:this.rotation});if(i)return void(this.zoomIndicator.innerHTML=i)}this.zoomIndicator.textContent=`${t}%`}updateImageInfo(){if(!this.options.buttons.info||!this.imageInfoPanel)return;const t=this.imageMetadata[this.currentIndex];if(!t)return;const i=this.options.i18n;let s="";this.options.onInfo&&(s=this.options.onInfo({data:this.images[this.currentIndex],index:this.currentIndex,metadata:t,scale:this.scale,rotation:this.rotation}),s)||(this.options.imageInfo.showName&&(s+=`<p><span class="info-label">${i.info.name}</span> ${t.name}</p>`),this.options.imageInfo.showDimensions&&(s+=`<p><span class="info-label">${i.info.dimensions}</span> ${t.width} \xd7 ${t.height}</p>`),s+=`\n <div class="images-viewer-shortcuts-title">${i.info.shortcuts}</div>\n <p><span class="info-label">${i.info.zoomIn}</span> \u2191 +</p>\n <p><span class="info-label">${i.info.zoomOut}</span> \u2193 -</p>\n <p><span class="info-label">${i.info.prev}</span> \u2190</p>\n <p><span class="info-label">${i.info.next}</span> \u2192</p>\n <p><span class="info-label">${i.info.reset}</span> 0</p>\n <p><span class="info-label">${i.info.fullscreen}</span> F</p>\n <p><span class="info-label">${i.info.info}</span> I</p>\n <p><span class="info-label">${i.info.close}</span> ESC</p>\n `),this.imageInfoPanel.innerHTML=s}toggleImageInfo(){this.options.buttons.info&&this.imageInfoPanel&&(this.imageInfoVisible=!this.imageInfoVisible,this.imageInfoVisible?this.imageInfoPanel.classList.add("visible"):this.imageInfoPanel.classList.remove("visible"))}updateNavButtons(){if(this.images.length<=1)return;const t=!!this.options.loop||this.currentIndex>0,i=!!this.options.loop||this.currentIndex<this.images.length-1;this.prevBtn&&(this.prevBtn.disabled=!t),this.nextBtn&&(this.nextBtn.disabled=!i),this.toolbarPrevBtn&&(this.toolbarPrevBtn.disabled=!t),this.toolbarNextBtn&&(this.toolbarNextBtn.disabled=!i)}updateThumbnails(){if(this.images.length<=1)return;document.querySelectorAll(".images-viewer-thumbnail-item").forEach(t=>{t.classList.remove("active")});const t=document.querySelector(`.images-viewer-thumbnail-item[data-index="${this.currentIndex}"]`);t&&(t.classList.add("active"),t.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center"}))}showLoading(){this.loading&&this.loading.classList.add("active")}hideLoading(){this.loading&&this.loading.classList.remove("active")}prev(){if(this.images.length<=1)return;let t=this.currentIndex-1;t<0&&(t=this.options.loop?this.images.length-1:0);const i=this.currentIndex;t!==this.currentIndex&&(this.currentIndex=t,this.loadCurrentImage()),this.options.onChange&&this.options.onChange({oldIndex:i,index:this.currentIndex,data:this.images[this.currentIndex],direction:"prev",img:this.image,dom:this.container})}next(){if(this.images.length<=1)return;let t=this.currentIndex+1;t>=this.images.length&&(t=this.options.loop?0:this.images.length-1),t!==this.currentIndex&&(this.currentIndex=t,this.loadCurrentImage());const i=this.currentIndex;this.options.onChange&&this.options.onChange({oldIndex:i,index:this.currentIndex,data:this.images[this.currentIndex],index:this.currentIndex,direction:"next",img:this.image,dom:this.container})}bindEvents(){this.topCloseBtn&&this.addEvent(this.topCloseBtn,"click",()=>this.close()),this.options.closeOnMaskClick&&this.addEvent(this.imageContainer,"click",t=>{t.target==this.imageContainer&&this.close()}),this.addEvent(document,"keydown",t=>this.handleKeydown(t));const t=function(t){let i,s=0;return function(...n){const e=Date.now(),h=e-s;h>300?(t.apply(this,n),s=e):(clearTimeout(i),i=setTimeout(()=>{t.apply(this,n),s=e},300-h))}}(()=>{this.handleResize()});this.addEvent(window,"resize",t),this.bindDragEvents(),this.bindTouchEvents()}addEvent(t,i,s,n){t.addEventListener(i,s,n);const e=`${i}-${Date.now()}-${Math.random()}`;this.eventListeners.set(e,{element:t,event:i,handler:s})}removeAllEvents(){this.eventListeners.forEach(({element:t,event:i,handler:s})=>{t.removeEventListener(i,s)}),this.eventListeners.clear()}rotatePoint(t,i,s){const n=s*Math.PI,e=Math.cos(n),h=Math.sin(n);return{x:t*e-i*h,y:t*h+i*e}}bindDragEvents(){this.addEvent(this.image,"mousedown",t=>{0===t.button&&(this.isDragging=!0,this.image.classList.add("dragging"),this.startX=t.clientX,this.startY=t.clientY,this.startTranslateX=this.translateX,this.startTranslateY=this.translateY,t.preventDefault())}),this.addEvent(document,"mousemove",t=>{if(!this.isDragging)return;const i=t.clientX-this.startX,s=t.clientY-this.startY,n=this.rotatePoint(i,s,-this.rotation);this.translateX=this.startTranslateX+n.x,this.translateY=this.startTranslateY+n.y,this.updateImageTransform(),this.options.onDrag&&this.options.onDrag({data:his.images[this.currentIndex],index:this.currentIndex,translateX:this.translateX,translateY:this.translateY}),t.preventDefault()}),this.addEvent(document,"mouseup",()=>{this.isDragging&&(this.isDragging=!1,this.image.classList.remove("dragging"))}),this.addEvent(document,"mouseleave",()=>{this.isDragging&&(this.isDragging=!1,this.image.classList.remove("dragging"))}),this.addEvent(this.imageContainer,"wheel",t=>{t.preventDefault();const i=this.imageContainer.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top,e=t.deltaY>0?-.05:.05;this.zoomAtPoint(e,s,n)}),this.addEvent(this.image,"dblclick",t=>{t.preventDefault();const i=this.imageContainer.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top;if(Math.abs(this.scale-1)<.01)if(this.hasPreviousState)this.scale=this.lastScale,this.translateX=this.lastTranslateX,this.translateY=this.lastTranslateY,this.hasPreviousState=!1;else{this.lastScale=this.scale,this.lastTranslateX=this.translateX,this.lastTranslateY=this.translateY,this.hasPreviousState=!0;const t=1.5,i=t/this.scale,e=this.imageContainer.clientWidth,h=this.imageContainer.clientHeight;this.translateX=this.translateX*i+s-e/2-i*(s-e/2),this.translateY=this.translateY*i+n-h/2-i*(n-h/2),this.scale=t}else{this.lastScale=this.scale,this.lastTranslateX=this.translateX,this.lastTranslateY=this.translateY,this.hasPreviousState=!0;const t=1,i=t/this.scale,e=this.imageContainer.clientWidth,h=this.imageContainer.clientHeight;this.translateX=this.translateX*i+s-e/2-i*(s-e/2),this.translateY=this.translateY*i+n-h/2-i*(n-h/2),this.scale=t}this.updateImageTransform(),this.updateZoomIndicator()})}bindTouchEvents(){this.addEvent(this.image,"touchstart",t=>{if(this.touchState.lastTouchTime=Date.now(),1===t.touches.length)this.touchState.isPinching||(this.touchState.isDragging=!0,this.touchState.startX=t.touches[0].clientX,this.touchState.startY=t.touches[0].clientY,this.touchState.startTranslateX=this.translateX,this.touchState.startTranslateY=this.translateY,this.image.classList.add("dragging"));else if(2===t.touches.length){this.touchState.isPinching=!0,this.touchState.isDragging=!1,this.image.classList.remove("dragging");const i=t.touches[0],s=t.touches[1];this.touchState.initialDistance=this.getDistance(i,s),this.touchState.initialScale=this.scale,this.touchState.initialTranslateX=this.translateX,this.touchState.initialTranslateY=this.translateY,this.touchState.centerX=(i.clientX+s.clientX)/2,this.touchState.centerY=(i.clientY+s.clientY)/2;const n=this.imageContainer.getBoundingClientRect(),e=this.touchState.centerX-n.left,h=this.touchState.centerY-n.top;this.calculateRelativeCenter(e,h),this.touchState.movementCount=0,this.touchState.scaleRatio=1}t.preventDefault()}),this.addEvent(this.image,"touchmove",t=>{if(!(Date.now()-this.touchState.lastTouchTime<16)){if(this.touchState.lastTouchTime=Date.now(),1===t.touches.length&&this.touchState.isDragging&&!this.touchState.isPinching){const i=t.touches[0].clientX-this.touchState.startX,s=t.touches[0].clientY-this.touchState.startY,n=this.rotatePoint(i,s,-this.rotation);this.touchState.movementCount++,(this.touchState.movementCount>this.touchState.stabilizationThreshold||Math.abs(i)>5||Math.abs(s)>5)&&(this.translateX=this.touchState.startTranslateX+n.x,this.translateY=this.touchState.startTranslateY+n.y,this.updateImageTransform(),this.options.onDrag&&this.options.onDrag({data:this.images[this.currentIndex],index:this.currentIndex,translateX:this.translateX,translateY:this.translateY}))}else if(2===t.touches.length&&this.touchState.isPinching){const i=t.touches[0],s=t.touches[1],n=this.getDistance(i,s);this.touchState.scaleRatio=n/this.touchState.initialDistance;const e=this.touchState.initialScale*this.touchState.scaleRatio,h=this.options.minScale,o=this.options.maxScale,a=Math.max(h,Math.min(o,e));if(Math.abs(a-this.scale)>this.touchState.minScaleChange){const t=a/this.touchState.initialScale,i=this.imageContainer.getBoundingClientRect(),s=i.width,n=i.height;this.translateX=this.touchState.initialTranslateX*t+this.touchState.centerX-s/2-t*(this.touchState.centerX-s/2),this.translateY=this.touchState.initialTranslateY*t+this.touchState.centerY-n/2-t*(this.touchState.centerY-n/2),this.scale=a,this.updateImageTransform(),this.updateZoomIndicator(),this.options.onZoom&&this.options.onZoom({data:this.images[this.currentIndex],index:this.currentIndex,scale:this.scale,oldScale:this.touchState.initialScale})}}t.preventDefault()}}),this.addEvent(this.image,"touchend",t=>{0===t.touches.length?(this.touchState.isDragging=!1,this.touchState.isPinching=!1,this.image.classList.remove("dragging")):1===t.touches.length&&this.touchState.isPinching&&(this.touchState.isPinching=!1,this.touchState.isDragging=!0,this.touchState.startX=t.touches[0].clientX,this.touchState.startY=t.touches[0].clientY,this.touchState.startTranslateX=this.translateX,this.touchState.startTranslateY=this.translateY,this.image.classList.add("dragging")),t.preventDefault()}),this.addEvent(this.image,"touchcancel",()=>{this.touchState.isDragging=!1,this.touchState.isPinching=!1,this.image.classList.remove("dragging")})}getDistance(t,i){const s=t.clientX-i.clientX,n=t.clientY-i.clientY;return Math.sqrt(s*s+n*n)}calculateRelativeCenter(t,i){if(!this.imageMetadata[this.currentIndex])return;const s=this.imageContainer.clientWidth,n=this.imageContainer.clientHeight/2,e=t-s/2-this.translateX,h=i-n-this.translateY;this.touchState.relativeCenterX=e/this.scale,this.touchState.relativeCenterY=h/this.scale}zoomAtPoint(t,i,s){const n=this.scale,e=this.options.maxScale,h=this.options.minScale,o=Math.max(h,Math.min(e,this.scale+t));if(o===this.scale)return;const a=o/n,r=this.imageContainer.clientWidth,l=this.imageContainer.clientHeight;this.translateX=this.translateX*a+i-r/2-a*(i-r/2),this.translateY=this.translateY*a+s-l/2-a*(s-l/2),this.scale=o,this.updateImageTransform(),this.updateZoomIndicator(),this.options.onZoom&&this.options.onZoom({data:this.images[this.currentIndex],index:this.currentIndex,scale:this.scale,oldScale:n})}zoom(t){const i=this.imageContainer.clientWidth,s=this.imageContainer.clientHeight;this.zoomAtPoint(t,i/2,s/2)}rotate(t){const i=this.rotation,s=i+t;if(i===s)return;const n=this.imageContainer.getBoundingClientRect(),e=n.width/2,h=n.height/2,o=e+this.translateX,a=h+this.translateY;this.rotation=s,this.translateX=0,this.translateY=0;const r=this.imageMetadata[this.currentIndex];if(r){const t=this.calculateBoundingBox(r.width,r.height,s),i=t.width*this.scale,n=t.height*this.scale;this.translateX=(o-e)*(i/(i-this.translateX)),this.translateY=(a-h)*(n/(n-this.translateY))}this.updateImageTransform(),this.updateZoomIndicator(),this.options.onRotate&&this.options.onRotate({data:this.images[this.currentIndex],index:this.currentIndex,rotation:this.rotation,oldRotation:i})}calculateBoundingBox(t,i,s){const n=s*Math.PI/180,e=Math.abs(Math.cos(n)),h=Math.abs(Math.sin(n));return{width:t*e+i*h,height:t*h+i*e}}reset(){this.rotation=0;const t=this.imageMetadata[this.currentIndex];t&&this.fitImageToScreen(t.width,t.height)}showOriginalSize(){this.rotation=0,this.scale=1,this.translateX=0,this.translateY=0,this.updateImageTransform(),this.updateZoomIndicator()}downloadImage(){const t=this.getImageUrl(this.currentIndex),i=this.imageMetadata[this.currentIndex];if(this.loadedImages.has(t)){const s=this.loadedImages.get(t);this.downloadFromImage(s,i)}}downloadFromImage(t,i){try{const s=document.createElement("canvas");s.width=t.naturalWidth||t.width,s.height=t.naturalHeight||t.height,s.getContext("2d").drawImage(t,0,0);const n=s.toDataURL("image/jpeg");this.downloadedImage(n,i)}catch(t){const s=this.getImageUrl(this.currentIndex);this.downloadedImage(s,i)}}downloadedImage(t,i){try{const s=document.createElement("a");s.href=t,s.download=i?i.name:"image.jpg",document.body.appendChild(s),s.click(),document.body.removeChild(s)}catch(t){}}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen&&(document.exitFullscreen(),this.isFullscreen=!1):(this.container.requestFullscreen().catch(t=>{}),this.isFullscreen=!0)}handleKeydown(t){if("block"===this.container.style.display){switch(t.key){case"Escape":this.close();break;case"ArrowLeft":this.prev();break;case"ArrowRight":this.next();break;case"ArrowUp":case"+":case"=":this.zoom(.1);break;case"ArrowDown":case"-":this.zoom(-.1);break;case"0":this.reset();break;case"f":case"F":this.toggleFullscreen();break;case"i":case"I":this.toggleImageInfo()}t.preventDefault()}}handleResize(){const t=this.imageMetadata[this.currentIndex];if(!t)return;const i=this.imageContainer.clientWidth,s=this.imageContainer.clientHeight,n=this.rotation%360;let e=t.width,h=t.height;90!==n&&270!==n||(e=t.height,h=t.width);const o=Math.min(i/e,s/h),a=e*this.scale,r=h*this.scale,l=a>i||r>s;let c=this.scale;c=l?Math.max(.1,o):o>=1?1:o,Math.abs(c-this.scale)>.01&&(this.scale=c,this.translateX=0,this.translateY=0,this.updateImageTransform(),this.updateZoomIndicator()),this.updateThumbnailsOnResize()}updateThumbnailsOnResize(){if(!this.thumbContainer)return;const t=parseInt(this.options.theme.thumbItemWidth),i=parseInt(this.options.theme.thumbGap),s=parseInt(this.options.theme.thumbPadding),n=this.options.preloadCount,e=window.innerWidth-2*s,h=Math.ceil(e/(t+i)),o=Math.min(h+n,this.images.length),a=this.thumbContainer.querySelectorAll(".images-viewer-thumbnail-item").length;if(a<o){const t=o-a;for(let i=a;i<a+t;i++)i<this.images.length&&this.createThumbnailItem(this.thumbContainer,i)}}onShow(){this.container.style.display="block",setTimeout(()=>{this.container.style.opacity="1",this.options.onShow&&this.options.onShow(this.container)},10)}close(){this.removeAllEvents(),this.cleanup(),this.container.style.opacity="0",setTimeout(()=>{this.container.style.display="none";const t=document.getElementById("images-viewer-styles");t&&t.remove(),this.container&&this.container.remove(),this.options.onClose&&this.options.onClose()},300)}cleanup(){for(const[t,i]of this.loadingImages.entries())i.onload=null,i.onerror=null,i.src="";this.loadedImages.clear(),this.loadingImages.clear(),this.imageMetadata=[],this.t&&(URL.revokeObjectURL(this.t),this.t=null)}}export{t as default};
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var global,factory;global=this,factory=function(){return class{constructor(t){if(this.defaultOptions={initialIndex:0,closeOnMaskClick:!1,loop:!0,preloadCount:3,maxCacheSize:30,minScale:.1,maxScale:5,buttons:{zoomIn:!0,zoomOut:!0,rotateLeft:!0,rotateRight:!0,reset:!0,download:!0,fullscreen:!0,prev:!0,next:!0,close:!0,topClose:!0,thumbnails:!0,info:!0,originalSize:!0},imageInfo:{visible:!1,showName:!0,showDimensions:!0},i18n:{info:{name:"\u540d\u79f0:",dimensions:"\u5c3a\u5bf8:",shortcuts:"\u5feb\u6377\u952e",zoomIn:"\u653e\u5927:",zoomOut:"\u7f29\u5c0f:",prev:"\u4e0a\u4e00\u5f20:",next:"\u4e0b\u4e00\u5f20:",reset:"\u91cd\u7f6e:",fullscreen:"\u5168\u5c4f:",info:"\u4fe1\u606f:",close:"\u5173\u95ed:"},buttons:{prev:"\u4e0a\u4e00\u5f20 (\u2190)",next:"\u4e0b\u4e00\u5f20 (\u2192)",close:"\u5173\u95ed (Esc)",loading:"\u52a0\u8f7d\u4e2d..."}},theme:{viewerBgColor:"rgba(0, 0, 0, 0.4)",toolbarBgColor:"rgba(150, 150, 150, 0.7)",toolbarBorderRadius:"30px",toolbarPadding:"8px 12px",toolbarBottom:"20px",buttonBgColor:"rgba(150, 150, 150, 0.7)",buttonHoverBg:"rgba(200, 200, 200, 0.4)",buttonSize:"40px",buttonFontSize:"20px",buttonBorderRadius:"50%",navButtonBgColor:"rgba(150, 150, 150, 0.7)",navButtonHoverBg:"rgba(200, 200, 200, 0.4)",navButtonSize:"50px",navButtonFontSize:"20px",navButtonBorderRadius:"50%",topCloseBtnSize:"50px",topCloseBtnTop:"20px",topCloseBtnRight:"20px",topCloseBtnFontSize:"24px",topCloseBtnBgColor:"rgba(150, 150, 150, 0.7)",topCloseBtnHoverBg:"rgba(200, 200, 200, 0.4)",infoBgColor:"rgba(150, 150, 150, 0.7)",infoBorderRadius:"12px",infoPadding:"10px 15px",infoFontSize:"13px",infoTop:"70px",infoLeft:"20px",zoomIndicatorBg:"rgba(150, 150, 150, 0.7)",zoomIndicatorBorderRadius:"18px",zoomIndicatorPadding:"6px 12px",zoomIndicatorFontSize:"14px",zoomIndicatorTop:"20px",zoomIndicatorLeft:"20px",activeColor:"rgba(100, 150, 255, 0.8)",textColor:"rgba(255, 255, 255, 0.9)",shadowColor:"rgba(0, 0, 0, 0.2)",transitionSpeed:"0.3s",thumbItemWidth:"70px",thumbItemHeight:"45px",thumbGap:"10px",thumbPadding:"15px",thumbMaxWidth:"90%"}},this.options={...this.defaultOptions,...t,buttons:{...this.defaultOptions.buttons,...t?.buttons||{}},imageInfo:{...this.defaultOptions.imageInfo,...t?.imageInfo||{}},i18n:{...this.defaultOptions.i18n,...t?.i18n||{},info:{...this.defaultOptions.i18n.info,...t?.i18n?.info||{}},buttons:{...this.defaultOptions.i18n.buttons,...t?.i18n?.buttons||{}}},theme:{...this.defaultOptions.theme,...t?.theme||{}}},this.parseImageOptions(t),0===this.images.length)throw new Error("\u672a\u63d0\u4f9b\u6709\u6548\u7684\u56fe\u7247URL");const i=Math.max(0,Math.min(this.options.initialIndex||0,this.images.length-1));this.currentIndex=i,this.scale=1,this.rotation=0,this.translateX=0,this.translateY=0,this.isDragging=!1,this.startX=0,this.startY=0,this.startTranslateX=0,this.startTranslateY=0,this.isFullscreen=!1,this.imageInfoVisible=this.options.imageInfo.visible,this.imageMetadata=[],this.loadedImages=new Map,this.loadingImages=new Map,this.loadedThumbnails=new Map,this.loadingThumbnails=new Map,this.lastTapTime=0,this.lastScale=1,this.lastTranslateX=0,this.lastTranslateY=0,this.hasPreviousState=!1,this.isToggledState=!1,this.touchState={isDragging:!1,isPinching:!1,initialDistance:null,initialScale:null,initialTranslateX:null,initialTranslateY:null,centerX:null,centerY:null,relativeCenterX:null,relativeCenterY:null,lastTouchTime:0,startX:0,startY:0,startTranslateX:0,startTranslateY:0,minScaleChange:.005,scaleRatio:1,stabilizationThreshold:3,movementCount:0},this.eventListeners=new Map,this.injectStyles(),this.preloadImages(),this.createOptimizedElements(),this.bindEvents(),this.onShow(),this.loadCurrentImage()}injectStyles(){const t=document.createElement("style");t.id="images-viewer-styles",t.textContent=`\n :root {\n /* \u80cc\u666f\u76f8\u5173\u53d8\u91cf */\n --viewer-bg-color: ${this.options.theme.viewerBgColor};\n \n /* \u5de5\u5177\u680f\u76f8\u5173\u53d8\u91cf */\n --toolbar-bg-color: ${this.options.theme.toolbarBgColor};\n --toolbar-border-radius: ${this.options.theme.toolbarBorderRadius};\n --toolbar-padding: ${this.options.theme.toolbarPadding};\n --toolbar-bottom: ${this.options.theme.toolbarBottom};\n \n /* \u6309\u94ae\u76f8\u5173\u53d8\u91cf */\n --button-bg-color: ${this.options.theme.buttonBgColor};\n --button-hover-bg: ${this.options.theme.buttonHoverBg};\n --button-size: ${this.options.theme.buttonSize};\n --button-font-size: ${this.options.theme.buttonFontSize};\n --button-border-radius: ${this.options.theme.buttonBorderRadius};\n\n /* \u5bfc\u822a\u6309\u94ae\u76f8\u5173\u53d8\u91cf */\n --nav-button-bg-color: ${this.options.theme.navButtonBgColor};\n --nav-button-hover-bg: ${this.options.theme.navButtonHoverBg};\n --nav-button-size: ${this.options.theme.navButtonSize};\n --nav-button-font-size: ${this.options.theme.navButtonFontSize};\n --nav-button-border-radius: ${this.options.theme.navButtonBorderRadius};\n \n /* \u53f3\u4e0a\u89d2\u5173\u95ed\u6309\u94ae\u53d8\u91cf */\n --top-close-btn-size: ${this.options.theme.topCloseBtnSize};\n --top-close-btn-top: ${this.options.theme.topCloseBtnTop};\n --top-close-btn-right: ${this.options.theme.topCloseBtnRight};\n --top-close-btn-font-size: ${this.options.theme.topCloseBtnFontSize};\n --top-close-btn-bg-color: ${this.options.theme.topCloseBtnBgColor};\n --top-close-btn-hover-bg: ${this.options.theme.topCloseBtnHoverBg};\n \n /* \u4fe1\u606f\u680f\u76f8\u5173\u53d8\u91cf */\n --info-bg-color: ${this.options.theme.infoBgColor};\n --info-border-radius: ${this.options.theme.infoBorderRadius};\n --info-padding: ${this.options.theme.infoPadding};\n --info-font-size: ${this.options.theme.infoFontSize};\n --info-top: ${this.options.theme.infoTop};\n --info-left: ${this.options.theme.infoLeft};\n \n /* \u7f29\u653e\u6307\u793a\u5668\u53d8\u91cf */\n --zoom-indicator-bg: ${this.options.theme.zoomIndicatorBg};\n --zoom-indicator-border-radius: ${this.options.theme.zoomIndicatorBorderRadius};\n --zoom-indicator-padding: ${this.options.theme.zoomIndicatorPadding};\n --zoom-indicator-font-size: ${this.options.theme.zoomIndicatorFontSize};\n --zoom-indicator-top: ${this.options.theme.zoomIndicatorTop};\n --zoom-indicator-left: ${this.options.theme.zoomIndicatorLeft};\n \n /* \u901a\u7528\u53d8\u91cf */\n --active-color: ${this.options.theme.activeColor};\n --text-color: ${this.options.theme.textColor};\n --shadow-color: ${this.options.theme.shadowColor};\n --transition-speed: ${this.options.theme.transitionSpeed};\n \n /* \u7f29\u7565\u56fe\u76f8\u5173\u53d8\u91cf */\n --thumb-max-width: ${this.options.theme.thumbMaxWidth};\n --thumb-gap: ${this.options.theme.thumbGap};\n --thumb-padding: ${this.options.theme.thumbPadding};\n --thumb-item-width: ${this.options.theme.thumbItemWidth};\n --thumb-item-height: ${this.options.theme.thumbItemHeight};\n }\n\n .images-viewer-container {\n position: fixed;\n left: 0;\n top: 0;\n inset: 0;\n width: 100%;\n height: 100%;\n z-index: 9999;\n opacity: 0;\n transition: opacity var(--transition-speed) ease;\n touch-action: none;\n -webkit-user-select: none;\n user-select: none;\n display: none;\n background-color: var(--viewer-bg-color);\n overflow: hidden;\n }\n\n .images-viewer-container::backdrop,\n .images-viewer-container:fullscreen {\n background-color: var(--viewer-bg-color);\n }\n\n /* \u56fe\u7247\u5bb9\u5668\u6837\u5f0f */\n .images-viewer-image-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n }\n\n /* \u56fe\u7247\u6837\u5f0f */\n .images-viewer-image {\n position: relative;\n object-fit: contain;\n cursor: grab;\n transition: transform 0.1s ease-out, opacity var(--transition-speed) ease;\n transform-origin: center center;\n opacity: 0;\n box-shadow: 0 8px 25px var(--shadow-color);\n border-radius: 4px;\n user-select: none;\n touch-action: none;\n }\n\n .images-viewer-image.loaded {\n opacity: 1;\n }\n\n .images-viewer-image.dragging {\n cursor: grabbing;\n transition: none;\n }\n\n /* \u52a0\u8f7d\u6307\u793a\u5668 */\n .images-viewer-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background-color: rgba(127, 127, 127, 0.7);\n padding: 20px 30px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n gap: 15px;\n color: var(--text-color);\n font-size: 18px;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--transition-speed) ease;\n z-index: 3;\n }\n\n .images-viewer-loading.active {\n opacity: 1;\n }\n\n .images-viewer-loading-spinner {\n width: 40px;\n height: 40px;\n border: 4px solid rgba(255, 255, 255, 0.2);\n border-top-color: var(--active-color);\n border-radius: 50%;\n animation: imageViewerSpin 1s linear infinite;\n }\n\n @keyframes imageViewerSpin {\n to {\n transform: rotate(360deg);\n }\n }\n\n /* \u53f3\u4e0a\u89d2\u5173\u95ed\u6309\u94ae\u6837\u5f0f */\n .images-viewer-top-close-btn {\n position: absolute;\n top: var(--top-close-btn-top);\n right: var(--top-close-btn-right);\n width: var(--top-close-btn-size);\n height: var(--top-close-btn-size);\n border-radius: 50%;\n background-color: var(--button-bg-color);\n color: var(--text-color);\n border: none;\n font-size: var(--top-close-btn-font-size);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all var(--transition-speed);\n z-index: 10;\n box-shadow: 0 2px 8px var(--shadow-color);\n }\n\n .images-viewer-top-close-btn:hover {\n background-color: var(--top-close-btn-hover-bg);\n transform: scale(1.1);\n }\n\n /* \u7f29\u653e\u6307\u793a\u5668\u6837\u5f0f */\n .images-viewer-zoom-indicator {\n position: absolute;\n top: var(--zoom-indicator-top);\n left: var(--zoom-indicator-left);\n color: var(--text-color);\n background-color: var(--zoom-indicator-bg);\n padding: var(--zoom-indicator-padding);\n border-radius: var(--zoom-indicator-border-radius);\n font-size: var(--zoom-indicator-font-size);\n z-index: 10;\n min-width: 60px;\n text-align: center;\n box-shadow: 0 2px 8px var(--shadow-color);\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n /* \u4fe1\u606f\u680f\u6837\u5f0f */\n .images-viewer-image-info {\n position: absolute;\n top: var(--info-top);\n left: var(--info-left);\n color: var(--text-color);\n background-color: var(--info-bg-color);\n padding: var(--info-padding);\n border-radius: var(--info-border-radius);\n font-size: var(--info-font-size);\n z-index: 10;\n max-width: calc(100% - 40px);\n box-shadow: 0 4px 12px var(--shadow-color);\n display: none;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .images-viewer-image-info.visible {\n display: block;\n animation: imageViewerFadeIn 0.3s ease;\n }\n\n .images-viewer-image-info p {\n margin: 4px 0;\n line-height: 1.4;\n }\n\n .images-viewer-image-info .info-label {\n color: #ddd;\n margin-right: 5px;\n }\n\n .images-viewer-shortcuts-title {\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n font-weight: bold;\n margin-bottom: 5px;\n }\n\n @keyframes imageViewerFadeIn {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* \u56fe\u7247\u8ba1\u6570\u5668 */\n .images-viewer-image-counter {\n position: absolute;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n color: var(--text-color);\n background-color: var(--info-bg-color);\n padding: 6px 12px;\n border-radius: 18px;\n font-size: 14px;\n z-index: 10;\n box-shadow: 0 2px 8px var(--shadow-color);\n }\n\n /* \u56fe\u7247\u6807\u9898\u663e\u793a */\n .images-viewer-image-title {\n position: absolute;\n top: 60px;\n left: 50%;\n transform: translateX(-50%);\n color: var(--text-color);\n padding: 4px 12px;\n border-radius: 18px;\n font-size: 12px;\n z-index: 10;\n max-width: 80%;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* \u5bfc\u822a\u6309\u94ae */\n .images-viewer-nav-buttons {\n position: absolute;\n top: 50%;\n left: 0;\n right: 0;\n transform: translateY(-50%);\n display: flex;\n justify-content: space-between;\n pointer-events: none;\n z-index: 5;\n padding: 0 10px;\n }\n\n .images-viewer-nav-btn {\n width: var(--nav-button-size);\n height: var(--nav-button-size);\n border-radius: var(--nav-button-border-radius);\n background-color: var(--nav-button-bg-color);\n color: var(--text-color);\n border: none;\n font-size: var(--nav-button-font-size);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n pointer-events: auto;\n opacity: 0.9;\n box-shadow: 0 2px 8px var(--shadow-color);\n z-index: 6;\n }\n\n .images-viewer-nav-btn:hover {\n background-color: var(--nav-button-hover-bg);\n opacity: 1;\n transform: scale(1.1);\n }\n\n .images-viewer-nav-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n transform: none;\n }\n\n /* \u5de5\u5177\u680f\u6837\u5f0f */\n .images-viewer-toolbar {\n position: absolute;\n bottom: var(--toolbar-bottom);\n left: 50%;\n transform: translateX(-50%);\n background-color: var(--toolbar-bg-color);\n padding: var(--toolbar-padding);\n border-radius: var(--toolbar-border-radius);\n display: flex;\n gap: 2px;\n z-index: 10;\n box-shadow: 0 6px 25px var(--shadow-color);\n max-width: calc(100% - 40px);\n overflow-x: auto;\n overflow-y: hidden;\n border: 1px solid rgba(255, 255, 255, 0.1);\n scrollbar-width: none;\n -ms-overflow-style: none;\n -webkit-overflow-scrolling: touch;\n }\n\n .images-viewer-toolbar::-webkit-scrollbar {\n display: none;\n }\n\n .images-viewer-tool-btn {\n width: var(--button-size);\n height: var(--button-size);\n background-color: transparent;\n border: none;\n color: var(--text-color);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--button-font-size);\n transition: all 0.2s;\n flex-shrink: 0;\n position: relative;\n border-radius: var(--button-border-radius);\n margin: 0 2px;\n z-index: 11;\n line-height: 1;\n }\n\n .images-viewer-tool-btn:hover {\n background-color: var(--button-hover-bg);\n transform: translateY(-2px);\n box-shadow: 0 4px 10px var(--shadow-color);\n }\n\n .images-viewer-tool-btn:active {\n background-color: rgba(255, 255, 255, 0.3);\n transform: translateY(0);\n }\n\n .images-viewer-tool-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* \u7f29\u7565\u56fe\u5bb9\u5668 */\n .images-viewer-thumbnails-container {\n position: absolute;\n bottom: 90px;\n left: 50%;\n max-width: var(--thumb-max-width);\n transform: translateX(-50%);\n padding: 10px var(--thumb-padding);\n background-color: var(--toolbar-bg-color);\n border-radius: 12px;\n display: flex;\n gap: var(--thumb-gap);\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n z-index: 10;\n box-shadow: 0 3px 15px var(--shadow-color);\n -webkit-overflow-scrolling: touch;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .images-viewer-thumbnails-container::-webkit-scrollbar {\n display: none;\n }\n\n .images-viewer-thumbnail-item {\n width: var(--thumb-item-width);\n height: var(--thumb-item-height);\n border: 2px solid transparent;\n border-radius: 6px;\n overflow: hidden;\n cursor: pointer;\n flex-shrink: 0;\n transition: all 0.2s;\n z-index: 11;\n position: relative;\n opacity: 0.6;\n }\n\n .images-viewer-thumbnail-item img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .images-viewer-thumbnail-item.active {\n border-color: var(--active-color);\n transform: scale(1.2);\n opacity: 1;\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n }\n\n /* \u7f29\u7565\u56fe\u52a0\u8f7d\u72b6\u6001 */\n .images-viewer-thumbnail-loading {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.3);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1;\n }\n\n .loading-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top: 2px solid var(--active-color);\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .loading-error {\n color: #ff6b6b;\n font-size: 10px;\n text-align: center;\n padding: 2px;\n }\n\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .images-viewer-thumbnail-item:hover {\n transform: scale(1.2);\n opacity: 0.9;\n }\n `,document.head.appendChild(t)}createOptimizedElements(){this.container=document.createElement("div"),this.container.className="images-viewer-container",document.body.appendChild(this.container),this.imageContainer=document.createElement("div"),this.imageContainer.className="images-viewer-image-container",this.container.appendChild(this.imageContainer),this.image=document.createElement("img"),this.image.className="images-viewer-image",this.image.alt="Preview image",this.image.crossOrigin="anonymous",this.imageContainer.appendChild(this.image),this.loading=document.createElement("div"),this.loading.className="images-viewer-loading",this.loading.innerHTML=`\n <div class="images-viewer-loading-spinner"></div>\n <div>${this.options.i18n.buttons.loading}</div>\n `,this.imageContainer.appendChild(this.loading),this.options.buttons.topClose&&(this.topCloseBtn=document.createElement("button"),this.topCloseBtn.className="images-viewer-top-close-btn",this.topCloseBtn.textContent="\xd7",this.topCloseBtn.title=this.options.i18n.buttons.close,this.container.appendChild(this.topCloseBtn)),this.zoomIndicator=document.createElement("div"),this.zoomIndicator.className="images-viewer-zoom-indicator",this.zoomIndicator.textContent="100%",this.container.appendChild(this.zoomIndicator),this.options.buttons.info&&(this.imageInfoPanel=document.createElement("div"),this.imageInfoPanel.className="images-viewer-image-info "+(this.imageInfoVisible?"visible":""),this.container.appendChild(this.imageInfoPanel)),this.images.length>1&&(this.counter=document.createElement("div"),this.counter.className="images-viewer-image-counter",this.container.appendChild(this.counter)),this.imageTitle=document.createElement("div"),this.imageTitle.className="images-viewer-image-title",this.container.appendChild(this.imageTitle),this.images.length>1&&(this.options.buttons.prev||this.options.buttons.next)&&this.createNavButtons(),this.images.length>1&&this.options.buttons.thumbnails&&this.createThumbnails(),this.createToolbar()}createNavButtons(){const t=document.createElement("div");t.className="images-viewer-nav-buttons",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.options.buttons.prev&&(this.prevBtn=document.createElement("button"),this.prevBtn.className="images-viewer-nav-btn images-viewer-prev-btn",this.prevBtn.textContent="\u2190",this.prevBtn.title=this.options.i18n.buttons.prev,this.addEvent(this.prevBtn,"click",t=>{t.stopPropagation(),this.prev()}),t.appendChild(this.prevBtn)),this.options.buttons.next&&(this.nextBtn=document.createElement("button"),this.nextBtn.className="images-viewer-nav-btn images-viewer-next-btn",this.nextBtn.textContent="\u2192",this.nextBtn.title=this.options.i18n.buttons.next,this.addEvent(this.nextBtn,"click",t=>{t.stopPropagation(),this.next()}),t.appendChild(this.nextBtn)),this.container.appendChild(t)}createToolbar(){const t=document.createElement("div");t.className="images-viewer-toolbar",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.addEvent(t,"wheel",i=>{i.preventDefault(),i.stopPropagation(),t.scrollLeft+=i.deltaY}),this.images.length>1&&(this.options.buttons.prev&&(this.toolbarPrevBtn=this.createToolButton("\u2190",()=>this.prev()),t.appendChild(this.toolbarPrevBtn)),this.options.buttons.next&&(this.toolbarNextBtn=this.createToolButton("\u2192",()=>this.next()),t.appendChild(this.toolbarNextBtn))),this.options.buttons.zoomOut&&(this.zoomOutBtn=this.createToolButton("\u2212",()=>this.zoom(-.1)),t.appendChild(this.zoomOutBtn)),this.options.buttons.zoomIn&&(this.zoomInBtn=this.createToolButton("+",()=>this.zoom(.1)),t.appendChild(this.zoomInBtn)),this.options.buttons.rotateLeft&&(this.rotateLeftBtn=this.createToolButton("\u21ba",()=>this.rotate(-90)),t.appendChild(this.rotateLeftBtn)),this.options.buttons.rotateRight&&(this.rotateRightBtn=this.createToolButton("\u21bb",()=>this.rotate(90)),t.appendChild(this.rotateRightBtn)),this.options.buttons.reset&&(this.resetBtn=this.createToolButton("\u27f3",()=>this.reset()),t.appendChild(this.resetBtn)),this.options.buttons.originalSize&&(this.originalSizeBtn=this.createToolButton("1:1",()=>this.showOriginalSize()),t.appendChild(this.originalSizeBtn)),this.options.buttons.info&&(this.infoBtn=this.createToolButton("\u24d8",()=>this.toggleImageInfo()),t.appendChild(this.infoBtn)),this.options.buttons.download&&(this.downloadBtn=this.createToolButton("\u21a1",()=>this.downloadImage()),t.appendChild(this.downloadBtn)),this.options.buttons.fullscreen&&(this.fullscreenBtn=this.createToolButton("\u26f6",()=>this.toggleFullscreen()),t.appendChild(this.fullscreenBtn)),this.options.buttons.close&&(this.closeBtn=this.createToolButton("\xd7",()=>this.close()),t.appendChild(this.closeBtn)),this.options.customButtons&&this.options.customButtons.forEach(i=>{t.appendChild(this.createToolButton(i[0],i[1]))}),this.container.appendChild(t)}createToolButton(t,i){const s=document.createElement("button");s.className="images-viewer-tool-btn";const n=document.createElement("span");return n.textContent=t,s.appendChild(n),this.addEvent(s,"click",t=>{t.stopPropagation(),i()}),s}createThumbnails(){const t=document.createElement("div");t.className="images-viewer-thumbnails-container",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.addEvent(t,"wheel",i=>{i.preventDefault(),i.stopPropagation(),t.scrollLeft+=i.deltaY});const i=parseInt(this.options.theme.thumbItemWidth),s=parseInt(this.options.theme.thumbGap),n=parseInt(this.options.theme.thumbPadding),e=this.options.preloadCount,o=window.innerWidth-2*n,h=Math.ceil(o/(i+s)),a=Math.min(h+e,this.images.length);for(let i=0;i<a;i++)this.createThumbnailItem(t,i);let r=a;this.addEvent(t,"scroll",()=>{const i=t.scrollLeft,s=t.clientWidth;if(i>t.scrollWidth-s-s&&r<this.images.length){const i=Math.min(h,this.images.length-r);for(let s=r;s<r+i;s++)this.createThumbnailItem(t,s);r+=i}}),this.container.appendChild(t),this.thumbContainer=t}createThumbnailItem(t,i){const s=this.getThumbnailUrl(i),n=this.getImageUrl(i),e=document.createElement("div");e.className="images-viewer-thumbnail-item "+(i===this.currentIndex?"active":""),e.dataset.index=i;const o=new Image;o.crossOrigin="anonymous";const h=document.createElement("div");h.className="images-viewer-thumbnail-loading",h.innerHTML='<div class="loading-spinner"></div>',e.appendChild(h),this.loadedThumbnails.has(s)?(o.src=this.loadedThumbnails.get(s).src,h.remove()):this.loadingThumbnails.has(s)?(h.style.display="flex",o.onload=()=>{h.remove(),o.onload=null,o.onerror=null},o.onerror=()=>{h.remove(),o.onload=null,o.onerror=null},o.src=s):this.loadedImages.has(n)?(o.src=this.loadedImages.get(n).src,h.remove()):(this.loadingThumbnails.set(s,o),h.style.display="flex",o.onload=()=>{this.loadedThumbnails.set(s,o),this.loadingThumbnails.delete(s),h.remove(),o.onload=null,o.onerror=null},o.onerror=()=>{this.loadingThumbnails.delete(s),h.remove(),o.onload=null,o.onerror=null},o.src=s),e.appendChild(o),this.addEvent(e,"click",t=>{t.stopPropagation();const i=parseInt(e.dataset.index);i!==this.currentIndex&&(this.currentIndex=i,this.loadCurrentImage(),this.updateThumbnails())}),t.appendChild(e)}updateThumbnailDisplay(t){if(!this.thumbContainer)return;const i=this.thumbContainer.querySelector(`[data-index="${t}"]`);if(i){const s=this.getThumbnailUrl(t),n=i.querySelector("img");if(n&&this.loadedThumbnails.has(s)){const t=this.loadedThumbnails.get(s);n.src=t.src;const e=i.querySelector(".images-viewer-thumbnail-loading");e&&e.remove()}}}updateImageTransform(){const t=`\n translate(${this.translateX}px, ${this.translateY}px)\n scale(${this.scale})\n rotate(${this.rotation}deg)\n `;this.image.style.transform=t}fitImageToScreen(t,i){this.scale=1,this.translateX=0,this.translateY=0;const s=this.imageContainer.clientWidth,n=this.imageContainer.clientHeight,e=this.rotation%360;let o=t,h=i;if(90!==e&&270!==e||(o=i,h=t),o>s||h>n){const t=s/o,i=n/h;this.scale=Math.min(t,i)}this.scale=Math.max(.1,this.scale),this.updateImageTransform(),this.updateZoomIndicator()}parseImageOptions(t){this.images=[],this.thumbnails=[],"string"==typeof t?this.images=[t]:Array.isArray(t)?this.images=t.filter(t=>"string"==typeof t&&""!==t.trim()):"object"==typeof t&&t.images&&Array.isArray(t.images)&&(this.images=t.images.map(t=>"string"==typeof t?t:"object"==typeof t&&t.url?(t.thumbnail&&this.thumbnails.push(t.thumbnail),t):null).filter(Boolean))}getImageUrl(t){const i=this.images[t];if("string"==typeof i)return i;if("object"==typeof i){if("function"==typeof i.url)return i.url(i,t);if(i.url)return i.url}return""}getThumbnailUrl(t){if(this.thumbnails[t]){const i=this.thumbnails[t];return"function"==typeof i?i(this.images[t],t):i}return this.getImageUrl(t)}getImageTitle(t){const i=this.images[t];if("object"==typeof i){if("function"==typeof i.title)return i.title(i,t);if(i.title)return i.title}return""}preloadImages(){const t=this.options.preloadCount;if(t<=0)return;const i=Math.max(0,this.currentIndex-t),s=Math.min(this.images.length-1,this.currentIndex+t);for(let t=i;t<=s;t++)this.loadImageAtIndex(t)}addToCache(t,i){this.loadedImages.set(t,i);const s=this.options.maxCacheSize;if(s>0&&this.loadedImages.size>s){const t=this.loadedImages.keys().next().value;this.loadedImages.delete(t)}}loadImageAtIndex(t){const i=this.getImageUrl(t);if(!i||this.loadedImages.has(i)||this.loadingImages.has(i))return;const s=new Image;s.onload=()=>{this.addToCache(i,s),this.loadingImages.delete(i),this.imageMetadata[t]={name:this.extractFileName(i),width:s.naturalWidth||s.width,height:s.naturalHeight||s.height},s.onload=null,s.onerror=null},s.onerror=()=>{this.loadingImages.delete(i),s.onload=null,s.onerror=null},s.crossOrigin="anonymous",s.src=i,this.loadingImages.set(i,s)}extractFileName(t){try{const i=new URL(t).pathname.split("/");let s=i[i.length-1];const n=s.indexOf("?");return n>-1&&(s=s.substring(0,n)),s||"unknown-image"}catch(t){return"unknown-image"}}loadCurrentImage(t){if(void 0!==t&&(this.currentIndex=t),this.hasPreviousState=!1,this.isToggledState=!1,this.images.length>1&&this.counter){let t=null;this.options.onCounter&&(t=this.options.onCounter({image:this.images[this.currentIndex],index:this.currentIndex,currentPage:this.currentIndex+1,totalPages:this.images.length,scale:this.scale,rotation:this.rotation})),t?this.counter.innerHTML=t:this.counter.textContent=`${this.currentIndex+1} / ${this.images.length}`}const i=this.getImageTitle(this.currentIndex);this.imageTitle&&i?(this.imageTitle.innerHTML=i,this.imageTitle.style.display="block"):this.imageTitle.style.display="none",this.updateNavButtons(),this.updateThumbnails(),this.scale=1,this.rotation=0,this.translateX=0,this.translateY=0;const s=this.getImageUrl(this.currentIndex);this.loadedImages.has(s)||this.loadingImages.has(s)||this.loadImageAtIndex(this.currentIndex),this.preloadAdjacentImages(),this.isImageLoaded()}preloadAdjacentImages(){const t=Math.max(0,this.currentIndex-2),i=Math.min(this.images.length-1,this.currentIndex+2);for(let s=t;s<=i;s++)this.loadThumbnailAtIndex(s);for(let s=t;s<=i;s++)this.loadImageAtIndex(s)}loadThumbnailAtIndex(t){const i=this.getThumbnailUrl(t);if(!i||this.loadedThumbnails.has(i)||this.loadingThumbnails.has(i))return;const s=new Image;s.onload=()=>{this.loadedThumbnails.set(i,s),this.loadingThumbnails.delete(i),this.updateThumbnailDisplay(t),s.onload=null,s.onerror=null},s.onerror=()=>{this.loadingThumbnails.delete(i),s.onload=null,s.onerror=null},s.crossOrigin="anonymous",s.src=i,this.loadingThumbnails.set(i,s)}isImageLoaded(){const t=this.getImageUrl(this.currentIndex),i=this.loadedImages.has(t),s=this.loadingImages.has(t);if(this.showLoading(),this.image.classList.remove("loaded"),i){this.image.src="";const i=this.loadedImages.get(t),s=document.createElement("canvas"),n=s.getContext("2d");s.width=i.naturalWidth||i.width,s.height=i.naturalHeight||i.height,n.drawImage(i,0,0),s.toBlob(t=>{if(t){const i=URL.createObjectURL(t);this.image.src=i,this.t&&(URL.revokeObjectURL(this.t),this.t=null),this.t=i}});const e=this.imageMetadata[this.currentIndex];return e&&(this.fitImageToScreen(e.width,e.height),this.updateImageInfo()),this.image.classList.add("loaded"),void this.hideLoading()}if(s){const i=()=>{this.loadedImages.has(t)?this.isImageLoaded():this.loadingImages.has(t)?setTimeout(i,100):this.hideLoading()};return void setTimeout(i,100)}const n=new Image;n.onload=()=>{this.addToCache(t,n),this.loadingImages.delete(t),this.imageMetadata[this.currentIndex]={name:this.extractFileName(t),width:n.naturalWidth||n.width,height:n.naturalHeight||n.height},this.isImageLoaded()},n.onerror=()=>{this.loadingImages.delete(t),this.hideLoading()},n.crossOrigin="anonymous",n.src=t,this.loadingImages.set(t,n)}updateZoomIndicator(){const t=Math.round(100*this.scale);if(this.options.onZoomIndicator){const i=this.options.onZoomIndicator({image:this.images[this.currentIndex],index:this.currentIndex,scale:this.scale,percentage:t,rotation:this.rotation});if(i)return void(this.zoomIndicator.innerHTML=i)}this.zoomIndicator.textContent=`${t}%`}updateImageInfo(){if(!this.options.buttons.info||!this.imageInfoPanel)return;const t=this.imageMetadata[this.currentIndex];if(!t)return;const i=this.options.i18n;let s="";this.options.onInfo&&(s=this.options.onInfo({image:this.images[this.currentIndex],index:this.currentIndex,metadata:t,scale:this.scale,rotation:this.rotation}),s)||(this.options.imageInfo.showName&&(s+=`<p><span class="info-label">${i.info.name}</span> ${t.name}</p>`),this.options.imageInfo.showDimensions&&(s+=`<p><span class="info-label">${i.info.dimensions}</span> ${t.width} \xd7 ${t.height}</p>`),s+=`\n <div class="images-viewer-shortcuts-title">${i.info.shortcuts}</div>\n <p><span class="info-label">${i.info.zoomIn}</span> \u2191 +</p>\n <p><span class="info-label">${i.info.zoomOut}</span> \u2193 -</p>\n <p><span class="info-label">${i.info.prev}</span> \u2190</p>\n <p><span class="info-label">${i.info.next}</span> \u2192</p>\n <p><span class="info-label">${i.info.reset}</span> 0</p>\n <p><span class="info-label">${i.info.fullscreen}</span> F</p>\n <p><span class="info-label">${i.info.info}</span> I</p>\n <p><span class="info-label">${i.info.close}</span> ESC</p>\n `),this.imageInfoPanel.innerHTML=s}toggleImageInfo(){this.options.buttons.info&&this.imageInfoPanel&&(this.imageInfoVisible=!this.imageInfoVisible,this.imageInfoVisible?this.imageInfoPanel.classList.add("visible"):this.imageInfoPanel.classList.remove("visible"))}updateNavButtons(){if(this.images.length<=1)return;const t=!!this.options.loop||this.currentIndex>0,i=!!this.options.loop||this.currentIndex<this.images.length-1;this.prevBtn&&(this.prevBtn.disabled=!t),this.nextBtn&&(this.nextBtn.disabled=!i),this.toolbarPrevBtn&&(this.toolbarPrevBtn.disabled=!t),this.toolbarNextBtn&&(this.toolbarNextBtn.disabled=!i)}updateThumbnails(){if(this.images.length<=1)return;document.querySelectorAll(".images-viewer-thumbnail-item").forEach(t=>{t.classList.remove("active")});const t=document.querySelector(`.images-viewer-thumbnail-item[data-index="${this.currentIndex}"]`);t&&(t.classList.add("active"),t.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center"}))}showLoading(){this.loading&&this.loading.classList.add("active")}hideLoading(){this.loading&&this.loading.classList.remove("active")}prev(){if(this.images.length<=1)return;let t=this.currentIndex-1;t<0&&(t=this.options.loop?this.images.length-1:0),t!==this.currentIndex&&(this.currentIndex=t,this.loadCurrentImage()),this.options.onChange&&this.options.onChange(this.currentIndex,"prev")}next(){if(this.images.length<=1)return;let t=this.currentIndex+1;t>=this.images.length&&(t=this.options.loop?0:this.images.length-1),t!==this.currentIndex&&(this.currentIndex=t,this.loadCurrentImage()),this.options.onChange&&this.options.onChange(this.currentIndex,"next")}bindEvents(){this.topCloseBtn&&this.addEvent(this.topCloseBtn,"click",()=>this.close()),this.options.closeOnMaskClick&&this.addEvent(this.imageContainer,"click",t=>{t.target==this.imageContainer&&this.close()}),this.addEvent(document,"keydown",t=>this.handleKeydown(t));const t=function(t){let i,s=0;return function(...n){const e=Date.now(),o=e-s;o>300?(t.apply(this,n),s=e):(clearTimeout(i),i=setTimeout(()=>{t.apply(this,n),s=e},300-o))}}(()=>{this.handleResize()});this.addEvent(window,"resize",t),this.bindDragEvents(),this.bindTouchEvents()}addEvent(t,i,s,n){t.addEventListener(i,s,n);const e=`${i}-${Date.now()}-${Math.random()}`;this.eventListeners.set(e,{element:t,event:i,handler:s})}removeAllEvents(){this.eventListeners.forEach(({element:t,event:i,handler:s})=>{t.removeEventListener(i,s)}),this.eventListeners.clear()}rotatePoint(t,i,s){const n=s*Math.PI,e=Math.cos(n),o=Math.sin(n);return{x:t*e-i*o,y:t*o+i*e}}bindDragEvents(){this.addEvent(this.image,"mousedown",t=>{0===t.button&&(this.isDragging=!0,this.image.classList.add("dragging"),this.startX=t.clientX,this.startY=t.clientY,this.startTranslateX=this.translateX,this.startTranslateY=this.translateY,t.preventDefault())}),this.addEvent(document,"mousemove",t=>{if(!this.isDragging)return;const i=t.clientX-this.startX,s=t.clientY-this.startY,n=this.rotatePoint(i,s,-this.rotation);if(this.translateX=this.startTranslateX+n.x,this.translateY=this.startTranslateY+n.y,this.updateImageTransform(),this.options.onDrag){const t=this.images[this.currentIndex];this.options.onDrag({image:t,index:this.currentIndex,translateX:this.translateX,translateY:this.translateY})}t.preventDefault()}),this.addEvent(document,"mouseup",()=>{this.isDragging&&(this.isDragging=!1,this.image.classList.remove("dragging"))}),this.addEvent(document,"mouseleave",()=>{this.isDragging&&(this.isDragging=!1,this.image.classList.remove("dragging"))}),this.addEvent(this.imageContainer,"wheel",t=>{t.preventDefault();const i=this.imageContainer.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top,e=t.deltaY>0?-.05:.05;this.zoomAtPoint(e,s,n)}),this.addEvent(this.image,"dblclick",t=>{t.preventDefault();const i=this.imageContainer.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top;if(Math.abs(this.scale-1)<.01)if(this.hasPreviousState)this.scale=this.lastScale,this.translateX=this.lastTranslateX,this.translateY=this.lastTranslateY,this.hasPreviousState=!1;else{this.lastScale=this.scale,this.lastTranslateX=this.translateX,this.lastTranslateY=this.translateY,this.hasPreviousState=!0;const t=1.5,i=t/this.scale,e=this.imageContainer.clientWidth,o=this.imageContainer.clientHeight;this.translateX=this.translateX*i+s-e/2-i*(s-e/2),this.translateY=this.translateY*i+n-o/2-i*(n-o/2),this.scale=t}else{this.lastScale=this.scale,this.lastTranslateX=this.translateX,this.lastTranslateY=this.translateY,this.hasPreviousState=!0;const t=1,i=t/this.scale,e=this.imageContainer.clientWidth,o=this.imageContainer.clientHeight;this.translateX=this.translateX*i+s-e/2-i*(s-e/2),this.translateY=this.translateY*i+n-o/2-i*(n-o/2),this.scale=t}this.updateImageTransform(),this.updateZoomIndicator()})}bindTouchEvents(){this.addEvent(this.image,"touchstart",t=>{if(this.touchState.lastTouchTime=Date.now(),1===t.touches.length)this.touchState.isPinching||(this.touchState.isDragging=!0,this.touchState.startX=t.touches[0].clientX,this.touchState.startY=t.touches[0].clientY,this.touchState.startTranslateX=this.translateX,this.touchState.startTranslateY=this.translateY,this.image.classList.add("dragging"));else if(2===t.touches.length){this.touchState.isPinching=!0,this.touchState.isDragging=!1,this.image.classList.remove("dragging");const i=t.touches[0],s=t.touches[1];this.touchState.initialDistance=this.getDistance(i,s),this.touchState.initialScale=this.scale,this.touchState.initialTranslateX=this.translateX,this.touchState.initialTranslateY=this.translateY,this.touchState.centerX=(i.clientX+s.clientX)/2,this.touchState.centerY=(i.clientY+s.clientY)/2;const n=this.imageContainer.getBoundingClientRect(),e=this.touchState.centerX-n.left,o=this.touchState.centerY-n.top;this.calculateRelativeCenter(e,o),this.touchState.movementCount=0,this.touchState.scaleRatio=1}t.preventDefault()}),this.addEvent(this.image,"touchmove",t=>{if(!(Date.now()-this.touchState.lastTouchTime<16)){if(this.touchState.lastTouchTime=Date.now(),1===t.touches.length&&this.touchState.isDragging&&!this.touchState.isPinching){const i=t.touches[0].clientX-this.touchState.startX,s=t.touches[0].clientY-this.touchState.startY,n=this.rotatePoint(i,s,-this.rotation);if(this.touchState.movementCount++,(this.touchState.movementCount>this.touchState.stabilizationThreshold||Math.abs(i)>5||Math.abs(s)>5)&&(this.translateX=this.touchState.startTranslateX+n.x,this.translateY=this.touchState.startTranslateY+n.y,this.updateImageTransform(),this.options.drag)){const t=this.images[this.currentIndex];this.options.drag({image:t,index:this.currentIndex,translateX:this.translateX,translateY:this.translateY})}}else if(2===t.touches.length&&this.touchState.isPinching){const i=t.touches[0],s=t.touches[1],n=this.getDistance(i,s);this.touchState.scaleRatio=n/this.touchState.initialDistance;const e=this.touchState.initialScale*this.touchState.scaleRatio,o=this.options.minScale,h=this.options.maxScale,a=Math.max(o,Math.min(h,e));if(Math.abs(a-this.scale)>this.touchState.minScaleChange){const t=a/this.touchState.initialScale,i=this.imageContainer.getBoundingClientRect(),s=i.width,n=i.height;if(this.translateX=this.touchState.initialTranslateX*t+this.touchState.centerX-s/2-t*(this.touchState.centerX-s/2),this.translateY=this.touchState.initialTranslateY*t+this.touchState.centerY-n/2-t*(this.touchState.centerY-n/2),this.scale=a,this.updateImageTransform(),this.updateZoomIndicator(),this.options.onZoom){const t=this.images[this.currentIndex];this.options.onZoom({image:t,index:this.currentIndex,scale:this.scale,oldScale:this.touchState.initialScale})}}}t.preventDefault()}}),this.addEvent(this.image,"touchend",t=>{0===t.touches.length?(this.touchState.isDragging=!1,this.touchState.isPinching=!1,this.image.classList.remove("dragging")):1===t.touches.length&&this.touchState.isPinching&&(this.touchState.isPinching=!1,this.touchState.isDragging=!0,this.touchState.startX=t.touches[0].clientX,this.touchState.startY=t.touches[0].clientY,this.touchState.startTranslateX=this.translateX,this.touchState.startTranslateY=this.translateY,this.image.classList.add("dragging")),t.preventDefault()}),this.addEvent(this.image,"touchcancel",()=>{this.touchState.isDragging=!1,this.touchState.isPinching=!1,this.image.classList.remove("dragging")})}getDistance(t,i){const s=t.clientX-i.clientX,n=t.clientY-i.clientY;return Math.sqrt(s*s+n*n)}calculateRelativeCenter(t,i){if(!this.imageMetadata[this.currentIndex])return;const s=this.imageContainer.clientWidth,n=this.imageContainer.clientHeight/2,e=t-s/2-this.translateX,o=i-n-this.translateY;this.touchState.relativeCenterX=e/this.scale,this.touchState.relativeCenterY=o/this.scale}zoomAtPoint(t,i,s){const n=this.scale,e=this.options.maxScale,o=this.options.minScale,h=Math.max(o,Math.min(e,this.scale+t));if(h===this.scale)return;const a=h/n,r=this.imageContainer.clientWidth,l=this.imageContainer.clientHeight;if(this.translateX=this.translateX*a+i-r/2-a*(i-r/2),this.translateY=this.translateY*a+s-l/2-a*(s-l/2),this.scale=h,this.updateImageTransform(),this.updateZoomIndicator(),this.options.zoom){const t=this.images[this.currentIndex];this.options.zoom({image:t,index:this.currentIndex,scale:this.scale,oldScale:n})}}zoom(t){const i=this.imageContainer.clientWidth,s=this.imageContainer.clientHeight;this.zoomAtPoint(t,i/2,s/2)}rotate(t){const i=this.rotation,s=i+t;if(i===s)return;const n=this.imageContainer.getBoundingClientRect(),e=n.width/2,o=n.height/2,h=e+this.translateX,a=o+this.translateY;this.rotation=s,this.translateX=0,this.translateY=0;const r=this.imageMetadata[this.currentIndex];if(r){const t=this.calculateBoundingBox(r.width,r.height,s),i=t.width*this.scale,n=t.height*this.scale;this.translateX=(h-e)*(i/(i-this.translateX)),this.translateY=(a-o)*(n/(n-this.translateY))}if(this.updateImageTransform(),this.updateZoomIndicator(),this.options.onRotate){const t=this.images[this.currentIndex];this.options.onRotate({image:t,index:this.currentIndex,rotation:this.rotation,oldRotation:i})}}calculateBoundingBox(t,i,s){const n=s*Math.PI/180,e=Math.abs(Math.cos(n)),o=Math.abs(Math.sin(n));return{width:t*e+i*o,height:t*o+i*e}}reset(){this.rotation=0;const t=this.imageMetadata[this.currentIndex];t&&this.fitImageToScreen(t.width,t.height)}showOriginalSize(){this.rotation=0,this.scale=1,this.translateX=0,this.translateY=0,this.updateImageTransform(),this.updateZoomIndicator()}downloadImage(){const t=this.getImageUrl(this.currentIndex),i=this.imageMetadata[this.currentIndex];if(this.loadedImages.has(t)){const s=this.loadedImages.get(t);this.downloadFromImage(s,i)}}downloadFromImage(t,i){try{const s=document.createElement("canvas");s.width=t.naturalWidth||t.width,s.height=t.naturalHeight||t.height,s.getContext("2d").drawImage(t,0,0);const n=s.toDataURL("image/jpeg");this.downloadedImage(n,i)}catch(t){const s=this.getImageUrl(this.currentIndex);this.downloadedImage(s,i)}}downloadedImage(t,i){try{const s=document.createElement("a");s.href=t,s.download=i?i.name:"image.jpg",document.body.appendChild(s),s.click(),document.body.removeChild(s)}catch(t){}}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen&&(document.exitFullscreen(),this.isFullscreen=!1):(this.container.requestFullscreen().catch(t=>{}),this.isFullscreen=!0)}handleKeydown(t){if("block"===this.container.style.display){switch(t.key){case"Escape":this.close();break;case"ArrowLeft":this.prev();break;case"ArrowRight":this.next();break;case"ArrowUp":case"+":case"=":this.zoom(.1);break;case"ArrowDown":case"-":this.zoom(-.1);break;case"0":this.reset();break;case"f":case"F":this.toggleFullscreen();break;case"i":case"I":this.toggleImageInfo()}t.preventDefault()}}handleResize(){const t=this.imageMetadata[this.currentIndex];if(!t)return;const i=this.imageContainer.clientWidth,s=this.imageContainer.clientHeight,n=this.rotation%360;let e=t.width,o=t.height;90!==n&&270!==n||(e=t.height,o=t.width);const h=Math.min(i/e,s/o),a=e*this.scale,r=o*this.scale,l=a>i||r>s;let c=this.scale;c=l?Math.max(.1,h):h>=1?1:h,Math.abs(c-this.scale)>.01&&(this.scale=c,this.translateX=0,this.translateY=0,this.updateImageTransform(),this.updateZoomIndicator()),this.updateThumbnailsOnResize()}updateThumbnailsOnResize(){if(!this.thumbContainer)return;const t=parseInt(this.options.theme.thumbItemWidth),i=parseInt(this.options.theme.thumbGap),s=parseInt(this.options.theme.thumbPadding),n=this.options.preloadCount,e=window.innerWidth-2*s,o=Math.ceil(e/(t+i)),h=Math.min(o+n,this.images.length),a=this.thumbContainer.querySelectorAll(".images-viewer-thumbnail-item").length;if(a<h){const t=h-a;for(let i=a;i<a+t;i++)i<this.images.length&&this.createThumbnailItem(this.thumbContainer,i)}}onShow(){this.container.style.display="block",setTimeout(()=>{this.container.style.opacity="1",this.options.onShow&&this.options.onShow(this.container)},10)}close(){this.removeAllEvents(),this.cleanup(),this.container.style.opacity="0",setTimeout(()=>{this.container.style.display="none";const t=document.getElementById("images-viewer-styles");t&&t.remove(),this.container&&this.container.remove(),this.options.onClose&&this.options.onClose()},300)}cleanup(){for(const[t,i]of this.loadingImages.entries())i.onload=null,i.onerror=null,i.src="";this.loadedImages.clear(),this.loadingImages.clear(),this.imageMetadata=[],this.t&&(URL.revokeObjectURL(this.t),this.t=null)}}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define(factory):(global="undefined"!=typeof globalThis?globalThis:global||self).ImagesViewer=factory();
|
|
1
|
+
var global,factory;global=this,factory=function(){return class{constructor(t){if(this.defaultOptions={initialIndex:0,closeOnMaskClick:!1,loop:!0,preloadCount:3,maxCacheSize:30,minScale:.1,maxScale:5,retryOnError:!1,props:{url:"url",title:"title",thumbnail:"thumbnail"},buttons:{zoomIn:!0,zoomOut:!0,rotateLeft:!0,rotateRight:!0,reset:!0,download:!0,fullscreen:!0,prev:!0,next:!0,close:!0,topClose:!0,thumbnails:!0,info:!0,originalSize:!0},imageInfo:{visible:!1,showName:!0,showDimensions:!0},i18n:{info:{name:"\u540d\u79f0:",dimensions:"\u5c3a\u5bf8:",shortcuts:"\u5feb\u6377\u952e",zoomIn:"\u653e\u5927:",zoomOut:"\u7f29\u5c0f:",prev:"\u4e0a\u4e00\u5f20:",next:"\u4e0b\u4e00\u5f20:",reset:"\u91cd\u7f6e:",fullscreen:"\u5168\u5c4f:",info:"\u4fe1\u606f:",close:"\u5173\u95ed:"},buttons:{prev:"\u4e0a\u4e00\u5f20 (\u2190)",next:"\u4e0b\u4e00\u5f20 (\u2192)",close:"\u5173\u95ed (Esc)",loading:"\u52a0\u8f7d\u4e2d..."}},theme:{viewerBgColor:"rgba(0, 0, 0, 0.4)",toolbarBgColor:"rgba(150, 150, 150, 0.7)",toolbarBorderRadius:"30px",toolbarPadding:"8px 12px",toolbarBottom:"20px",buttonBgColor:"rgba(150, 150, 150, 0.7)",buttonHoverBg:"rgba(200, 200, 200, 0.4)",buttonSize:"40px",buttonFontSize:"20px",buttonBorderRadius:"50%",navButtonBgColor:"rgba(150, 150, 150, 0.7)",navButtonHoverBg:"rgba(200, 200, 200, 0.4)",navButtonSize:"50px",navButtonFontSize:"20px",navButtonBorderRadius:"50%",topCloseBtnSize:"50px",topCloseBtnTop:"20px",topCloseBtnRight:"20px",topCloseBtnFontSize:"24px",topCloseBtnBgColor:"rgba(150, 150, 150, 0.7)",topCloseBtnHoverBg:"rgba(200, 200, 200, 0.4)",infoBgColor:"rgba(150, 150, 150, 0.7)",infoBorderRadius:"12px",infoPadding:"10px 15px",infoFontSize:"13px",infoTop:"70px",infoLeft:"20px",zoomIndicatorBg:"rgba(150, 150, 150, 0.7)",zoomIndicatorBorderRadius:"18px",zoomIndicatorPadding:"6px 12px",zoomIndicatorFontSize:"14px",zoomIndicatorTop:"20px",zoomIndicatorLeft:"20px",activeColor:"rgba(100, 150, 255, 0.8)",textColor:"rgba(255, 255, 255, 0.9)",shadowColor:"rgba(0, 0, 0, 0.2)",transitionSpeed:"0.3s",thumbItemWidth:"70px",thumbItemHeight:"45px",thumbGap:"10px",thumbPadding:"15px",thumbMaxWidth:"90%"}},this.options={...this.defaultOptions,...t,buttons:{...this.defaultOptions.buttons,...t?.buttons||{}},imageInfo:{...this.defaultOptions.imageInfo,...t?.imageInfo||{}},props:{...this.defaultOptions.props,...t?.props||{}},i18n:{...this.defaultOptions.i18n,...t?.i18n||{},info:{...this.defaultOptions.i18n.info,...t?.i18n?.info||{}},buttons:{...this.defaultOptions.i18n.buttons,...t?.i18n?.buttons||{}}},theme:{...this.defaultOptions.theme,...t?.theme||{}}},this.parseImageOptions(t),0===this.images.length)throw new Error("No images URL");const i=Math.max(0,Math.min(this.options.initialIndex||0,this.images.length-1));this.currentIndex=i,this.scale=1,this.rotation=0,this.translateX=0,this.translateY=0,this.isDragging=!1,this.startX=0,this.startY=0,this.startTranslateX=0,this.startTranslateY=0,this.isFullscreen=!1,this.imageInfoVisible=this.options.imageInfo.visible,this.imageMetadata=[],this.loadedImages=new Map,this.loadingImages=new Map,this.failedImages=new Set,this.loadedThumbnails=new Map,this.loadingThumbnails=new Map,this.failedThumbnails=new Set,this.lastTapTime=0,this.lastScale=1,this.lastTranslateX=0,this.lastTranslateY=0,this.hasPreviousState=!1,this.isToggledState=!1,this.touchState={isDragging:!1,isPinching:!1,initialDistance:null,initialScale:null,initialTranslateX:null,initialTranslateY:null,centerX:null,centerY:null,relativeCenterX:null,relativeCenterY:null,lastTouchTime:0,startX:0,startY:0,startTranslateX:0,startTranslateY:0,minScaleChange:.005,scaleRatio:1,stabilizationThreshold:3,movementCount:0},this.eventListeners=new Map,this.injectStyles(),this.preloadImages(),this.createOptimizedElements(),this.bindEvents(),this.onShow(),this.loadCurrentImage()}injectStyles(){const t=document.createElement("style");t.id="images-viewer-styles",t.textContent=`\n :root {\n /* \u80cc\u666f\u76f8\u5173\u53d8\u91cf */\n --viewer-bg-color: ${this.options.theme.viewerBgColor};\n \n /* \u5de5\u5177\u680f\u76f8\u5173\u53d8\u91cf */\n --toolbar-bg-color: ${this.options.theme.toolbarBgColor};\n --toolbar-border-radius: ${this.options.theme.toolbarBorderRadius};\n --toolbar-padding: ${this.options.theme.toolbarPadding};\n --toolbar-bottom: ${this.options.theme.toolbarBottom};\n \n /* \u6309\u94ae\u76f8\u5173\u53d8\u91cf */\n --button-bg-color: ${this.options.theme.buttonBgColor};\n --button-hover-bg: ${this.options.theme.buttonHoverBg};\n --button-size: ${this.options.theme.buttonSize};\n --button-font-size: ${this.options.theme.buttonFontSize};\n --button-border-radius: ${this.options.theme.buttonBorderRadius};\n\n /* \u5bfc\u822a\u6309\u94ae\u76f8\u5173\u53d8\u91cf */\n --nav-button-bg-color: ${this.options.theme.navButtonBgColor};\n --nav-button-hover-bg: ${this.options.theme.navButtonHoverBg};\n --nav-button-size: ${this.options.theme.navButtonSize};\n --nav-button-font-size: ${this.options.theme.navButtonFontSize};\n --nav-button-border-radius: ${this.options.theme.navButtonBorderRadius};\n \n /* \u53f3\u4e0a\u89d2\u5173\u95ed\u6309\u94ae\u53d8\u91cf */\n --top-close-btn-size: ${this.options.theme.topCloseBtnSize};\n --top-close-btn-top: ${this.options.theme.topCloseBtnTop};\n --top-close-btn-right: ${this.options.theme.topCloseBtnRight};\n --top-close-btn-font-size: ${this.options.theme.topCloseBtnFontSize};\n --top-close-btn-bg-color: ${this.options.theme.topCloseBtnBgColor};\n --top-close-btn-hover-bg: ${this.options.theme.topCloseBtnHoverBg};\n \n /* \u4fe1\u606f\u680f\u76f8\u5173\u53d8\u91cf */\n --info-bg-color: ${this.options.theme.infoBgColor};\n --info-border-radius: ${this.options.theme.infoBorderRadius};\n --info-padding: ${this.options.theme.infoPadding};\n --info-font-size: ${this.options.theme.infoFontSize};\n --info-top: ${this.options.theme.infoTop};\n --info-left: ${this.options.theme.infoLeft};\n \n /* \u7f29\u653e\u6307\u793a\u5668\u53d8\u91cf */\n --zoom-indicator-bg: ${this.options.theme.zoomIndicatorBg};\n --zoom-indicator-border-radius: ${this.options.theme.zoomIndicatorBorderRadius};\n --zoom-indicator-padding: ${this.options.theme.zoomIndicatorPadding};\n --zoom-indicator-font-size: ${this.options.theme.zoomIndicatorFontSize};\n --zoom-indicator-top: ${this.options.theme.zoomIndicatorTop};\n --zoom-indicator-left: ${this.options.theme.zoomIndicatorLeft};\n \n /* \u901a\u7528\u53d8\u91cf */\n --active-color: ${this.options.theme.activeColor};\n --text-color: ${this.options.theme.textColor};\n --shadow-color: ${this.options.theme.shadowColor};\n --transition-speed: ${this.options.theme.transitionSpeed};\n \n /* \u7f29\u7565\u56fe\u76f8\u5173\u53d8\u91cf */\n --thumb-max-width: ${this.options.theme.thumbMaxWidth};\n --thumb-gap: ${this.options.theme.thumbGap};\n --thumb-padding: ${this.options.theme.thumbPadding};\n --thumb-item-width: ${this.options.theme.thumbItemWidth};\n --thumb-item-height: ${this.options.theme.thumbItemHeight};\n }\n\n .images-viewer-container {\n position: fixed;\n left: 0;\n top: 0;\n inset: 0;\n width: 100%;\n height: 100%;\n z-index: 9999;\n opacity: 0;\n transition: opacity var(--transition-speed) ease;\n touch-action: none;\n -webkit-user-select: none;\n user-select: none;\n display: none;\n background-color: var(--viewer-bg-color);\n overflow: hidden;\n }\n\n .images-viewer-container::backdrop,\n .images-viewer-container:fullscreen {\n background-color: var(--viewer-bg-color);\n }\n\n /* \u56fe\u7247\u5bb9\u5668\u6837\u5f0f */\n .images-viewer-image-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n }\n\n /* \u56fe\u7247\u6837\u5f0f */\n .images-viewer-image {\n position: relative;\n object-fit: contain;\n cursor: grab;\n opacity: 0;\n box-shadow: 0 8px 25px var(--shadow-color);\n border-radius: 4px;\n user-select: none;\n touch-action: none;\n }\n\n .images-viewer-image.loaded {\n opacity: 1;\n transition: transform 0.1s ease-out, opacity var(--transition-speed) ease;\n transform-origin: center center;\n }\n\n .images-viewer-image.dragging {\n cursor: grabbing;\n transition: none;\n }\n\n /* \u52a0\u8f7d\u6307\u793a\u5668 */\n .images-viewer-loading {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n background-color: rgba(127, 127, 127, 0.7);\n padding: 20px 30px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n gap: 15px;\n color: var(--text-color);\n font-size: 18px;\n opacity: 0;\n pointer-events: none;\n transition: opacity var(--transition-speed) ease;\n z-index: 3;\n }\n\n .images-viewer-loading.active {\n opacity: 1;\n }\n\n .images-viewer-loading-spinner {\n width: 40px;\n height: 40px;\n border: 4px solid rgba(255, 255, 255, 0.2);\n border-top-color: var(--active-color);\n border-radius: 50%;\n animation: imageViewerSpin 1s linear infinite;\n }\n\n @keyframes imageViewerSpin {\n to {\n transform: rotate(360deg);\n }\n }\n\n /* \u53f3\u4e0a\u89d2\u5173\u95ed\u6309\u94ae\u6837\u5f0f */\n .images-viewer-top-close-btn {\n position: absolute;\n top: var(--top-close-btn-top);\n right: var(--top-close-btn-right);\n width: var(--top-close-btn-size);\n height: var(--top-close-btn-size);\n border-radius: 50%;\n background-color: var(--button-bg-color);\n color: var(--text-color);\n border: none;\n font-size: var(--top-close-btn-font-size);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all var(--transition-speed);\n z-index: 10;\n box-shadow: 0 2px 8px var(--shadow-color);\n }\n\n .images-viewer-top-close-btn:hover {\n background-color: var(--top-close-btn-hover-bg);\n transform: scale(1.1);\n }\n\n /* \u7f29\u653e\u6307\u793a\u5668\u6837\u5f0f */\n .images-viewer-zoom-indicator {\n position: absolute;\n top: var(--zoom-indicator-top);\n left: var(--zoom-indicator-left);\n color: var(--text-color);\n background-color: var(--zoom-indicator-bg);\n padding: var(--zoom-indicator-padding);\n border-radius: var(--zoom-indicator-border-radius);\n font-size: var(--zoom-indicator-font-size);\n z-index: 10;\n min-width: 60px;\n text-align: center;\n box-shadow: 0 2px 8px var(--shadow-color);\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n /* \u4fe1\u606f\u680f\u6837\u5f0f */\n .images-viewer-image-info {\n position: absolute;\n top: var(--info-top);\n left: var(--info-left);\n color: var(--text-color);\n background-color: var(--info-bg-color);\n padding: var(--info-padding);\n border-radius: var(--info-border-radius);\n font-size: var(--info-font-size);\n z-index: 10;\n max-width: calc(100% - 40px);\n box-shadow: 0 4px 12px var(--shadow-color);\n display: none;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .images-viewer-image-info.visible {\n display: block;\n animation: imageViewerFadeIn 0.3s ease;\n }\n\n .images-viewer-image-info p {\n margin: 4px 0;\n line-height: 1.4;\n }\n\n .images-viewer-image-info .info-label {\n color: #ddd;\n margin-right: 5px;\n }\n\n .images-viewer-shortcuts-title {\n margin-top: 10px;\n padding-top: 10px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n font-weight: bold;\n margin-bottom: 5px;\n }\n\n @keyframes imageViewerFadeIn {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* \u56fe\u7247\u8ba1\u6570\u5668 */\n .images-viewer-image-counter {\n position: absolute;\n top: 20px;\n left: 50%;\n transform: translateX(-50%);\n color: var(--text-color);\n background-color: var(--info-bg-color);\n padding: 6px 12px;\n border-radius: 18px;\n font-size: 14px;\n z-index: 10;\n box-shadow: 0 2px 8px var(--shadow-color);\n }\n\n /* \u56fe\u7247\u6807\u9898\u663e\u793a */\n .images-viewer-image-title {\n position: absolute;\n top: 60px;\n left: 50%;\n transform: translateX(-50%);\n color: var(--text-color);\n padding: 4px 12px;\n border-radius: 18px;\n font-size: 12px;\n z-index: 10;\n max-width: 80%;\n text-align: center;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* \u5bfc\u822a\u6309\u94ae */\n .images-viewer-nav-buttons {\n position: absolute;\n top: 50%;\n left: 0;\n right: 0;\n transform: translateY(-50%);\n display: flex;\n justify-content: space-between;\n pointer-events: none;\n z-index: 5;\n padding: 0 10px;\n }\n\n .images-viewer-nav-btn {\n width: var(--nav-button-size);\n height: var(--nav-button-size);\n border-radius: var(--nav-button-border-radius);\n background-color: var(--nav-button-bg-color);\n color: var(--text-color);\n border: none;\n font-size: var(--nav-button-font-size);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n pointer-events: auto;\n opacity: 0.9;\n box-shadow: 0 2px 8px var(--shadow-color);\n z-index: 6;\n }\n\n .images-viewer-nav-btn:hover {\n background-color: var(--nav-button-hover-bg);\n opacity: 1;\n transform: scale(1.1);\n }\n\n .images-viewer-nav-btn:disabled {\n opacity: 0.3;\n cursor: not-allowed;\n transform: none;\n }\n\n /* \u5de5\u5177\u680f\u6837\u5f0f */\n .images-viewer-toolbar {\n position: absolute;\n bottom: var(--toolbar-bottom);\n left: 50%;\n transform: translateX(-50%);\n background-color: var(--toolbar-bg-color);\n padding: var(--toolbar-padding);\n border-radius: var(--toolbar-border-radius);\n display: flex;\n gap: 2px;\n z-index: 10;\n box-shadow: 0 6px 25px var(--shadow-color);\n max-width: calc(100% - 40px);\n overflow-x: auto;\n overflow-y: hidden;\n border: 1px solid rgba(255, 255, 255, 0.1);\n scrollbar-width: none;\n -ms-overflow-style: none;\n -webkit-overflow-scrolling: touch;\n }\n\n .images-viewer-toolbar::-webkit-scrollbar {\n display: none;\n }\n\n .images-viewer-tool-btn {\n width: var(--button-size);\n height: var(--button-size);\n background-color: transparent;\n border: none;\n color: var(--text-color);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: var(--button-font-size);\n transition: all 0.2s;\n flex-shrink: 0;\n position: relative;\n border-radius: var(--button-border-radius);\n margin: 0 2px;\n z-index: 11;\n line-height: 1;\n }\n\n .images-viewer-tool-btn:hover {\n background-color: var(--button-hover-bg);\n transform: translateY(-2px);\n box-shadow: 0 4px 10px var(--shadow-color);\n }\n\n .images-viewer-tool-btn:active {\n background-color: rgba(255, 255, 255, 0.3);\n transform: translateY(0);\n }\n\n .images-viewer-tool-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n /* \u7f29\u7565\u56fe\u5bb9\u5668 */\n .images-viewer-thumbnails-container {\n position: absolute;\n bottom: 90px;\n left: 50%;\n max-width: var(--thumb-max-width);\n transform: translateX(-50%);\n padding: 10px var(--thumb-padding);\n background-color: var(--toolbar-bg-color);\n border-radius: 12px;\n display: flex;\n gap: var(--thumb-gap);\n overflow-x: auto;\n scrollbar-width: none;\n -ms-overflow-style: none;\n z-index: 10;\n box-shadow: 0 3px 15px var(--shadow-color);\n -webkit-overflow-scrolling: touch;\n border: 1px solid rgba(255, 255, 255, 0.1);\n }\n\n .images-viewer-thumbnails-container::-webkit-scrollbar {\n display: none;\n }\n\n .images-viewer-thumbnail-item {\n width: var(--thumb-item-width);\n height: var(--thumb-item-height);\n border: 2px solid transparent;\n border-radius: 6px;\n overflow: hidden;\n cursor: pointer;\n flex-shrink: 0;\n transition: all 0.2s;\n z-index: 11;\n position: relative;\n opacity: 0.6;\n }\n\n .images-viewer-thumbnail-item img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .images-viewer-thumbnail-item.active {\n border-color: var(--active-color);\n transform: scale(1.2);\n opacity: 1;\n box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);\n }\n\n /* \u7f29\u7565\u56fe\u52a0\u8f7d\u72b6\u6001 */\n .images-viewer-thumbnail-loading {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.3);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1;\n }\n\n .loading-spinner {\n width: 20px;\n height: 20px;\n border: 2px solid rgba(255, 255, 255, 0.3);\n border-top: 2px solid var(--active-color);\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n .loading-error {\n color: #ff6b6b;\n font-size: 10px;\n text-align: center;\n padding: 2px;\n }\n\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .images-viewer-thumbnail-item:hover {\n transform: scale(1.2);\n opacity: 0.9;\n }\n `,document.head.appendChild(t)}createOptimizedElements(){this.container=document.createElement("div"),this.container.className="images-viewer-container",document.body.appendChild(this.container),this.imageContainer=document.createElement("div"),this.imageContainer.className="images-viewer-image-container",this.container.appendChild(this.imageContainer),this.image=document.createElement("img"),this.image.className="images-viewer-image",this.image.crossOrigin="anonymous",this.image.alt=" ",this.imageContainer.appendChild(this.image),this.loading=document.createElement("div"),this.loading.className="images-viewer-loading",this.loading.innerHTML=`\n <div class="images-viewer-loading-spinner"></div>\n <div>${this.options.i18n.buttons.loading}</div>\n `,this.imageContainer.appendChild(this.loading),this.options.buttons.topClose&&(this.topCloseBtn=document.createElement("button"),this.topCloseBtn.className="images-viewer-top-close-btn",this.topCloseBtn.textContent="\xd7",this.topCloseBtn.title=this.options.i18n.buttons.close,this.container.appendChild(this.topCloseBtn)),this.zoomIndicator=document.createElement("div"),this.zoomIndicator.className="images-viewer-zoom-indicator",this.zoomIndicator.textContent="100%",this.container.appendChild(this.zoomIndicator),this.options.buttons.info&&(this.imageInfoPanel=document.createElement("div"),this.imageInfoPanel.className="images-viewer-image-info "+(this.imageInfoVisible?"visible":""),this.container.appendChild(this.imageInfoPanel)),this.images.length>1&&(this.counter=document.createElement("div"),this.counter.className="images-viewer-image-counter",this.container.appendChild(this.counter)),this.imageTitle=document.createElement("div"),this.imageTitle.className="images-viewer-image-title",this.container.appendChild(this.imageTitle),this.images.length>1&&(this.options.buttons.prev||this.options.buttons.next)&&this.createNavButtons(),this.images.length>1&&this.options.buttons.thumbnails&&this.createThumbnails(),this.createToolbar()}createNavButtons(){const t=document.createElement("div");t.className="images-viewer-nav-buttons",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.options.buttons.prev&&(this.prevBtn=document.createElement("button"),this.prevBtn.className="images-viewer-nav-btn images-viewer-prev-btn",this.prevBtn.textContent="\u2190",this.prevBtn.title=this.options.i18n.buttons.prev,this.addEvent(this.prevBtn,"click",t=>{t.stopPropagation(),this.prev()}),t.appendChild(this.prevBtn)),this.options.buttons.next&&(this.nextBtn=document.createElement("button"),this.nextBtn.className="images-viewer-nav-btn images-viewer-next-btn",this.nextBtn.textContent="\u2192",this.nextBtn.title=this.options.i18n.buttons.next,this.addEvent(this.nextBtn,"click",t=>{t.stopPropagation(),this.next()}),t.appendChild(this.nextBtn)),this.container.appendChild(t)}createToolbar(){const t=document.createElement("div");t.className="images-viewer-toolbar",this.addEvent(t,"click",t=>{t.stopPropagation()}),this.addEvent(t,"wheel",i=>{i.preventDefault(),i.stopPropagation(),t.scrollLeft+=i.deltaY}),this.images.length>1&&(this.options.buttons.prev&&(this.toolbarPrevBtn=this.createToolButton("\u2190",()=>this.prev()),t.appendChild(this.toolbarPrevBtn)),this.options.buttons.next&&(this.toolbarNextBtn=this.createToolButton("\u2192",()=>this.next()),t.appendChild(this.toolbarNextBtn))),this.options.buttons.zoomOut&&(this.zoomOutBtn=this.createToolButton("\u2212",()=>this.zoom(-.1)),t.appendChild(this.zoomOutBtn)),this.options.buttons.zoomIn&&(this.zoomInBtn=this.createToolButton("+",()=>this.zoom(.1)),t.appendChild(this.zoomInBtn)),this.options.buttons.rotateLeft&&(this.rotateLeftBtn=this.createToolButton("\u21ba",()=>this.rotate(-90)),t.appendChild(this.rotateLeftBtn)),this.options.buttons.rotateRight&&(this.rotateRightBtn=this.createToolButton("\u21bb",()=>this.rotate(90)),t.appendChild(this.rotateRightBtn)),this.options.buttons.reset&&(this.resetBtn=this.createToolButton("\u27f3",()=>this.reset()),t.appendChild(this.resetBtn)),this.options.buttons.originalSize&&(this.originalSizeBtn=this.createToolButton("1:1",()=>this.showOriginalSize()),t.appendChild(this.originalSizeBtn)),this.options.buttons.info&&(this.infoBtn=this.createToolButton("\u24d8",()=>this.toggleImageInfo()),t.appendChild(this.infoBtn)),this.options.buttons.download&&(this.downloadBtn=this.createToolButton("\u21a1",()=>this.downloadImage()),t.appendChild(this.downloadBtn)),this.options.buttons.fullscreen&&(this.fullscreenBtn=this.createToolButton("\u26f6",()=>this.toggleFullscreen()),t.appendChild(this.fullscreenBtn)),this.options.buttons.close&&(this.closeBtn=this.createToolButton("\xd7",()=>this.close()),t.appendChild(this.closeBtn)),this.options.customButtons&&this.options.customButtons.forEach(i=>{t.appendChild(this.createToolButton(i[0],i[1]))}),this.container.appendChild(t)}createToolButton(t,i){const s=document.createElement("button");s.className="images-viewer-tool-btn";const n=document.createElement("span");return n.textContent=t,s.appendChild(n),this.addEvent(s,"click",t=>{t.stopPropagation(),i()}),s}createThumbnails(){const t=document.createElement("div");t.className="images-viewer-thumbnails-container",this.addEvent(t,"click",t=>{t.stopPropagation();const i=t.target.closest(".images-viewer-thumbnail-item");if(i){const t=parseInt(i.dataset.index);if(!isNaN(t)&&t!==this.currentIndex){const i=this.currentIndex;this.currentIndex=t,this.loadCurrentImage(),this.updateThumbnails(),this.options.onChange&&this.options.onChange({index:this.currentIndex,oldIndex:i,direction:this.currentIndex>i?"next":"prev",data:this.images[this.currentIndex],img:this.image,dom:this.container})}}}),this.addEvent(t,"wheel",i=>{i.preventDefault(),i.stopPropagation(),t.scrollLeft+=i.deltaY});const i=parseInt(this.options.theme.thumbItemWidth),s=parseInt(this.options.theme.thumbGap),n=parseInt(this.options.theme.thumbPadding),e=this.options.preloadCount,o=window.innerWidth-2*n,h=Math.ceil(o/(i+s)),a=Math.min(h+e,this.images.length);for(let i=0;i<a;i++)this.createThumbnailItem(t,i);let r=a;this.addEvent(t,"scroll",()=>{const i=t.scrollLeft,s=t.clientWidth;if(i>t.scrollWidth-s-s&&r<this.images.length){const i=Math.min(h,this.images.length-r);for(let s=r;s<r+i;s++)this.createThumbnailItem(t,s);r+=i}}),this.container.appendChild(t),this.thumbContainer=t}createThumbnailItem(t,i){const s=this.getThumbnailUrl(i),n=this.getImageUrl(i),e=document.createElement("div");e.className="images-viewer-thumbnail-item "+(i===this.currentIndex?"active":""),e.dataset.index=i;const o=new Image;o.crossOrigin="anonymous",o.src="";const h=document.createElement("div");h.className="images-viewer-thumbnail-loading",h.innerHTML='<div class="loading-spinner"></div>',e.appendChild(h),this.loadedThumbnails.has(s)?(o.src=this.loadedThumbnails.get(s).src,h.remove()):this.loadingThumbnails.has(s)?(h.style.display="flex",o.onload=()=>{h.remove(),o.onload=null,o.onerror=null},o.onerror=()=>{h.remove(),this.failedThumbnails.add(s),o.onload=null,o.onerror=null,this.options.onThumbnailError&&this.options.onThumbnailError({data:this.images[i],index:i,url:s,img:o})},o.src=s):this.failedThumbnails.has(s)&&!this.options.retryOnError?h.remove():this.loadedImages.has(n)?(o.src=this.loadedImages.get(n).src,h.remove()):(this.loadingThumbnails.set(s,o),h.style.display="flex",o.onload=()=>{this.loadedThumbnails.set(s,o),this.loadingThumbnails.delete(s),this.failedThumbnails.delete(s),h.remove(),o.onload=null,o.onerror=null},o.onerror=()=>{this.loadingThumbnails.delete(s),this.failedThumbnails.add(s),h.remove(),o.onload=null,o.onerror=null,this.options.onThumbnailError&&this.options.onThumbnailError({data:this.images[i],index:i,url:s,img:o})},o.src=s),e.appendChild(o),t.appendChild(e)}updateThumbnailDisplay(t){if(!this.thumbContainer)return;const i=this.thumbContainer.querySelector(`[data-index="${t}"]`);if(i){const s=this.getThumbnailUrl(t),n=i.querySelector("img");if(n&&this.loadedThumbnails.has(s)){const t=this.loadedThumbnails.get(s);n.src=t.src,n.style.display="block";const e=i.querySelector(".images-viewer-thumbnail-loading");e&&e.remove()}}}updateImageTransform(){const t=`\n translate(${this.translateX}px, ${this.translateY}px)\n scale(${this.scale})\n rotate(${this.rotation}deg)\n `;this.image.style.transform=t}fitImageToScreen(t,i){this.scale=1,this.translateX=0,this.translateY=0;const s=this.imageContainer.clientWidth,n=this.imageContainer.clientHeight,e=this.rotation%360;let o=t,h=i;if(90!==e&&270!==e||(o=i,h=t),o>s||h>n){const t=s/o,i=n/h;this.scale=Math.min(t,i)}this.scale=Math.max(.1,this.scale),this.updateImageTransform(),this.updateZoomIndicator()}parseImageOptions(t){this.images=[],"string"==typeof t?this.images=[t]:Array.isArray(t)?this.images=t.filter(t=>""!==t):"object"==typeof t&&t.images&&Array.isArray(t.images)&&(this.images=t.images.filter(t=>""!==t))}getImageUrl(t){const i=this.images[t];if("string"==typeof i)return i;if("object"==typeof i){const s=this.options.props.url;if("function"==typeof s)return s(i,t);if("string"==typeof s&&i[s])return i[s]}return""}getThumbnailUrl(t){const i=this.images[t];if("object"==typeof i){const s=this.options.props.thumbnail;if("function"==typeof s)return s(i,t);if("string"==typeof s&&i[s])return i[s]}return this.getImageUrl(t)}getImageTitle(t){const i=this.images[t];if("object"==typeof i){const s=this.options.props.title;if("function"==typeof s)return s(i,t);if("string"==typeof s&&i[s])return i[s]}return""}preloadImages(){const t=this.options.preloadCount;if(t<=0)return;const i=Math.max(0,this.currentIndex-t),s=Math.min(this.images.length-1,this.currentIndex+t);for(let t=i;t<=s;t++)this.loadImageAtIndex(t)}addToCache(t,i){this.loadedImages.set(t,i);const s=this.options.maxCacheSize;if(s>0&&this.loadedImages.size>s){const t=this.loadedImages.keys().next().value;this.loadedImages.delete(t)}}loadImageAtIndex(t){const i=this.getImageUrl(t);if(!i||this.loadedImages.has(i)||this.loadingImages.has(i)||!this.options.retryOnError&&this.failedImages.has(i))return;const s=new Image;s.onload=()=>{this.addToCache(i,s),this.loadingImages.delete(i),this.failedImages.delete(i),this.imageMetadata[t]={name:this.extractFileName(i),width:s.naturalWidth||s.width,height:s.naturalHeight||s.height},s.onload=null,s.onerror=null},s.onerror=()=>{this.loadingImages.delete(i),this.failedImages.add(i),s.onload=null,s.onerror=null;const n=this.getImageUrl(this.currentIndex);i===n&&(this.hideLoading(),this.image.classList.add("loaded"),this.options.onImageError&&this.options.onImageError({data:this.images[t],index:t,url:i,img:this.image}))},s.crossOrigin="anonymous",s.src=i,this.loadingImages.set(i,s)}extractFileName(t){try{const i=new URL(t).pathname.split("/");let s=i[i.length-1];const n=s.indexOf("?");return n>-1&&(s=s.substring(0,n)),s||"unknown-image"}catch(t){return"unknown-image"}}loadCurrentImage(t){if(void 0!==t&&(this.currentIndex=t),this.hasPreviousState=!1,this.isToggledState=!1,this.images.length>1&&this.counter){let t=null;this.options.onCounter&&(t=this.options.onCounter({data:this.images[this.currentIndex],index:this.currentIndex,currentPage:this.currentIndex+1,totalPages:this.images.length,scale:this.scale,rotation:this.rotation})),t?this.counter.innerHTML=t:this.counter.textContent=`${this.currentIndex+1} / ${this.images.length}`}const i=this.getImageTitle(this.currentIndex);this.imageTitle&&i?(this.imageTitle.innerHTML=i,this.imageTitle.style.display="block"):this.imageTitle.style.display="none",this.updateNavButtons(),this.updateThumbnails(),this.scale=1,this.rotation=0,this.translateX=0,this.translateY=0;const s=this.getImageUrl(this.currentIndex);this.loadedImages.has(s)||this.loadingImages.has(s)||this.loadImageAtIndex(this.currentIndex),this.preloadAdjacentImages(),this.isImageLoaded()}preloadAdjacentImages(){const t=Math.max(0,this.currentIndex-2),i=Math.min(this.images.length-1,this.currentIndex+2);for(let s=t;s<=i;s++)this.loadThumbnailAtIndex(s);for(let s=t;s<=i;s++)this.loadImageAtIndex(s)}loadThumbnailAtIndex(t){const i=this.getThumbnailUrl(t);if(!i||this.loadedThumbnails.has(i)||this.loadingThumbnails.has(i)||!this.options.retryOnError&&this.failedThumbnails.has(i))return;const s=new Image;s.onload=()=>{this.loadedThumbnails.set(i,s),this.loadingThumbnails.delete(i),this.failedThumbnails.delete(i),this.updateThumbnailDisplay(t),s.onload=null,s.onerror=null},s.onerror=()=>{this.loadingThumbnails.delete(i),this.failedThumbnails.add(i),s.onload=null,s.onerror=null;let n=null;if(this.thumbContainer){const i=this.thumbContainer.querySelector(`[data-index="${t}"]`);if(i){n=i.querySelector("img");const t=i.querySelector(".images-viewer-thumbnail-loading");t&&t.remove()}}this.options.onThumbnailError&&this.options.onThumbnailError({data:this.images[t],index:t,url:i,img:n})},s.crossOrigin="anonymous",s.src=i,this.loadingThumbnails.set(i,s)}isImageLoaded(){const t=this.getImageUrl(this.currentIndex),i=this.loadedImages.has(t),s=this.loadingImages.has(t),n=this.failedImages.has(t);if(this.showLoading(),this.image.classList.remove("loaded"),this.image.src="",i){const i=this.loadedImages.get(t),s=document.createElement("canvas"),n=s.getContext("2d");s.width=i.naturalWidth||i.width,s.height=i.naturalHeight||i.height,n.drawImage(i,0,0),s.toBlob(t=>{if(t){const i=URL.createObjectURL(t);this.image.onload=()=>{this.image.onload=null},this.image.src=i,this.t&&(URL.revokeObjectURL(this.t),this.t=null),this.t=i}});const e=this.imageMetadata[this.currentIndex];return e&&(this.fitImageToScreen(e.width,e.height),this.updateImageInfo()),this.image.classList.add("loaded"),void this.hideLoading()}if(n&&!this.options.retryOnError)return this.hideLoading(),this.image.classList.add("loaded"),void(this.options.onImageError&&this.options.onImageError({data:this.images[this.currentIndex],index:this.currentIndex,url:t,img:this.image}));if(s){const i=()=>{const s=this.getImageUrl(this.currentIndex);this.loadedImages.has(t)?t===s&&this.isImageLoaded():this.loadingImages.has(t)?setTimeout(i,100):(this.hideLoading(),t===s&&(this.image.classList.add("loaded"),this.options.onImageError&&this.options.onImageError({data:this.images[this.currentIndex],index:this.currentIndex,url:t,img:this.image})))};return void setTimeout(i,100)}const e=new Image;e.onload=()=>{this.addToCache(t,e),this.loadingImages.delete(t),this.imageMetadata[this.currentIndex]={name:this.extractFileName(t),width:e.naturalWidth||e.width,height:e.naturalHeight||e.height},this.isImageLoaded()},e.onerror=()=>{this.loadingImages.delete(t),this.hideLoading();const i=this.getImageUrl(this.currentIndex);t===i&&(this.image.classList.add("loaded"),this.options.onImageError&&this.options.onImageError({data:this.images[this.currentIndex],index:this.currentIndex,url:t,img:this.image}))},e.crossOrigin="anonymous",e.src=t,this.loadingImages.set(t,e)}updateZoomIndicator(){const t=Math.round(100*this.scale);if(this.options.onZoomIndicator){const i=this.options.onZoomIndicator({data:this.images[this.currentIndex],index:this.currentIndex,scale:this.scale,percentage:t,rotation:this.rotation});if(i)return void(this.zoomIndicator.innerHTML=i)}this.zoomIndicator.textContent=`${t}%`}updateImageInfo(){if(!this.options.buttons.info||!this.imageInfoPanel)return;const t=this.imageMetadata[this.currentIndex];if(!t)return;const i=this.options.i18n;let s="";this.options.onInfo&&(s=this.options.onInfo({data:this.images[this.currentIndex],index:this.currentIndex,metadata:t,scale:this.scale,rotation:this.rotation}),s)||(this.options.imageInfo.showName&&(s+=`<p><span class="info-label">${i.info.name}</span> ${t.name}</p>`),this.options.imageInfo.showDimensions&&(s+=`<p><span class="info-label">${i.info.dimensions}</span> ${t.width} \xd7 ${t.height}</p>`),s+=`\n <div class="images-viewer-shortcuts-title">${i.info.shortcuts}</div>\n <p><span class="info-label">${i.info.zoomIn}</span> \u2191 +</p>\n <p><span class="info-label">${i.info.zoomOut}</span> \u2193 -</p>\n <p><span class="info-label">${i.info.prev}</span> \u2190</p>\n <p><span class="info-label">${i.info.next}</span> \u2192</p>\n <p><span class="info-label">${i.info.reset}</span> 0</p>\n <p><span class="info-label">${i.info.fullscreen}</span> F</p>\n <p><span class="info-label">${i.info.info}</span> I</p>\n <p><span class="info-label">${i.info.close}</span> ESC</p>\n `),this.imageInfoPanel.innerHTML=s}toggleImageInfo(){this.options.buttons.info&&this.imageInfoPanel&&(this.imageInfoVisible=!this.imageInfoVisible,this.imageInfoVisible?this.imageInfoPanel.classList.add("visible"):this.imageInfoPanel.classList.remove("visible"))}updateNavButtons(){if(this.images.length<=1)return;const t=!!this.options.loop||this.currentIndex>0,i=!!this.options.loop||this.currentIndex<this.images.length-1;this.prevBtn&&(this.prevBtn.disabled=!t),this.nextBtn&&(this.nextBtn.disabled=!i),this.toolbarPrevBtn&&(this.toolbarPrevBtn.disabled=!t),this.toolbarNextBtn&&(this.toolbarNextBtn.disabled=!i)}updateThumbnails(){if(this.images.length<=1)return;document.querySelectorAll(".images-viewer-thumbnail-item").forEach(t=>{t.classList.remove("active")});const t=document.querySelector(`.images-viewer-thumbnail-item[data-index="${this.currentIndex}"]`);t&&(t.classList.add("active"),t.scrollIntoView({behavior:"smooth",block:"nearest",inline:"center"}))}showLoading(){this.loading&&this.loading.classList.add("active")}hideLoading(){this.loading&&this.loading.classList.remove("active")}prev(){if(this.images.length<=1)return;let t=this.currentIndex-1;t<0&&(t=this.options.loop?this.images.length-1:0);const i=this.currentIndex;t!==this.currentIndex&&(this.currentIndex=t,this.loadCurrentImage()),this.options.onChange&&this.options.onChange({oldIndex:i,index:this.currentIndex,data:this.images[this.currentIndex],direction:"prev",img:this.image,dom:this.container})}next(){if(this.images.length<=1)return;let t=this.currentIndex+1;t>=this.images.length&&(t=this.options.loop?0:this.images.length-1),t!==this.currentIndex&&(this.currentIndex=t,this.loadCurrentImage());const i=this.currentIndex;this.options.onChange&&this.options.onChange({oldIndex:i,index:this.currentIndex,data:this.images[this.currentIndex],index:this.currentIndex,direction:"next",img:this.image,dom:this.container})}bindEvents(){this.topCloseBtn&&this.addEvent(this.topCloseBtn,"click",()=>this.close()),this.options.closeOnMaskClick&&this.addEvent(this.imageContainer,"click",t=>{t.target==this.imageContainer&&this.close()}),this.addEvent(document,"keydown",t=>this.handleKeydown(t));const t=function(t){let i,s=0;return function(...n){const e=Date.now(),o=e-s;o>300?(t.apply(this,n),s=e):(clearTimeout(i),i=setTimeout(()=>{t.apply(this,n),s=e},300-o))}}(()=>{this.handleResize()});this.addEvent(window,"resize",t),this.bindDragEvents(),this.bindTouchEvents()}addEvent(t,i,s,n){t.addEventListener(i,s,n);const e=`${i}-${Date.now()}-${Math.random()}`;this.eventListeners.set(e,{element:t,event:i,handler:s})}removeAllEvents(){this.eventListeners.forEach(({element:t,event:i,handler:s})=>{t.removeEventListener(i,s)}),this.eventListeners.clear()}rotatePoint(t,i,s){const n=s*Math.PI,e=Math.cos(n),o=Math.sin(n);return{x:t*e-i*o,y:t*o+i*e}}bindDragEvents(){this.addEvent(this.image,"mousedown",t=>{0===t.button&&(this.isDragging=!0,this.image.classList.add("dragging"),this.startX=t.clientX,this.startY=t.clientY,this.startTranslateX=this.translateX,this.startTranslateY=this.translateY,t.preventDefault())}),this.addEvent(document,"mousemove",t=>{if(!this.isDragging)return;const i=t.clientX-this.startX,s=t.clientY-this.startY,n=this.rotatePoint(i,s,-this.rotation);this.translateX=this.startTranslateX+n.x,this.translateY=this.startTranslateY+n.y,this.updateImageTransform(),this.options.onDrag&&this.options.onDrag({data:his.images[this.currentIndex],index:this.currentIndex,translateX:this.translateX,translateY:this.translateY}),t.preventDefault()}),this.addEvent(document,"mouseup",()=>{this.isDragging&&(this.isDragging=!1,this.image.classList.remove("dragging"))}),this.addEvent(document,"mouseleave",()=>{this.isDragging&&(this.isDragging=!1,this.image.classList.remove("dragging"))}),this.addEvent(this.imageContainer,"wheel",t=>{t.preventDefault();const i=this.imageContainer.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top,e=t.deltaY>0?-.05:.05;this.zoomAtPoint(e,s,n)}),this.addEvent(this.image,"dblclick",t=>{t.preventDefault();const i=this.imageContainer.getBoundingClientRect(),s=t.clientX-i.left,n=t.clientY-i.top;if(Math.abs(this.scale-1)<.01)if(this.hasPreviousState)this.scale=this.lastScale,this.translateX=this.lastTranslateX,this.translateY=this.lastTranslateY,this.hasPreviousState=!1;else{this.lastScale=this.scale,this.lastTranslateX=this.translateX,this.lastTranslateY=this.translateY,this.hasPreviousState=!0;const t=1.5,i=t/this.scale,e=this.imageContainer.clientWidth,o=this.imageContainer.clientHeight;this.translateX=this.translateX*i+s-e/2-i*(s-e/2),this.translateY=this.translateY*i+n-o/2-i*(n-o/2),this.scale=t}else{this.lastScale=this.scale,this.lastTranslateX=this.translateX,this.lastTranslateY=this.translateY,this.hasPreviousState=!0;const t=1,i=t/this.scale,e=this.imageContainer.clientWidth,o=this.imageContainer.clientHeight;this.translateX=this.translateX*i+s-e/2-i*(s-e/2),this.translateY=this.translateY*i+n-o/2-i*(n-o/2),this.scale=t}this.updateImageTransform(),this.updateZoomIndicator()})}bindTouchEvents(){this.addEvent(this.image,"touchstart",t=>{if(this.touchState.lastTouchTime=Date.now(),1===t.touches.length)this.touchState.isPinching||(this.touchState.isDragging=!0,this.touchState.startX=t.touches[0].clientX,this.touchState.startY=t.touches[0].clientY,this.touchState.startTranslateX=this.translateX,this.touchState.startTranslateY=this.translateY,this.image.classList.add("dragging"));else if(2===t.touches.length){this.touchState.isPinching=!0,this.touchState.isDragging=!1,this.image.classList.remove("dragging");const i=t.touches[0],s=t.touches[1];this.touchState.initialDistance=this.getDistance(i,s),this.touchState.initialScale=this.scale,this.touchState.initialTranslateX=this.translateX,this.touchState.initialTranslateY=this.translateY,this.touchState.centerX=(i.clientX+s.clientX)/2,this.touchState.centerY=(i.clientY+s.clientY)/2;const n=this.imageContainer.getBoundingClientRect(),e=this.touchState.centerX-n.left,o=this.touchState.centerY-n.top;this.calculateRelativeCenter(e,o),this.touchState.movementCount=0,this.touchState.scaleRatio=1}t.preventDefault()}),this.addEvent(this.image,"touchmove",t=>{if(!(Date.now()-this.touchState.lastTouchTime<16)){if(this.touchState.lastTouchTime=Date.now(),1===t.touches.length&&this.touchState.isDragging&&!this.touchState.isPinching){const i=t.touches[0].clientX-this.touchState.startX,s=t.touches[0].clientY-this.touchState.startY,n=this.rotatePoint(i,s,-this.rotation);this.touchState.movementCount++,(this.touchState.movementCount>this.touchState.stabilizationThreshold||Math.abs(i)>5||Math.abs(s)>5)&&(this.translateX=this.touchState.startTranslateX+n.x,this.translateY=this.touchState.startTranslateY+n.y,this.updateImageTransform(),this.options.onDrag&&this.options.onDrag({data:this.images[this.currentIndex],index:this.currentIndex,translateX:this.translateX,translateY:this.translateY}))}else if(2===t.touches.length&&this.touchState.isPinching){const i=t.touches[0],s=t.touches[1],n=this.getDistance(i,s);this.touchState.scaleRatio=n/this.touchState.initialDistance;const e=this.touchState.initialScale*this.touchState.scaleRatio,o=this.options.minScale,h=this.options.maxScale,a=Math.max(o,Math.min(h,e));if(Math.abs(a-this.scale)>this.touchState.minScaleChange){const t=a/this.touchState.initialScale,i=this.imageContainer.getBoundingClientRect(),s=i.width,n=i.height;this.translateX=this.touchState.initialTranslateX*t+this.touchState.centerX-s/2-t*(this.touchState.centerX-s/2),this.translateY=this.touchState.initialTranslateY*t+this.touchState.centerY-n/2-t*(this.touchState.centerY-n/2),this.scale=a,this.updateImageTransform(),this.updateZoomIndicator(),this.options.onZoom&&this.options.onZoom({data:this.images[this.currentIndex],index:this.currentIndex,scale:this.scale,oldScale:this.touchState.initialScale})}}t.preventDefault()}}),this.addEvent(this.image,"touchend",t=>{0===t.touches.length?(this.touchState.isDragging=!1,this.touchState.isPinching=!1,this.image.classList.remove("dragging")):1===t.touches.length&&this.touchState.isPinching&&(this.touchState.isPinching=!1,this.touchState.isDragging=!0,this.touchState.startX=t.touches[0].clientX,this.touchState.startY=t.touches[0].clientY,this.touchState.startTranslateX=this.translateX,this.touchState.startTranslateY=this.translateY,this.image.classList.add("dragging")),t.preventDefault()}),this.addEvent(this.image,"touchcancel",()=>{this.touchState.isDragging=!1,this.touchState.isPinching=!1,this.image.classList.remove("dragging")})}getDistance(t,i){const s=t.clientX-i.clientX,n=t.clientY-i.clientY;return Math.sqrt(s*s+n*n)}calculateRelativeCenter(t,i){if(!this.imageMetadata[this.currentIndex])return;const s=this.imageContainer.clientWidth,n=this.imageContainer.clientHeight/2,e=t-s/2-this.translateX,o=i-n-this.translateY;this.touchState.relativeCenterX=e/this.scale,this.touchState.relativeCenterY=o/this.scale}zoomAtPoint(t,i,s){const n=this.scale,e=this.options.maxScale,o=this.options.minScale,h=Math.max(o,Math.min(e,this.scale+t));if(h===this.scale)return;const a=h/n,r=this.imageContainer.clientWidth,l=this.imageContainer.clientHeight;this.translateX=this.translateX*a+i-r/2-a*(i-r/2),this.translateY=this.translateY*a+s-l/2-a*(s-l/2),this.scale=h,this.updateImageTransform(),this.updateZoomIndicator(),this.options.onZoom&&this.options.onZoom({data:this.images[this.currentIndex],index:this.currentIndex,scale:this.scale,oldScale:n})}zoom(t){const i=this.imageContainer.clientWidth,s=this.imageContainer.clientHeight;this.zoomAtPoint(t,i/2,s/2)}rotate(t){const i=this.rotation,s=i+t;if(i===s)return;const n=this.imageContainer.getBoundingClientRect(),e=n.width/2,o=n.height/2,h=e+this.translateX,a=o+this.translateY;this.rotation=s,this.translateX=0,this.translateY=0;const r=this.imageMetadata[this.currentIndex];if(r){const t=this.calculateBoundingBox(r.width,r.height,s),i=t.width*this.scale,n=t.height*this.scale;this.translateX=(h-e)*(i/(i-this.translateX)),this.translateY=(a-o)*(n/(n-this.translateY))}this.updateImageTransform(),this.updateZoomIndicator(),this.options.onRotate&&this.options.onRotate({data:this.images[this.currentIndex],index:this.currentIndex,rotation:this.rotation,oldRotation:i})}calculateBoundingBox(t,i,s){const n=s*Math.PI/180,e=Math.abs(Math.cos(n)),o=Math.abs(Math.sin(n));return{width:t*e+i*o,height:t*o+i*e}}reset(){this.rotation=0;const t=this.imageMetadata[this.currentIndex];t&&this.fitImageToScreen(t.width,t.height)}showOriginalSize(){this.rotation=0,this.scale=1,this.translateX=0,this.translateY=0,this.updateImageTransform(),this.updateZoomIndicator()}downloadImage(){const t=this.getImageUrl(this.currentIndex),i=this.imageMetadata[this.currentIndex];if(this.loadedImages.has(t)){const s=this.loadedImages.get(t);this.downloadFromImage(s,i)}}downloadFromImage(t,i){try{const s=document.createElement("canvas");s.width=t.naturalWidth||t.width,s.height=t.naturalHeight||t.height,s.getContext("2d").drawImage(t,0,0);const n=s.toDataURL("image/jpeg");this.downloadedImage(n,i)}catch(t){const s=this.getImageUrl(this.currentIndex);this.downloadedImage(s,i)}}downloadedImage(t,i){try{const s=document.createElement("a");s.href=t,s.download=i?i.name:"image.jpg",document.body.appendChild(s),s.click(),document.body.removeChild(s)}catch(t){}}toggleFullscreen(){document.fullscreenElement?document.exitFullscreen&&(document.exitFullscreen(),this.isFullscreen=!1):(this.container.requestFullscreen().catch(t=>{}),this.isFullscreen=!0)}handleKeydown(t){if("block"===this.container.style.display){switch(t.key){case"Escape":this.close();break;case"ArrowLeft":this.prev();break;case"ArrowRight":this.next();break;case"ArrowUp":case"+":case"=":this.zoom(.1);break;case"ArrowDown":case"-":this.zoom(-.1);break;case"0":this.reset();break;case"f":case"F":this.toggleFullscreen();break;case"i":case"I":this.toggleImageInfo()}t.preventDefault()}}handleResize(){const t=this.imageMetadata[this.currentIndex];if(!t)return;const i=this.imageContainer.clientWidth,s=this.imageContainer.clientHeight,n=this.rotation%360;let e=t.width,o=t.height;90!==n&&270!==n||(e=t.height,o=t.width);const h=Math.min(i/e,s/o),a=e*this.scale,r=o*this.scale,l=a>i||r>s;let c=this.scale;c=l?Math.max(.1,h):h>=1?1:h,Math.abs(c-this.scale)>.01&&(this.scale=c,this.translateX=0,this.translateY=0,this.updateImageTransform(),this.updateZoomIndicator()),this.updateThumbnailsOnResize()}updateThumbnailsOnResize(){if(!this.thumbContainer)return;const t=parseInt(this.options.theme.thumbItemWidth),i=parseInt(this.options.theme.thumbGap),s=parseInt(this.options.theme.thumbPadding),n=this.options.preloadCount,e=window.innerWidth-2*s,o=Math.ceil(e/(t+i)),h=Math.min(o+n,this.images.length),a=this.thumbContainer.querySelectorAll(".images-viewer-thumbnail-item").length;if(a<h){const t=h-a;for(let i=a;i<a+t;i++)i<this.images.length&&this.createThumbnailItem(this.thumbContainer,i)}}onShow(){this.container.style.display="block",setTimeout(()=>{this.container.style.opacity="1",this.options.onShow&&this.options.onShow(this.container)},10)}close(){this.removeAllEvents(),this.cleanup(),this.container.style.opacity="0",setTimeout(()=>{this.container.style.display="none";const t=document.getElementById("images-viewer-styles");t&&t.remove(),this.container&&this.container.remove(),this.options.onClose&&this.options.onClose()},300)}cleanup(){for(const[t,i]of this.loadingImages.entries())i.onload=null,i.onerror=null,i.src="";this.loadedImages.clear(),this.loadingImages.clear(),this.imageMetadata=[],this.t&&(URL.revokeObjectURL(this.t),this.t=null)}}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=factory():"function"==typeof define&&define.amd?define(factory):(global="undefined"!=typeof globalThis?globalThis:global||self).ImagesViewer=factory();
|
package/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface RotateEventData {
|
|
|
27
27
|
/**
|
|
28
28
|
* 当前图片数据
|
|
29
29
|
*/
|
|
30
|
-
|
|
30
|
+
data: string | ImageObject;
|
|
31
31
|
/**
|
|
32
32
|
* 当前图片索引
|
|
33
33
|
*/
|
|
@@ -49,7 +49,7 @@ export interface DragEventData {
|
|
|
49
49
|
/**
|
|
50
50
|
* 当前图片数据
|
|
51
51
|
*/
|
|
52
|
-
|
|
52
|
+
data: string | ImageObject;
|
|
53
53
|
/**
|
|
54
54
|
* 当前图片索引
|
|
55
55
|
*/
|
|
@@ -71,7 +71,7 @@ export interface ZoomEventData {
|
|
|
71
71
|
/**
|
|
72
72
|
* 当前图片数据
|
|
73
73
|
*/
|
|
74
|
-
|
|
74
|
+
data: string | ImageObject;
|
|
75
75
|
/**
|
|
76
76
|
* 当前图片索引
|
|
77
77
|
*/
|
|
@@ -86,6 +86,36 @@ export interface ZoomEventData {
|
|
|
86
86
|
oldScale: number;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* 切换事件参数
|
|
91
|
+
*/
|
|
92
|
+
export interface ChangeEventData {
|
|
93
|
+
/**
|
|
94
|
+
* 当前图片索引
|
|
95
|
+
*/
|
|
96
|
+
index: number;
|
|
97
|
+
/**
|
|
98
|
+
* 旧的图片索引
|
|
99
|
+
*/
|
|
100
|
+
oldIndex: number;
|
|
101
|
+
/**
|
|
102
|
+
* 切换方向
|
|
103
|
+
*/
|
|
104
|
+
direction: 'next' | 'prev';
|
|
105
|
+
/**
|
|
106
|
+
* 当前图片数据
|
|
107
|
+
*/
|
|
108
|
+
data: string | ImageObject;
|
|
109
|
+
/**
|
|
110
|
+
* 当前图片 DOM 元素
|
|
111
|
+
*/
|
|
112
|
+
img: HTMLImageElement;
|
|
113
|
+
/**
|
|
114
|
+
* 查看器容器 DOM 元素
|
|
115
|
+
*/
|
|
116
|
+
dom: HTMLDivElement;
|
|
117
|
+
}
|
|
118
|
+
|
|
89
119
|
/**
|
|
90
120
|
* 信息栏自定义函数参数
|
|
91
121
|
*/
|
|
@@ -93,7 +123,7 @@ export interface InfoTextParams {
|
|
|
93
123
|
/**
|
|
94
124
|
* 当前图片数据
|
|
95
125
|
*/
|
|
96
|
-
|
|
126
|
+
data: string | ImageObject;
|
|
97
127
|
/**
|
|
98
128
|
* 当前图片索引
|
|
99
129
|
*/
|
|
@@ -123,7 +153,7 @@ export interface CounterParams {
|
|
|
123
153
|
/**
|
|
124
154
|
* 当前图片数据
|
|
125
155
|
*/
|
|
126
|
-
|
|
156
|
+
data: string | ImageObject;
|
|
127
157
|
/**
|
|
128
158
|
* 当前图片索引
|
|
129
159
|
*/
|
|
@@ -153,7 +183,7 @@ export interface ZoomIndicatorParams {
|
|
|
153
183
|
/**
|
|
154
184
|
* 当前图片数据
|
|
155
185
|
*/
|
|
156
|
-
|
|
186
|
+
data: string | ImageObject;
|
|
157
187
|
/**
|
|
158
188
|
* 当前图片索引
|
|
159
189
|
*/
|
|
@@ -172,6 +202,28 @@ export interface ZoomIndicatorParams {
|
|
|
172
202
|
rotation: number;
|
|
173
203
|
}
|
|
174
204
|
|
|
205
|
+
/**
|
|
206
|
+
* 错误回调参数
|
|
207
|
+
*/
|
|
208
|
+
export interface ErrorCallbackParams {
|
|
209
|
+
/**
|
|
210
|
+
* 当前图片数据
|
|
211
|
+
*/
|
|
212
|
+
data: string | ImageObject;
|
|
213
|
+
/**
|
|
214
|
+
* 当前图片索引
|
|
215
|
+
*/
|
|
216
|
+
index: number;
|
|
217
|
+
/**
|
|
218
|
+
* 加载失败的图片 URL
|
|
219
|
+
*/
|
|
220
|
+
url: string;
|
|
221
|
+
/**
|
|
222
|
+
* 对应的图片或缩略图 DOM 元素
|
|
223
|
+
*/
|
|
224
|
+
img: HTMLImageElement | null;
|
|
225
|
+
}
|
|
226
|
+
|
|
175
227
|
/**
|
|
176
228
|
* ImagesViewer 配置选项
|
|
177
229
|
*/
|
|
@@ -275,6 +327,79 @@ interface ImagesViewerOptions {
|
|
|
275
327
|
showDimensions?: boolean;
|
|
276
328
|
};
|
|
277
329
|
|
|
330
|
+
/**
|
|
331
|
+
* 图片信息显示配置
|
|
332
|
+
*/
|
|
333
|
+
props?: {
|
|
334
|
+
/** 图片 URL 的属性名或获取函数 */
|
|
335
|
+
url?: string | ((item: any, index: number) => string);
|
|
336
|
+
/** 图片标题的属性名或获取函数 */
|
|
337
|
+
title?: string | ((item: any, index: number) => string);
|
|
338
|
+
/** 缩略图 URL 的属性名或获取函数 */
|
|
339
|
+
thumbnail?: string | ((item: any, index: number) => string);
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* 是否在失败后重新请求
|
|
344
|
+
* @default false
|
|
345
|
+
*/
|
|
346
|
+
retryOnError?: boolean;
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 图片加载失败回调
|
|
350
|
+
*/
|
|
351
|
+
onImageError?: (data: ErrorCallbackParams) => void;
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 缩略图加载失败回调
|
|
355
|
+
*/
|
|
356
|
+
onThumbnailError?: (data: ErrorCallbackParams) => void;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* 查看器显示时的回调
|
|
360
|
+
*/
|
|
361
|
+
onShow?: (container: HTMLDivElement) => void;
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* 查看器关闭时的回调
|
|
365
|
+
*/
|
|
366
|
+
onClose?: () => void;
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* 图片切换时的回调
|
|
370
|
+
*/
|
|
371
|
+
onChange?: (data: ChangeEventData) => void;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* 图片拖动时的回调
|
|
375
|
+
*/
|
|
376
|
+
onDrag?: (data: DragEventData) => void;
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* 图片缩放时的回调
|
|
380
|
+
*/
|
|
381
|
+
onZoom?: (data: ZoomEventData) => void;
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* 图片旋转时的回调
|
|
385
|
+
*/
|
|
386
|
+
onRotate?: (data: RotateEventData) => void;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 自定义信息栏文本
|
|
390
|
+
*/
|
|
391
|
+
onInfo?: (data: InfoTextParams) => string | null | undefined;
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* 自定义页数显示
|
|
395
|
+
*/
|
|
396
|
+
onCounter?: (data: CounterParams) => string | null | undefined;
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* 自定义缩放指示器
|
|
400
|
+
*/
|
|
401
|
+
onZoomIndicator?: (data: ZoomIndicatorParams) => string | null | undefined;
|
|
402
|
+
|
|
278
403
|
/**
|
|
279
404
|
* 国际化配置
|
|
280
405
|
*/
|
|
@@ -319,30 +444,30 @@ interface ImagesViewerOptions {
|
|
|
319
444
|
* 主题配置
|
|
320
445
|
*/
|
|
321
446
|
theme?: {
|
|
322
|
-
|
|
447
|
+
/** 背景相关 */
|
|
323
448
|
viewerBgColor?: string;
|
|
324
449
|
|
|
325
|
-
|
|
450
|
+
/** 工具栏相关 */
|
|
326
451
|
toolbarBgColor?: string;
|
|
327
452
|
toolbarBorderRadius?: string;
|
|
328
453
|
toolbarPadding?: string;
|
|
329
454
|
toolbarBottom?: string;
|
|
330
455
|
|
|
331
|
-
|
|
456
|
+
/** 按钮相关 */
|
|
332
457
|
buttonBgColor?: string;
|
|
333
458
|
buttonHoverBg?: string;
|
|
334
459
|
buttonSize?: string;
|
|
335
460
|
buttonFontSize?: string;
|
|
336
461
|
buttonBorderRadius?: string;
|
|
337
462
|
|
|
338
|
-
|
|
463
|
+
/** 导航按钮相关 */
|
|
339
464
|
navButtonBgColor?: string;
|
|
340
465
|
navButtonHoverBg?: string;
|
|
341
466
|
navButtonSize?: string;
|
|
342
467
|
navButtonFontSize?: string;
|
|
343
468
|
navButtonBorderRadius?: string;
|
|
344
469
|
|
|
345
|
-
|
|
470
|
+
/** 右上角关闭按钮 */
|
|
346
471
|
topCloseBtnSize?: string;
|
|
347
472
|
topCloseBtnTop?: string;
|
|
348
473
|
topCloseBtnRight?: string;
|
|
@@ -350,7 +475,7 @@ interface ImagesViewerOptions {
|
|
|
350
475
|
topCloseBtnBgColor?: string;
|
|
351
476
|
topCloseBtnHoverBg?: string;
|
|
352
477
|
|
|
353
|
-
|
|
478
|
+
/** 信息栏相关 */
|
|
354
479
|
infoBgColor?: string;
|
|
355
480
|
infoBorderRadius?: string;
|
|
356
481
|
infoPadding?: string;
|
|
@@ -358,7 +483,7 @@ interface ImagesViewerOptions {
|
|
|
358
483
|
infoTop?: string;
|
|
359
484
|
infoLeft?: string;
|
|
360
485
|
|
|
361
|
-
|
|
486
|
+
/** 缩放指示器 */
|
|
362
487
|
zoomIndicatorBg?: string;
|
|
363
488
|
zoomIndicatorBorderRadius?: string;
|
|
364
489
|
zoomIndicatorPadding?: string;
|
|
@@ -366,66 +491,19 @@ interface ImagesViewerOptions {
|
|
|
366
491
|
zoomIndicatorTop?: string;
|
|
367
492
|
zoomIndicatorLeft?: string;
|
|
368
493
|
|
|
369
|
-
|
|
494
|
+
/** 通用 */
|
|
370
495
|
activeColor?: string;
|
|
371
496
|
textColor?: string;
|
|
372
497
|
shadowColor?: string;
|
|
373
498
|
transitionSpeed?: string;
|
|
374
499
|
|
|
375
|
-
|
|
500
|
+
/** 缩略图 */
|
|
376
501
|
thumbItemWidth?: string;
|
|
377
502
|
thumbItemHeight?: string;
|
|
378
503
|
thumbGap?: string;
|
|
379
504
|
thumbPadding?: string;
|
|
380
505
|
thumbMaxWidth?: string;
|
|
381
506
|
};
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* 回调函数 - 查看器显示时触发
|
|
385
|
-
*/
|
|
386
|
-
onShow?: (container: HTMLElement) => void;
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* 回调函数 - 查看器关闭时触发
|
|
390
|
-
*/
|
|
391
|
-
onClose?: () => void;
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* 回调函数 - 图片切换时触发
|
|
395
|
-
* @param currentIndex 当前图片索引
|
|
396
|
-
* @param direction 切换方向 'prev' | 'next'
|
|
397
|
-
*/
|
|
398
|
-
onChange?: (currentIndex: number, direction: 'prev' | 'next') => void;
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* 旋转事件回调
|
|
402
|
-
*/
|
|
403
|
-
onRotate?: (data: RotateEventData) => void;
|
|
404
|
-
|
|
405
|
-
/**
|
|
406
|
-
* 拖动事件回调
|
|
407
|
-
*/
|
|
408
|
-
onDrag?: (data: DragEventData) => void;
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* 缩放事件回调
|
|
412
|
-
*/
|
|
413
|
-
onZoom?: (data: ZoomEventData) => void;
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* 信息栏自定义函数
|
|
417
|
-
*/
|
|
418
|
-
onInfo?: (data: InfoTextParams) => string | null | undefined;
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* 页数显示自定义函数
|
|
422
|
-
*/
|
|
423
|
-
onCounter?: (data: CounterParams) => string | null | undefined;
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* 缩放指数自定义函数
|
|
427
|
-
*/
|
|
428
|
-
onZoomIndicator?: (data: ZoomIndicatorParams) => string | null | undefined;
|
|
429
507
|
}
|
|
430
508
|
|
|
431
509
|
/**
|