|
|
@@ -1,16 +1,30 @@
|
|
|
import { ref, nextTick, onBeforeUnmount, Ref } from 'vue'
|
|
|
|
|
|
export function useThumbnails(getFilePath: (p: string) => string) {
|
|
|
- const thumbnailMap = ref<Record<string, string>>({})
|
|
|
- const generatingSet = new Set<string>()
|
|
|
+ const thumbnailMap = ref<Record<string, string | null>>({})
|
|
|
+ const generatingMap = new Map<string, AbortController>()
|
|
|
const placeholderDataUrl =
|
|
|
'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200"><rect width="100%" height="100%" fill="#F5F7FA"/></svg>'
|
|
|
|
|
|
- async function generateThumbnail(imagePath: string, maxW = 400, maxH = 400) {
|
|
|
+ function isGenerating(key: string) {
|
|
|
+ return generatingMap.has(key)
|
|
|
+ }
|
|
|
+
|
|
|
+ function startGenerating(key: string) {
|
|
|
+ generatingMap.set(key, new AbortController())
|
|
|
+ }
|
|
|
+
|
|
|
+ function stopGenerating(key: string) {
|
|
|
+ generatingMap.get(key)?.abort()
|
|
|
+ generatingMap.delete(key)
|
|
|
+ }
|
|
|
+
|
|
|
+ async function generateThumbnail(imagePath: string, actionId: string, maxW = 400, maxH = 400) {
|
|
|
if (!imagePath) return
|
|
|
- if (thumbnailMap.value[imagePath]) return
|
|
|
- if (generatingSet.has(imagePath)) return
|
|
|
- generatingSet.add(imagePath)
|
|
|
+ const key = `${imagePath}::${actionId}`
|
|
|
+ if (thumbnailMap.value[imagePath] !== undefined) return
|
|
|
+ if (isGenerating(key)) return
|
|
|
+ startGenerating(key)
|
|
|
try {
|
|
|
const src = getFilePath(imagePath)
|
|
|
const img = new Image()
|
|
|
@@ -22,7 +36,7 @@ export function useThumbnails(getFilePath: (p: string) => string) {
|
|
|
const w = img.naturalWidth || img.width
|
|
|
const h = img.naturalHeight || img.height
|
|
|
if (!w || !h) {
|
|
|
- thumbnailMap.value[imagePath] = ''
|
|
|
+ thumbnailMap.value[imagePath] = null
|
|
|
return
|
|
|
}
|
|
|
const ratio = Math.min(1, maxW / w, maxH / h)
|
|
|
@@ -33,7 +47,7 @@ export function useThumbnails(getFilePath: (p: string) => string) {
|
|
|
canvas.height = th
|
|
|
const ctx = canvas.getContext('2d')
|
|
|
if (!ctx) {
|
|
|
- thumbnailMap.value[imagePath] = ''
|
|
|
+ thumbnailMap.value[imagePath] = null
|
|
|
return
|
|
|
}
|
|
|
ctx.drawImage(img, 0, 0, tw, th)
|
|
|
@@ -41,9 +55,9 @@ export function useThumbnails(getFilePath: (p: string) => string) {
|
|
|
thumbnailMap.value[imagePath] = dataUrl
|
|
|
} catch (e) {
|
|
|
console.warn('generateThumbnail error', e)
|
|
|
- thumbnailMap.value[imagePath] = ''
|
|
|
+ thumbnailMap.value[imagePath] = null
|
|
|
} finally {
|
|
|
- generatingSet.delete(imagePath)
|
|
|
+ stopGenerating(key)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -63,7 +77,10 @@ export function useThumbnails(getFilePath: (p: string) => string) {
|
|
|
if (item && Array.isArray(item.items)) {
|
|
|
item.items.forEach((img: any) => {
|
|
|
const p = img?.PhotoRecord?.image_path
|
|
|
- if (p && !thumbnailMap.value[p]) generateThumbnail(p, 400, 400)
|
|
|
+ const actionId = img?.action_id || img?.action_name || ''
|
|
|
+ if (p && thumbnailMap.value[p] === undefined) {
|
|
|
+ generateThumbnail(p, actionId, 400, 400)
|
|
|
+ }
|
|
|
})
|
|
|
}
|
|
|
observer?.unobserve(el)
|
|
|
@@ -91,6 +108,8 @@ export function useThumbnails(getFilePath: (p: string) => string) {
|
|
|
observer.disconnect()
|
|
|
observer = null
|
|
|
}
|
|
|
+ generatingMap.forEach((ctrl) => ctrl.abort())
|
|
|
+ generatingMap.clear()
|
|
|
}
|
|
|
|
|
|
onBeforeUnmount(() => {
|