图片失败后继续加载阅读页并支持单图重试
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {computed, ref, watch} from "vue";
|
import {computed, ref, watch, onBeforeUnmount} from "vue";
|
||||||
|
import {startImages, settleImage, retryImage} from "../utils/progressiveImages.js";
|
||||||
import store from "../store/index.js";
|
import store from "../store/index.js";
|
||||||
let onlineReadingScrollbar = ref()
|
let onlineReadingScrollbar = ref()
|
||||||
let links = ref()
|
let links = ref()
|
||||||
@@ -7,13 +8,12 @@ let index = ref(0) //下标
|
|||||||
let page = ref(0) //页数 下标+1=页数 用于跳转
|
let page = ref(0) //页数 下标+1=页数 用于跳转
|
||||||
|
|
||||||
let imagesForLoading = ref([])
|
let imagesForLoading = ref([])
|
||||||
let loadIndex = ref(0)
|
|
||||||
let isReading = ref(false)
|
let isReading = ref(false)
|
||||||
|
|
||||||
let max = ref(0)
|
let max = ref(0)
|
||||||
let current_page = 0
|
let current_page = 0
|
||||||
let lengthPerPage = computed(() => {
|
let lengthPerPage = computed(() => {
|
||||||
return store.state.lengthPerPage
|
return Math.max(1, Number(store.state.lengthPerPage) || 10)
|
||||||
})
|
})
|
||||||
let readingGallery = computed(() => {
|
let readingGallery = computed(() => {
|
||||||
return store.state.readingGallery
|
return store.state.readingGallery
|
||||||
@@ -36,28 +36,34 @@ function alterPage(){
|
|||||||
}
|
}
|
||||||
index.value = 0
|
index.value = 0
|
||||||
page.value = 1
|
page.value = 1
|
||||||
loadIndex.value = 0
|
imagesForLoading.value = startImages(links.value)
|
||||||
imagesForLoading.value.splice(0)
|
|
||||||
loadImage()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//跳转到对应页数
|
//跳转到对应页数
|
||||||
function jump(targetIndex){
|
function jump(targetIndex){
|
||||||
|
if(targetIndex < 0 || targetIndex >= Math.ceil(readingGallery.value.images.length / lengthPerPage.value)) return
|
||||||
|
clearPageSwitchTimer()
|
||||||
links.value = readingGallery.value.images.slice(targetIndex * lengthPerPage.value, (targetIndex + 1) * lengthPerPage.value)
|
links.value = readingGallery.value.images.slice(targetIndex * lengthPerPage.value, (targetIndex + 1) * lengthPerPage.value)
|
||||||
index.value = targetIndex
|
index.value = targetIndex
|
||||||
page.value = targetIndex + 1
|
page.value = targetIndex + 1
|
||||||
imagesForLoading.value.splice(0)
|
imagesForLoading.value = startImages(links.value)
|
||||||
loadIndex.value = 0
|
|
||||||
loadImage()
|
|
||||||
onlineReadingScrollbar.value.setScrollTop(0)
|
onlineReadingScrollbar.value.setScrollTop(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadImage(){
|
function imageResult(item, failed = false){
|
||||||
if(loadIndex.value < links.value.length)
|
settleImage(imagesForLoading.value, links.value, item, failed)
|
||||||
imagesForLoading.value.push(links.value[loadIndex.value++])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pageSwitchTimer
|
||||||
|
function clearPageSwitchTimer(){
|
||||||
|
clearInterval(pageSwitchTimer)
|
||||||
|
pageSwitchTimer = undefined
|
||||||
|
}
|
||||||
|
onBeforeUnmount(clearPageSwitchTimer)
|
||||||
|
|
||||||
function closeDialog(){
|
function closeDialog(){
|
||||||
|
clearPageSwitchTimer()
|
||||||
|
imagesForLoading.value = []
|
||||||
onlineReadingScrollbar.value.setScrollTop(0)
|
onlineReadingScrollbar.value.setScrollTop(0)
|
||||||
isReading.value = false
|
isReading.value = false
|
||||||
store.commit("_closeReader")
|
store.commit("_closeReader")
|
||||||
@@ -70,16 +76,16 @@ function switch_page(target_page){
|
|||||||
jump(index.value - 1)
|
jump(index.value - 1)
|
||||||
onlineReadingScrollbar.value.setScrollTop(onlineReadingScrollbar.value.wrapRef.scrollHeight)
|
onlineReadingScrollbar.value.setScrollTop(onlineReadingScrollbar.value.wrapRef.scrollHeight)
|
||||||
let top = 0
|
let top = 0
|
||||||
let timer = setInterval(() => {
|
pageSwitchTimer = setInterval(() => {
|
||||||
if(onlineReadingScrollbar.value.scrollTop === top){
|
if(onlineReadingScrollbar.value.scrollTop === top){
|
||||||
clearInterval(timer)
|
clearPageSwitchTimer()
|
||||||
document.querySelector("div.el-scrollbar__wrap.el-scrollbar__wrap--hidden-default > div > div:last-child > img").click()
|
document.querySelector("div.el-scrollbar__wrap.el-scrollbar__wrap--hidden-default > div > div:last-child > img").click()
|
||||||
}
|
}
|
||||||
top = onlineReadingScrollbar.value.scrollTop
|
top = onlineReadingScrollbar.value.scrollTop
|
||||||
onlineReadingScrollbar.value.setScrollTop(onlineReadingScrollbar.value.wrapRef.scrollHeight)
|
onlineReadingScrollbar.value.setScrollTop(onlineReadingScrollbar.value.wrapRef.scrollHeight)
|
||||||
}, 100)
|
}, 100)
|
||||||
//下一页
|
//下一页
|
||||||
}else if(target_page === 0 && current_page === lengthPerPage.value - 1 && index.value < max.value){
|
}else if(target_page === 0 && current_page === lengthPerPage.value - 1 && index.value < max.value - 1){
|
||||||
jump(index.value + 1)
|
jump(index.value + 1)
|
||||||
current_page = 0
|
current_page = 0
|
||||||
document.querySelector("div.el-scrollbar__wrap.el-scrollbar__wrap--hidden-default > div > div:nth-child(1) > img").click()
|
document.querySelector("div.el-scrollbar__wrap.el-scrollbar__wrap--hidden-default > div > div:nth-child(1) > img").click()
|
||||||
@@ -105,9 +111,10 @@ function set_current_page(page){
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-scrollbar height="75vh" ref="onlineReadingScrollbar">
|
<el-scrollbar height="75vh" ref="onlineReadingScrollbar">
|
||||||
<div v-for="(link, i) in imagesForLoading" style="display: inline-block; text-align: center; min-height: 300px">
|
<div v-for="(item, i) in imagesForLoading" :key="item.url + '-' + item.attempt" style="display: inline-block; text-align: center; min-height: 300px">
|
||||||
<el-image :src="link" :style="{'width': '66vw', 'background-color': 'gary'}"
|
<el-image :src="item.src" :style="{'width': '66vw', 'background-color': 'gary'}"
|
||||||
:preview-src-list="links" :initial-index="i" @switch="switch_page" @show="set_current_page(i)" loading="lazy" @load="loadImage"/><br>
|
:preview-src-list="links" :initial-index="i" @switch="switch_page" @show="set_current_page(i)" loading="lazy" @load="imageResult(item)" @error="imageResult(item, true)"/>
|
||||||
|
<div v-if="item.failed" role="alert">图片加载失败 <el-button size="small" @click="retryImage(item)">重试</el-button></div><br>
|
||||||
{{lengthPerPage * index + i + 1}}
|
{{lengthPerPage * index + i + 1}}
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
function imageItem(url, index) {
|
||||||
|
return {url, src: url, index, attempt: 0, failed: false, settled: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startImages(urls) {
|
||||||
|
return urls.length ? [imageItem(urls[0], 0)] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
export function settleImage(items, urls, item, failed = false) {
|
||||||
|
// Ignore events from images removed by a page change or dialog close.
|
||||||
|
if (!items.includes(item)) return
|
||||||
|
item.failed = failed
|
||||||
|
if (item.settled) return
|
||||||
|
item.settled = true
|
||||||
|
if (items.length < urls.length)
|
||||||
|
items.push(imageItem(urls[items.length], items.length))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function retryImage(item) {
|
||||||
|
item.failed = false
|
||||||
|
item.attempt++
|
||||||
|
item.src = item.url + (item.url.includes('?') ? '&' : '?') + 'retry=' + item.attempt
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import test from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import {startImages, settleImage, retryImage} from '../src/utils/progressiveImages.js'
|
||||||
|
|
||||||
|
test('a failed image does not prevent later pages from loading', () => {
|
||||||
|
const urls = ['first', 'second', 'third']
|
||||||
|
const items = startImages(urls)
|
||||||
|
settleImage(items, urls, items[0], true)
|
||||||
|
assert.equal(items[0].failed, true)
|
||||||
|
assert.equal(items[1].url, 'second')
|
||||||
|
settleImage(items, urls, items[1])
|
||||||
|
assert.equal(items[2].url, 'third')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('retry does not append a duplicate page and bypasses a failed URL cache', () => {
|
||||||
|
const urls = ['first?gid=1', 'second', 'third']
|
||||||
|
const items = startImages(urls)
|
||||||
|
settleImage(items, urls, items[0], true)
|
||||||
|
retryImage(items[0])
|
||||||
|
assert.equal(items[0].src, 'first?gid=1&retry=1')
|
||||||
|
settleImage(items, urls, items[0])
|
||||||
|
assert.equal(items.length, 2)
|
||||||
|
assert.equal(items[0].failed, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('late image events cannot append content after changing pages', () => {
|
||||||
|
const old = startImages(['old'])[0]
|
||||||
|
const urls = ['new', 'next']
|
||||||
|
const items = startImages(urls)
|
||||||
|
settleImage(items, urls, old)
|
||||||
|
assert.equal(items.length, 1)
|
||||||
|
assert.deepEqual(startImages([]), [])
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user