vitepress-plugin-image-preview 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +141 -0
- package/dist/components/ImagePreview.vue +68 -0
- package/dist/components/ImageViewer.vue +450 -0
- package/dist/index.d.mts +56 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +95 -0
- package/dist/index.mjs +59 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 sugar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# vitepress-plugin-image-preview
|
|
2
|
+
|
|
3
|
+
[English](https://github.com/ATQQ/sugar-blog/blob/master/packages/vitepress-plugin-image-preview/README-en.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
VitePress 图片预览插件,样式参考 [Element Plus Image Viewer](https://element-plus.org/zh-CN/component/image.html#image-preview)。
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
## 安装
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add vitepress-plugin-image-preview
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## 使用
|
|
17
|
+
|
|
18
|
+
在 `.vitepress/config.ts` 中引入插件:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { defineConfig } from 'vitepress'
|
|
22
|
+
import { ImagePreviewPlugin } from 'vitepress-plugin-image-preview'
|
|
23
|
+
|
|
24
|
+
export default defineConfig({
|
|
25
|
+
vite: {
|
|
26
|
+
plugins: [
|
|
27
|
+
ImagePreviewPlugin()
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 选项
|
|
34
|
+
|
|
35
|
+
| 参数 | 说明 | 类型 | 默认值 |
|
|
36
|
+
| --- | --- | --- | --- |
|
|
37
|
+
| selector | 图片选择器 | `string` | `.content-container .main img,.VPPage img` |
|
|
38
|
+
| wrapperId | 监听容器 ID | `string` | `#VPContent` |
|
|
39
|
+
| slots | 插槽 | `string \| string[]` | `['doc-before','page-top']` |
|
|
40
|
+
| showProgress | 是否在预览图片时显示进度条 | `boolean` | `true` |
|
|
41
|
+
| infinite | 是否开启无限循环 | `boolean` | `false` |
|
|
42
|
+
| zoomRatio | 缩放速率 | `number` | `1.2` |
|
|
43
|
+
| hideOnClickModal | 点击 modal 时是否隐藏 | `boolean` | `false` |
|
|
44
|
+
| minScale | 最小缩放比例 | `number` | `0.2` |
|
|
45
|
+
| maxScale | 最大缩放比例 | `number` | `7` |
|
|
46
|
+
| toolbar | 工具栏 | `string[]` | `['zoomOut', 'zoomIn','reset', 'rotateLeft', 'rotateRight', 'download']` |
|
|
47
|
+
|
|
48
|
+
## 高级用法
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { defineConfig } from 'vitepress'
|
|
52
|
+
import { ImagePreviewPlugin } from 'vitepress-plugin-image-preview'
|
|
53
|
+
|
|
54
|
+
export default defineConfig({
|
|
55
|
+
vite: {
|
|
56
|
+
plugins: [
|
|
57
|
+
ImagePreviewPlugin({
|
|
58
|
+
// 自定义图片选择器
|
|
59
|
+
selector: '.content-container .main img',
|
|
60
|
+
// 自定义监听容器 ID
|
|
61
|
+
wrapperId: '#VPContent',
|
|
62
|
+
// 显示进度条
|
|
63
|
+
showProgress: true,
|
|
64
|
+
// 开启无限循环
|
|
65
|
+
infinite: true,
|
|
66
|
+
// 点击遮罩关闭
|
|
67
|
+
hideOnClickModal: true,
|
|
68
|
+
// 自定义缩放比率
|
|
69
|
+
zoomRatio: 1.1,
|
|
70
|
+
// 自定义工具栏
|
|
71
|
+
toolbar: ['zoomIn', 'zoomOut', 'reset', 'rotateLeft', 'rotateRight', 'download']
|
|
72
|
+
})
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 类型定义
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
export interface ImagePreviewOptions {
|
|
82
|
+
/**
|
|
83
|
+
* Image selector
|
|
84
|
+
* @default '.content-container .main img,.VPPage img'
|
|
85
|
+
*/
|
|
86
|
+
selector?: string
|
|
87
|
+
/**
|
|
88
|
+
* Wrapper selector to attach event listener
|
|
89
|
+
* @default '#VPContent'
|
|
90
|
+
*/
|
|
91
|
+
wrapperId?: string
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Slots to attach event listener
|
|
95
|
+
* @default ['doc-before','page-top']
|
|
96
|
+
*/
|
|
97
|
+
slots?: string | string[]
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Show progress bar | 是否在预览图片时显示进度条
|
|
101
|
+
* @default true
|
|
102
|
+
*/
|
|
103
|
+
showProgress?: boolean
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Infinite loop | 是否开启无限循环
|
|
107
|
+
* @default false
|
|
108
|
+
*/
|
|
109
|
+
infinite?: boolean
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Zoom ratio | 缩放速率
|
|
113
|
+
* @default 1.2
|
|
114
|
+
*/
|
|
115
|
+
zoomRatio?: number
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Hide modal on click | 点击 modal 时是否隐藏
|
|
119
|
+
* @default false
|
|
120
|
+
*/
|
|
121
|
+
hideOnClickModal?: boolean
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Minimum scale | 最小缩放比例
|
|
125
|
+
* @default 0.2
|
|
126
|
+
*/
|
|
127
|
+
minScale?: number
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Maximum scale | 最大缩放比例
|
|
131
|
+
* @default 7
|
|
132
|
+
*/
|
|
133
|
+
maxScale?: number
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Toolbar | 工具栏
|
|
137
|
+
* @default ['zoomOut', 'zoomIn','reset', 'rotateLeft', 'rotateRight', 'download']
|
|
138
|
+
*/
|
|
139
|
+
toolbar?: string[]
|
|
140
|
+
}
|
|
141
|
+
```
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { onMounted, onUnmounted, reactive, ref } from 'vue'
|
|
3
|
+
|
|
4
|
+
// @ts-expect-error
|
|
5
|
+
import imagePreviewOptions from 'virtual:image-preview-options'
|
|
6
|
+
import ImageViewer from './ImageViewer.vue'
|
|
7
|
+
|
|
8
|
+
const show = ref(false)
|
|
9
|
+
const previewImageInfo = reactive<{ url: string; list: string[]; idx: number }>(
|
|
10
|
+
{
|
|
11
|
+
url: '',
|
|
12
|
+
list: [],
|
|
13
|
+
idx: 0
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
function previewImage(e: Event) {
|
|
18
|
+
const target = e.target as HTMLElement
|
|
19
|
+
const currentTarget = e.currentTarget as HTMLElement
|
|
20
|
+
if (target.tagName.toLowerCase() === 'img') {
|
|
21
|
+
const selector = imagePreviewOptions?.selector || '.content-container .main img,.VPPage img'
|
|
22
|
+
const imgs = currentTarget.querySelectorAll<HTMLImageElement>(selector)
|
|
23
|
+
const idx = Array.from(imgs).findIndex(el => el === target)
|
|
24
|
+
const urls = Array.from(imgs).map(el => el.src)
|
|
25
|
+
|
|
26
|
+
const url = target.getAttribute('src')
|
|
27
|
+
previewImageInfo.url = url!
|
|
28
|
+
previewImageInfo.list = urls
|
|
29
|
+
previewImageInfo.idx = idx
|
|
30
|
+
|
|
31
|
+
// 兼容点击main之外的图片
|
|
32
|
+
if (idx === -1 && url) {
|
|
33
|
+
previewImageInfo.list.push(url)
|
|
34
|
+
previewImageInfo.idx = previewImageInfo.list.length - 1
|
|
35
|
+
}
|
|
36
|
+
show.value = true
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
onMounted(() => {
|
|
41
|
+
const wrapperId = imagePreviewOptions?.wrapperId || '#VPContent'
|
|
42
|
+
const docDomContainer = document.querySelector(wrapperId)
|
|
43
|
+
docDomContainer?.addEventListener('click', previewImage)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
onUnmounted(() => {
|
|
47
|
+
const wrapperId = imagePreviewOptions?.wrapperId || '#VPContent'
|
|
48
|
+
const docDomContainer = document.querySelector(wrapperId)
|
|
49
|
+
docDomContainer?.removeEventListener('click', previewImage)
|
|
50
|
+
})
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<template>
|
|
54
|
+
<ImageViewer
|
|
55
|
+
v-if="show"
|
|
56
|
+
:infinite="imagePreviewOptions?.infinite ?? false"
|
|
57
|
+
:hide-on-click-modal="imagePreviewOptions?.hideOnClickModal ?? false"
|
|
58
|
+
:show-progress="imagePreviewOptions?.showProgress ?? true"
|
|
59
|
+
:zoom-ratio="imagePreviewOptions?.zoomRatio ?? 1.2"
|
|
60
|
+
:min-scale="imagePreviewOptions?.minScale ?? 0.2"
|
|
61
|
+
:max-scale="imagePreviewOptions?.maxScale ?? 7"
|
|
62
|
+
:toolbar="imagePreviewOptions?.toolbar ?? ['zoomOut', 'zoomIn', 'reset', 'rotateLeft', 'rotateRight', 'download']"
|
|
63
|
+
teleported
|
|
64
|
+
:url-list="previewImageInfo.list"
|
|
65
|
+
:initial-index="previewImageInfo.idx"
|
|
66
|
+
@close="show = false"
|
|
67
|
+
/>
|
|
68
|
+
</template>
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue'
|
|
3
|
+
import { useScrollLock } from '@vueuse/core'
|
|
4
|
+
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
urlList: {
|
|
7
|
+
type: Array as () => string[],
|
|
8
|
+
default: () => [],
|
|
9
|
+
},
|
|
10
|
+
initialIndex: {
|
|
11
|
+
type: Number,
|
|
12
|
+
default: 0,
|
|
13
|
+
},
|
|
14
|
+
infinite: {
|
|
15
|
+
type: Boolean,
|
|
16
|
+
default: true,
|
|
17
|
+
},
|
|
18
|
+
hideOnClickModal: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: false,
|
|
21
|
+
},
|
|
22
|
+
teleported: {
|
|
23
|
+
type: Boolean,
|
|
24
|
+
default: false,
|
|
25
|
+
},
|
|
26
|
+
toolbar: {
|
|
27
|
+
type: Array as () => string[],
|
|
28
|
+
default: () => ['zoomOut', 'zoomIn', 'reset', 'rotateLeft', 'rotateRight', 'download'],
|
|
29
|
+
},
|
|
30
|
+
showProgress: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: true,
|
|
33
|
+
},
|
|
34
|
+
zoomRatio: {
|
|
35
|
+
type: Number,
|
|
36
|
+
default: 1.2,
|
|
37
|
+
},
|
|
38
|
+
minScale: {
|
|
39
|
+
type: Number,
|
|
40
|
+
default: 0.2,
|
|
41
|
+
},
|
|
42
|
+
maxScale: {
|
|
43
|
+
type: Number,
|
|
44
|
+
default: 7,
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const emit = defineEmits(['close', 'switch'])
|
|
49
|
+
|
|
50
|
+
const index = ref(props.initialIndex)
|
|
51
|
+
const isLocked = useScrollLock(document.body)
|
|
52
|
+
|
|
53
|
+
const currentImg = computed(() => props.urlList[index.value])
|
|
54
|
+
const isSingle = computed(() => props.urlList.length <= 1)
|
|
55
|
+
const isFirst = computed(() => index.value === 0)
|
|
56
|
+
const isLast = computed(() => index.value === props.urlList.length - 1)
|
|
57
|
+
|
|
58
|
+
const transform = reactive({
|
|
59
|
+
scale: 1,
|
|
60
|
+
deg: 0,
|
|
61
|
+
offsetX: 0,
|
|
62
|
+
offsetY: 0,
|
|
63
|
+
enableTransition: false,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const imgStyle = computed(() => ({
|
|
67
|
+
transform: `translate3d(${transform.offsetX}px, ${transform.offsetY}px, 0) scale(${transform.scale}) rotate(${transform.deg}deg)`,
|
|
68
|
+
transition: transform.enableTransition ? 'transform .3s' : '',
|
|
69
|
+
}))
|
|
70
|
+
|
|
71
|
+
function reset() {
|
|
72
|
+
transform.scale = 1
|
|
73
|
+
transform.deg = 0
|
|
74
|
+
transform.offsetX = 0
|
|
75
|
+
transform.offsetY = 0
|
|
76
|
+
transform.enableTransition = false
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
watch(index, () => {
|
|
80
|
+
reset()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
function hide() {
|
|
84
|
+
isLocked.value = false
|
|
85
|
+
emit('close')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function prev() {
|
|
89
|
+
if (isFirst.value && !props.infinite)
|
|
90
|
+
return
|
|
91
|
+
const len = props.urlList.length
|
|
92
|
+
index.value = (index.value - 1 + len) % len
|
|
93
|
+
emit('switch', index.value)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function next() {
|
|
97
|
+
if (isLast.value && !props.infinite)
|
|
98
|
+
return
|
|
99
|
+
const len = props.urlList.length
|
|
100
|
+
index.value = (index.value + 1) % len
|
|
101
|
+
emit('switch', index.value)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function handleMaskClick() {
|
|
105
|
+
if (props.hideOnClickModal) {
|
|
106
|
+
hide()
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Actions
|
|
111
|
+
function handleActions(action: string) {
|
|
112
|
+
transform.enableTransition = true
|
|
113
|
+
switch (action) {
|
|
114
|
+
case 'zoomOut':
|
|
115
|
+
if (transform.scale > props.minScale) {
|
|
116
|
+
transform.scale = parseFloat(Math.max(props.minScale, transform.scale / props.zoomRatio).toFixed(3))
|
|
117
|
+
}
|
|
118
|
+
break
|
|
119
|
+
case 'zoomIn':
|
|
120
|
+
if (transform.scale < props.maxScale) {
|
|
121
|
+
transform.scale = parseFloat(Math.min(props.maxScale, transform.scale * props.zoomRatio).toFixed(3))
|
|
122
|
+
}
|
|
123
|
+
break
|
|
124
|
+
case 'rotateRight':
|
|
125
|
+
transform.deg += 90
|
|
126
|
+
break
|
|
127
|
+
case 'rotateLeft':
|
|
128
|
+
transform.deg -= 90
|
|
129
|
+
break
|
|
130
|
+
case 'reset': // 1:1
|
|
131
|
+
reset()
|
|
132
|
+
break
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function handleDownload() {
|
|
137
|
+
const a = document.createElement('a')
|
|
138
|
+
a.href = currentImg.value
|
|
139
|
+
a.target = '_blank'
|
|
140
|
+
a.download = ''
|
|
141
|
+
document.body.appendChild(a)
|
|
142
|
+
a.click()
|
|
143
|
+
document.body.removeChild(a)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Drag Logic
|
|
147
|
+
const isDragging = ref(false)
|
|
148
|
+
let startX = 0
|
|
149
|
+
let startY = 0
|
|
150
|
+
let startOffsetX = 0
|
|
151
|
+
let startOffsetY = 0
|
|
152
|
+
|
|
153
|
+
function handleMouseDown(e: MouseEvent) {
|
|
154
|
+
if (e.button !== 0)
|
|
155
|
+
return
|
|
156
|
+
transform.enableTransition = false
|
|
157
|
+
isDragging.value = true
|
|
158
|
+
startX = e.clientX
|
|
159
|
+
startY = e.clientY
|
|
160
|
+
startOffsetX = transform.offsetX
|
|
161
|
+
startOffsetY = transform.offsetY
|
|
162
|
+
e.preventDefault()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function handleMouseMove(e: MouseEvent) {
|
|
166
|
+
if (!isDragging.value)
|
|
167
|
+
return
|
|
168
|
+
const deltaX = e.clientX - startX
|
|
169
|
+
const deltaY = e.clientY - startY
|
|
170
|
+
transform.offsetX = startOffsetX + deltaX
|
|
171
|
+
transform.offsetY = startOffsetY + deltaY
|
|
172
|
+
e.preventDefault()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function handleMouseUp() {
|
|
176
|
+
isDragging.value = false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Wheel Zoom
|
|
180
|
+
function handleWheel(e: WheelEvent) {
|
|
181
|
+
const delta = e.deltaY < 0 ? 1 : -1
|
|
182
|
+
const zoomRate = props.zoomRatio
|
|
183
|
+
const newScale = delta > 0
|
|
184
|
+
? transform.scale * zoomRate
|
|
185
|
+
: transform.scale / zoomRate
|
|
186
|
+
|
|
187
|
+
if (newScale >= props.minScale && newScale <= props.maxScale) {
|
|
188
|
+
transform.scale = parseFloat(newScale.toFixed(3))
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
onMounted(() => {
|
|
193
|
+
isLocked.value = true
|
|
194
|
+
window.addEventListener('keydown', handleKeydown)
|
|
195
|
+
window.addEventListener('mouseup', handleMouseUp)
|
|
196
|
+
window.addEventListener('mousemove', handleMouseMove)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
onUnmounted(() => {
|
|
200
|
+
isLocked.value = false
|
|
201
|
+
window.removeEventListener('keydown', handleKeydown)
|
|
202
|
+
window.removeEventListener('mouseup', handleMouseUp)
|
|
203
|
+
window.removeEventListener('mousemove', handleMouseMove)
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
207
|
+
if (e.key === 'Escape') {
|
|
208
|
+
hide()
|
|
209
|
+
}
|
|
210
|
+
else if (e.key === 'ArrowLeft') {
|
|
211
|
+
prev()
|
|
212
|
+
}
|
|
213
|
+
else if (e.key === 'ArrowRight') {
|
|
214
|
+
next()
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
</script>
|
|
218
|
+
|
|
219
|
+
<template>
|
|
220
|
+
<Teleport to="body" :disabled="!teleported">
|
|
221
|
+
<transition name="viewer-fade">
|
|
222
|
+
<div class="vitepress-image-viewer__wrapper" tabindex="-1" @wheel.prevent="handleWheel">
|
|
223
|
+
<div class="vitepress-image-viewer__mask" @click="handleMaskClick" />
|
|
224
|
+
|
|
225
|
+
<!-- Close -->
|
|
226
|
+
<span class="vitepress-image-viewer__btn vitepress-image-viewer__close" @click="hide">
|
|
227
|
+
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z" /></svg>
|
|
228
|
+
</span>
|
|
229
|
+
|
|
230
|
+
<!-- Arrows -->
|
|
231
|
+
<template v-if="!isSingle">
|
|
232
|
+
<span
|
|
233
|
+
class="vitepress-image-viewer__btn vitepress-image-viewer__prev"
|
|
234
|
+
:class="{ 'is-disabled': !infinite && isFirst }"
|
|
235
|
+
@click="prev"
|
|
236
|
+
>
|
|
237
|
+
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path fill="currentColor" d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z" /></svg>
|
|
238
|
+
</span>
|
|
239
|
+
<span
|
|
240
|
+
class="vitepress-image-viewer__btn vitepress-image-viewer__next"
|
|
241
|
+
:class="{ 'is-disabled': !infinite && isLast }"
|
|
242
|
+
@click="next"
|
|
243
|
+
>
|
|
244
|
+
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path fill="currentColor" d="M340.864 149.312a30.592 30.592 0 0 0 0 42.752L652.736 512 340.864 831.872a30.592 30.592 0 0 0 0 42.752 29.12 29.12 0 0 0 41.728 0L714.24 534.336a32 32 0 0 0 0-44.672L382.592 149.376a29.12 29.12 0 0 0-41.728 0z" /></svg>
|
|
245
|
+
</span>
|
|
246
|
+
</template>
|
|
247
|
+
|
|
248
|
+
<!-- Progress -->
|
|
249
|
+
<div v-if="showProgress" class="vitepress-image-viewer__progress">
|
|
250
|
+
{{ index + 1 }} / {{ urlList.length }}
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<!-- Actions Toolbar -->
|
|
254
|
+
<div v-if="toolbar.length" class="vitepress-image-viewer__actions">
|
|
255
|
+
<!-- 图片进度展示当前图片索引和图片总数 -->
|
|
256
|
+
<div class="vitepress-image-viewer__actions__inner">
|
|
257
|
+
<svg v-if="toolbar.includes('zoomOut')" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" @click="handleActions('zoomOut')"><path fill="currentColor" d="m795.904 750.72 124.992 124.928a32 32 0 0 1-45.248 45.248L750.656 795.904a416 416 0 1 1 45.248-45.248zM480 832a352 352 0 1 0 0-704 352 352 0 0 0 0 704M352 448h256a32 32 0 0 1 0 64H352a32 32 0 0 1 0-64" /></svg>
|
|
258
|
+
<svg v-if="toolbar.includes('zoomIn')" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" @click="handleActions('zoomIn')"><path fill="currentColor" d="m795.904 750.72 124.992 124.928a32 32 0 0 1-45.248 45.248L750.656 795.904a416 416 0 1 1 45.248-45.248zM480 832a352 352 0 1 0 0-704 352 352 0 0 0 0 704m-32-384v-96a32 32 0 0 1 64 0v96h96a32 32 0 0 1 0 64h-96v96a32 32 0 0 1-64 0v-96h-96a32 32 0 0 1 0-64z" /></svg>
|
|
259
|
+
<i v-if="toolbar.includes('zoomOut') || toolbar.includes('zoomIn')" class="vitepress-image-viewer__actions__divider" />
|
|
260
|
+
<svg v-if="toolbar.includes('reset')" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" @click="handleActions('reset')"><path fill="currentColor" d="M784.512 230.272v-50.56a32 32 0 1 1 64 0v149.056a32 32 0 0 1-32 32H667.52a32 32 0 1 1 0-64h92.992A320 320 0 1 0 524.8 833.152a320 320 0 0 0 320-320h64a384 384 0 0 1-384 384 384 384 0 0 1-384-384 384 384 0 0 1 643.712-282.88" /></svg>
|
|
261
|
+
<i v-if="toolbar.includes('reset')" class="vitepress-image-viewer__actions__divider" />
|
|
262
|
+
<svg v-if="toolbar.includes('rotateLeft')" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" @click="handleActions('rotateLeft')"><path fill="currentColor" d="M289.088 296.704h92.992a32 32 0 0 1 0 64H232.96a32 32 0 0 1-32-32V179.712a32 32 0 0 1 64 0v50.56a384 384 0 0 1 643.84 282.88 384 384 0 0 1-383.936 384 384 384 0 0 1-384-384h64a320 320 0 1 0 640 0 320 320 0 0 0-555.712-216.448z" /></svg>
|
|
263
|
+
<svg v-if="toolbar.includes('rotateRight')" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" @click="handleActions('rotateRight')"><path fill="currentColor" d="M771.776 794.88A384 384 0 0 1 128 512h64a320 320 0 0 0 555.712 216.448H654.72a32 32 0 1 1 0-64h149.056a32 32 0 0 1 32 32v148.928a32 32 0 1 1-64 0v-50.56zM276.288 295.616h92.992a32 32 0 0 1 0 64H220.16a32 32 0 0 1-32-32V178.56a32 32 0 0 1 64 0v50.56A384 384 0 0 1 896.128 512h-64a320 320 0 0 0-555.776-216.384z" /></svg>
|
|
264
|
+
<i v-if="toolbar.includes('rotateLeft') || toolbar.includes('rotateRight')" class="vitepress-image-viewer__actions__divider" />
|
|
265
|
+
<svg v-if="toolbar.includes('download')" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" @click="handleDownload"><path fill="currentColor" d="M160 832h704a32 32 0 1 1 0 64H160a32 32 0 1 1 0-64m384-253.696 236.288-236.352 45.248 45.248L508.8 704 192 387.2l45.248-45.248L480 584.704V128h64z" /></svg>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<!-- Canvas -->
|
|
270
|
+
<div class="vitepress-image-viewer__canvas">
|
|
271
|
+
<img
|
|
272
|
+
:src="currentImg"
|
|
273
|
+
:style="imgStyle"
|
|
274
|
+
class="vitepress-image-viewer__img"
|
|
275
|
+
@mousedown="handleMouseDown"
|
|
276
|
+
>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</transition>
|
|
280
|
+
</Teleport>
|
|
281
|
+
</template>
|
|
282
|
+
|
|
283
|
+
<style lang="scss" scoped>
|
|
284
|
+
.vitepress-image-viewer__wrapper {
|
|
285
|
+
position: fixed;
|
|
286
|
+
top: 0;
|
|
287
|
+
right: 0;
|
|
288
|
+
bottom: 0;
|
|
289
|
+
left: 0;
|
|
290
|
+
z-index: 2000;
|
|
291
|
+
display: flex;
|
|
292
|
+
align-items: center;
|
|
293
|
+
justify-content: center;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.vitepress-image-viewer__mask {
|
|
297
|
+
position: absolute;
|
|
298
|
+
width: 100%;
|
|
299
|
+
height: 100%;
|
|
300
|
+
top: 0;
|
|
301
|
+
left: 0;
|
|
302
|
+
opacity: .5;
|
|
303
|
+
background: #000;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.vitepress-image-viewer__btn {
|
|
307
|
+
position: absolute;
|
|
308
|
+
z-index: 1;
|
|
309
|
+
display: flex;
|
|
310
|
+
align-items: center;
|
|
311
|
+
justify-content: center;
|
|
312
|
+
border-radius: 50%;
|
|
313
|
+
opacity: .8;
|
|
314
|
+
cursor: pointer;
|
|
315
|
+
box-sizing: border-box;
|
|
316
|
+
user-select: none;
|
|
317
|
+
background-color: #606266;
|
|
318
|
+
color: #fff;
|
|
319
|
+
width: 44px;
|
|
320
|
+
height: 44px;
|
|
321
|
+
font-size: 24px;
|
|
322
|
+
|
|
323
|
+
&:hover {
|
|
324
|
+
border-color: #fff;
|
|
325
|
+
background-color: #909399;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
&.is-disabled {
|
|
329
|
+
cursor: not-allowed;
|
|
330
|
+
opacity: .3;
|
|
331
|
+
background-color: #606266;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.vitepress-image-viewer__close {
|
|
336
|
+
top: 40px;
|
|
337
|
+
right: 40px;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.vitepress-image-viewer__prev {
|
|
341
|
+
top: 50%;
|
|
342
|
+
transform: translateY(-50%);
|
|
343
|
+
left: 40px;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.vitepress-image-viewer__next {
|
|
347
|
+
top: 50%;
|
|
348
|
+
transform: translateY(-50%);
|
|
349
|
+
right: 40px;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.vitepress-image-viewer__actions {
|
|
353
|
+
left: 50%;
|
|
354
|
+
bottom: 30px;
|
|
355
|
+
transform: translateX(-50%);
|
|
356
|
+
min-width: 282px;
|
|
357
|
+
height: 44px;
|
|
358
|
+
padding: 0 23px;
|
|
359
|
+
background-color: #606266;
|
|
360
|
+
border-color: #fff;
|
|
361
|
+
border-radius: 22px;
|
|
362
|
+
position: absolute;
|
|
363
|
+
z-index: 1;
|
|
364
|
+
display: flex;
|
|
365
|
+
align-items: center;
|
|
366
|
+
justify-content: center;
|
|
367
|
+
opacity: .8;
|
|
368
|
+
box-sizing: border-box;
|
|
369
|
+
user-select: none;
|
|
370
|
+
|
|
371
|
+
&__inner {
|
|
372
|
+
width: 100%;
|
|
373
|
+
height: 100%;
|
|
374
|
+
text-align: justify;
|
|
375
|
+
cursor: default;
|
|
376
|
+
font-size: 23px;
|
|
377
|
+
color: #fff;
|
|
378
|
+
display: flex;
|
|
379
|
+
align-items: center;
|
|
380
|
+
justify-content: space-around;
|
|
381
|
+
gap: 12px;
|
|
382
|
+
|
|
383
|
+
svg {
|
|
384
|
+
cursor: pointer;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
&__divider {
|
|
389
|
+
display: inline-block;
|
|
390
|
+
width: 1px;
|
|
391
|
+
height: 18px;
|
|
392
|
+
margin: 0 10px;
|
|
393
|
+
background: hsla(0,0%,100%,.5);
|
|
394
|
+
vertical-align: middle;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.vitepress-image-viewer__progress {
|
|
399
|
+
position: absolute;
|
|
400
|
+
left: 50%;
|
|
401
|
+
transform: translateX(-50%);
|
|
402
|
+
bottom: 80px;
|
|
403
|
+
font-size: 16px;
|
|
404
|
+
color: #fff;
|
|
405
|
+
user-select: none;
|
|
406
|
+
z-index: 10;
|
|
407
|
+
opacity: 0.8;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.vitepress-image-viewer__canvas {
|
|
411
|
+
width: 100%;
|
|
412
|
+
height: 100%;
|
|
413
|
+
display: flex;
|
|
414
|
+
justify-content: center;
|
|
415
|
+
align-items: center;
|
|
416
|
+
z-index: 0;
|
|
417
|
+
pointer-events: none;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.vitepress-image-viewer__img {
|
|
421
|
+
max-width: 100%;
|
|
422
|
+
max-height: 100%;
|
|
423
|
+
user-select: none;
|
|
424
|
+
pointer-events: auto;
|
|
425
|
+
cursor: grab;
|
|
426
|
+
transition: transform .3s;
|
|
427
|
+
|
|
428
|
+
&:active {
|
|
429
|
+
cursor: grabbing;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.viewer-fade-enter-active {
|
|
434
|
+
animation: viewer-fade-in .3s;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.viewer-fade-leave-active {
|
|
438
|
+
animation: viewer-fade-out .3s;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@keyframes viewer-fade-in {
|
|
442
|
+
0% { opacity: 0; }
|
|
443
|
+
100% { opacity: 1; }
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
@keyframes viewer-fade-out {
|
|
447
|
+
0% { opacity: 1; }
|
|
448
|
+
100% { opacity: 0; }
|
|
449
|
+
}
|
|
450
|
+
</style>
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
interface ImagePreviewOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Image selector
|
|
4
|
+
* @default '.content-container .main img,.VPPage img'
|
|
5
|
+
*/
|
|
6
|
+
selector?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Wrapper selector to attach event listener
|
|
9
|
+
* @default '#VPContent'
|
|
10
|
+
*/
|
|
11
|
+
wrapperId?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Slots to attach event listener
|
|
14
|
+
* @default ['doc-before','page-top']
|
|
15
|
+
*/
|
|
16
|
+
slots?: string | string[];
|
|
17
|
+
/**
|
|
18
|
+
* Show progress bar | 是否在预览图片时显示进度条
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
showProgress?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Infinite loop | 是否开启无限循环
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
infinite?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Zoom ratio | 缩放速率
|
|
29
|
+
* @default 1.2
|
|
30
|
+
*/
|
|
31
|
+
zoomRatio?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Hide modal on click | 点击 modal 时是否隐藏
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
hideOnClickModal?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Minimum scale | 最小缩放比例
|
|
39
|
+
* @default 0.2
|
|
40
|
+
*/
|
|
41
|
+
minScale?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Maximum scale | 最大缩放比例
|
|
44
|
+
* @default 7
|
|
45
|
+
*/
|
|
46
|
+
maxScale?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Toolbar | 工具栏
|
|
49
|
+
* @default ['zoomOut', 'zoomIn','reset', 'rotateLeft', 'rotateRight', 'download']
|
|
50
|
+
*/
|
|
51
|
+
toolbar?: string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare function ImagePreviewPlugin(options?: ImagePreviewOptions): any;
|
|
55
|
+
|
|
56
|
+
export { ImagePreviewOptions, ImagePreviewPlugin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
interface ImagePreviewOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Image selector
|
|
4
|
+
* @default '.content-container .main img,.VPPage img'
|
|
5
|
+
*/
|
|
6
|
+
selector?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Wrapper selector to attach event listener
|
|
9
|
+
* @default '#VPContent'
|
|
10
|
+
*/
|
|
11
|
+
wrapperId?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Slots to attach event listener
|
|
14
|
+
* @default ['doc-before','page-top']
|
|
15
|
+
*/
|
|
16
|
+
slots?: string | string[];
|
|
17
|
+
/**
|
|
18
|
+
* Show progress bar | 是否在预览图片时显示进度条
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
showProgress?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Infinite loop | 是否开启无限循环
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
infinite?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Zoom ratio | 缩放速率
|
|
29
|
+
* @default 1.2
|
|
30
|
+
*/
|
|
31
|
+
zoomRatio?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Hide modal on click | 点击 modal 时是否隐藏
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
hideOnClickModal?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Minimum scale | 最小缩放比例
|
|
39
|
+
* @default 0.2
|
|
40
|
+
*/
|
|
41
|
+
minScale?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Maximum scale | 最大缩放比例
|
|
44
|
+
* @default 7
|
|
45
|
+
*/
|
|
46
|
+
maxScale?: number;
|
|
47
|
+
/**
|
|
48
|
+
* Toolbar | 工具栏
|
|
49
|
+
* @default ['zoomOut', 'zoomIn','reset', 'rotateLeft', 'rotateRight', 'download']
|
|
50
|
+
*/
|
|
51
|
+
toolbar?: string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare function ImagePreviewPlugin(options?: ImagePreviewOptions): any;
|
|
55
|
+
|
|
56
|
+
export { ImagePreviewOptions, ImagePreviewPlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
ImagePreviewPlugin: () => ImagePreviewPlugin
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(src_exports);
|
|
36
|
+
var import_node_path = __toESM(require("path"));
|
|
37
|
+
var import_node_url = require("url");
|
|
38
|
+
var import_javascript_stringify = require("javascript-stringify");
|
|
39
|
+
var import_meta = {};
|
|
40
|
+
function isESM() {
|
|
41
|
+
return typeof __filename === "undefined" || typeof __dirname === "undefined";
|
|
42
|
+
}
|
|
43
|
+
function getDirname() {
|
|
44
|
+
return isESM() ? import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url)) : __dirname;
|
|
45
|
+
}
|
|
46
|
+
var componentName = "ImagePreview";
|
|
47
|
+
var componentFile = `${componentName}.vue`;
|
|
48
|
+
var aliasComponentFile = `${getDirname()}/components/${componentFile}`;
|
|
49
|
+
var virtualModuleId = "virtual:image-preview-options";
|
|
50
|
+
var resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
51
|
+
function ImagePreviewPlugin(options = {}) {
|
|
52
|
+
const pluginOps = {
|
|
53
|
+
name: "vitepress-plugin-image-preview",
|
|
54
|
+
enforce: "pre",
|
|
55
|
+
config: () => {
|
|
56
|
+
return {
|
|
57
|
+
resolve: {
|
|
58
|
+
alias: {
|
|
59
|
+
[`./${componentFile}`]: aliasComponentFile
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
transform(code, id) {
|
|
65
|
+
if (id.endsWith("vitepress/dist/client/theme-default/Layout.vue")) {
|
|
66
|
+
let transformResult = code;
|
|
67
|
+
const slots = [options.slots || ["doc-before", "page-top"]].flat();
|
|
68
|
+
for (const slot of slots) {
|
|
69
|
+
const slotPosition = `<slot name="${slot}" />`;
|
|
70
|
+
transformResult = transformResult.replace(slotPosition, `${slotPosition}
|
|
71
|
+
<ClientOnly><${componentName} /></ClientOnly>`);
|
|
72
|
+
}
|
|
73
|
+
const setupPosition = '<script setup lang="ts">';
|
|
74
|
+
transformResult = transformResult.replace(setupPosition, `${setupPosition}
|
|
75
|
+
import ${componentName} from './${componentFile}'`);
|
|
76
|
+
return transformResult;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
resolveId(id) {
|
|
80
|
+
if (id === virtualModuleId) {
|
|
81
|
+
return resolvedVirtualModuleId;
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
load(id) {
|
|
85
|
+
if (id === resolvedVirtualModuleId) {
|
|
86
|
+
return `export default ${(0, import_javascript_stringify.stringify)(options)}`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
return pluginOps;
|
|
91
|
+
}
|
|
92
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
93
|
+
0 && (module.exports = {
|
|
94
|
+
ImagePreviewPlugin
|
|
95
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { stringify } from "javascript-stringify";
|
|
5
|
+
function isESM() {
|
|
6
|
+
return typeof __filename === "undefined" || typeof __dirname === "undefined";
|
|
7
|
+
}
|
|
8
|
+
function getDirname() {
|
|
9
|
+
return isESM() ? path.dirname(fileURLToPath(import.meta.url)) : __dirname;
|
|
10
|
+
}
|
|
11
|
+
var componentName = "ImagePreview";
|
|
12
|
+
var componentFile = `${componentName}.vue`;
|
|
13
|
+
var aliasComponentFile = `${getDirname()}/components/${componentFile}`;
|
|
14
|
+
var virtualModuleId = "virtual:image-preview-options";
|
|
15
|
+
var resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
16
|
+
function ImagePreviewPlugin(options = {}) {
|
|
17
|
+
const pluginOps = {
|
|
18
|
+
name: "vitepress-plugin-image-preview",
|
|
19
|
+
enforce: "pre",
|
|
20
|
+
config: () => {
|
|
21
|
+
return {
|
|
22
|
+
resolve: {
|
|
23
|
+
alias: {
|
|
24
|
+
[`./${componentFile}`]: aliasComponentFile
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
transform(code, id) {
|
|
30
|
+
if (id.endsWith("vitepress/dist/client/theme-default/Layout.vue")) {
|
|
31
|
+
let transformResult = code;
|
|
32
|
+
const slots = [options.slots || ["doc-before", "page-top"]].flat();
|
|
33
|
+
for (const slot of slots) {
|
|
34
|
+
const slotPosition = `<slot name="${slot}" />`;
|
|
35
|
+
transformResult = transformResult.replace(slotPosition, `${slotPosition}
|
|
36
|
+
<ClientOnly><${componentName} /></ClientOnly>`);
|
|
37
|
+
}
|
|
38
|
+
const setupPosition = '<script setup lang="ts">';
|
|
39
|
+
transformResult = transformResult.replace(setupPosition, `${setupPosition}
|
|
40
|
+
import ${componentName} from './${componentFile}'`);
|
|
41
|
+
return transformResult;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
resolveId(id) {
|
|
45
|
+
if (id === virtualModuleId) {
|
|
46
|
+
return resolvedVirtualModuleId;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
load(id) {
|
|
50
|
+
if (id === resolvedVirtualModuleId) {
|
|
51
|
+
return `export default ${stringify(options)}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
return pluginOps;
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
ImagePreviewPlugin
|
|
59
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vitepress-plugin-image-preview",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "vitepress plugin, Image Preview, 图片预览",
|
|
5
|
+
"author": "sugar",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/ATQQ/sugar-blog/tree/master/packages/vitepress-plugin-image-preview",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/ATQQ/sugar-blog.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/ATQQ/sugar-blog/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"vitepress",
|
|
17
|
+
"plugin",
|
|
18
|
+
"image",
|
|
19
|
+
"preview",
|
|
20
|
+
"viewer",
|
|
21
|
+
"图片预览"
|
|
22
|
+
],
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": "./dist/index.mjs",
|
|
26
|
+
"require": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"main": "dist/index.js",
|
|
30
|
+
"module": "dist/index.mjs",
|
|
31
|
+
"types": "dist/index.d.ts",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"vitepress": "^1 || ^2"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@vueuse/core": "^14.1.0",
|
|
40
|
+
"javascript-stringify": "^2.1.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"chokidar": "^3.6.0",
|
|
44
|
+
"fs-extra": "^11.1.1",
|
|
45
|
+
"tinyglobby": "^0.2.6",
|
|
46
|
+
"tsup": "^7.2.0",
|
|
47
|
+
"typescript": "^5.5.4",
|
|
48
|
+
"vite": "^5",
|
|
49
|
+
"vitepress": "2.0.0-alpha.15",
|
|
50
|
+
"vue": "^3.5.24"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"dev": "pnpm run /^dev:.*/",
|
|
54
|
+
"dev:plugin": "npx tsup src/index.ts --dts --watch --format esm,cjs --external vitepress",
|
|
55
|
+
"dev:component": "node scripts/watchAndCopy.mjs",
|
|
56
|
+
"dev:watch": "node scripts/watchAndCopy.mjs",
|
|
57
|
+
"build": "pnpm run /^build:.*/",
|
|
58
|
+
"build:plugin": "npx tsup src/index.ts --dts --format esm,cjs --external vitepress --silent",
|
|
59
|
+
"build:component": "node scripts/copyComponents.mjs"
|
|
60
|
+
}
|
|
61
|
+
}
|