图片预览手机适配 + 正文图统一接入 lightbox

- lightbox:打开锁背景滚动(手机上背景不再跟着滑)、手机滑动左右翻页、
  dvh 跟随全面屏可视高度、关闭钮/角标走 safe-area、图片全宽无圆角
- 全局 capture 委托:时间线/详情页 v-html 正文里的图统一可点开预览,
  capture+stop 压住整卡跳转;thumbifyHtml 把原图地址留在 data-fullsrc,
  预览用原图而非 480w 缩略图;链接卡缩略图不劫持(点击仍是打开链接)
This commit is contained in:
Sakurasan
2026-09-29 01:29:19 +08:00
parent c2d5e9db89
commit ce07f4a347
3 changed files with 87 additions and 16 deletions
+55 -13
View File
@@ -3,27 +3,54 @@ import { onBeforeUnmount, watch } from 'vue'
import { lightboxState as st, closeLightbox, stepLightbox } from '../lightbox'
// 图片预览层:App.vue 全局挂一次。铺满全屏的暗底 + 居中大图,
// 多图时左右箭头 / 方向键切换,角标显示第几张;Esc / 点空白收起。
// 多图时左右箭头 / 方向键 / 手机滑动切换,角标显示第几张;Esc / 点空白收起。
function onKey(e) {
if (e.key === 'Escape') closeLightbox()
if (e.key === 'ArrowLeft') stepLightbox(-1)
if (e.key === 'ArrowRight') stepLightbox(1)
}
// 打开时锁住背景滚动(手机上尤其明显:否则手指滑动会带着页面一起动)
let prevOverflow = ''
watch(
() => st.open,
(v) => {
if (v) window.addEventListener('keydown', onKey)
else window.removeEventListener('keydown', onKey)
if (v) {
window.addEventListener('keydown', onKey)
prevOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
} else {
window.removeEventListener('keydown', onKey)
document.body.style.overflow = prevOverflow
}
}
)
onBeforeUnmount(() => window.removeEventListener('keydown', onKey))
onBeforeUnmount(() => {
window.removeEventListener('keydown', onKey)
document.body.style.overflow = prevOverflow
})
// 手机滑动翻页:横向位移超 44px 且大于纵向位移才算一次滑动
let touchX = 0
let touchY = 0
function onTouchStart(e) {
touchX = e.touches[0].clientX
touchY = e.touches[0].clientY
}
function onTouchEnd(e) {
const dx = e.changedTouches[0].clientX - touchX
const dy = e.changedTouches[0].clientY - touchY
if (Math.abs(dx) > 44 && Math.abs(dx) > Math.abs(dy) * 1.4 && st.list.length > 1) {
stepLightbox(dx < 0 ? 1 : -1)
}
}
</script>
<template>
<Teleport to="body">
<Transition name="lb">
<div v-if="st.open" class="lb-overlay" @click="closeLightbox">
<div v-if="st.open" class="lb-overlay" @click="closeLightbox" @touchstart.passive="onTouchStart" @touchend.passive="onTouchEnd">
<img
class="lb-img"
:src="st.list[st.index]"
@@ -124,28 +151,43 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKey))
}
@media (max-width: 640px) {
.lb-overlay {
/* 全面屏 URL 栏收展时 100vh 会偏大,dvh 跟随可视高度 */
align-items: center;
}
.lb-img {
max-width: 96vw;
max-height: 74vh;
max-width: 100vw;
max-height: 74dvh;
border-radius: 0;
}
.lb-nav {
width: 38px;
height: 38px;
width: 40px;
height: 40px;
font-size: 22px;
/* 悬浮在图片两侧半透明,可见但不抢戏 */
background: rgba(255, 255, 255, 0.12);
}
.lb-nav.prev {
left: 10px;
left: calc(8px + env(safe-area-inset-left));
}
.lb-nav.next {
right: 10px;
right: calc(8px + env(safe-area-inset-right));
}
.lb-close {
top: 10px;
right: 10px;
top: calc(10px + env(safe-area-inset-top));
right: calc(10px + env(safe-area-inset-right));
width: 42px;
height: 42px;
}
.lb-count {
bottom: calc(14px + env(safe-area-inset-bottom));
font-size: 12px;
}
}