优化PptxRendererPreview.vue
This commit is contained in:
parent
c36357547f
commit
e7d64e2842
@ -3,13 +3,13 @@
|
||||
<!-- 渲染区 -->
|
||||
<div class="pptx-renderer-body" ref="containerRef" />
|
||||
|
||||
<!-- 导航栏 -->
|
||||
<div v-if="!loading && !hasError" class="pptx-nav">
|
||||
<button class="nav-btn" :disabled="currentSlide <= 0" @click="goPrev" :title="t('Previous slide')">
|
||||
<!-- 导航栏(始终渲染,避免 body 高度跳变) -->
|
||||
<div class="pptx-nav">
|
||||
<button class="nav-btn" :disabled="!viewer || currentSlide <= 0" @click="goPrev" :title="t('Previous slide')">
|
||||
<Icon icon="tabler:chevron-left" :size="18" />
|
||||
</button>
|
||||
<span class="nav-indicator">{{ currentSlide + 1 }} / {{ totalSlides }}</span>
|
||||
<button class="nav-btn" :disabled="currentSlide >= totalSlides - 1" @click="goNext" :title="t('Next slide')">
|
||||
<span class="nav-indicator">{{ viewer ? (currentSlide + 1) + ' / ' + totalSlides : '—' }}</span>
|
||||
<button class="nav-btn" :disabled="!viewer || currentSlide >= totalSlides - 1" @click="goNext" :title="t('Next slide')">
|
||||
<Icon icon="tabler:chevron-right" :size="18" />
|
||||
</button>
|
||||
</div>
|
||||
@ -50,6 +50,8 @@ const currentSlide = ref(0)
|
||||
const totalSlides = ref(0)
|
||||
|
||||
let viewer: PptxViewer | null = null
|
||||
let resizeObserver: ResizeObserver | null = null
|
||||
let resizeTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function resolveUrl(url: string): string {
|
||||
if (!url) return ''
|
||||
@ -57,6 +59,44 @@ function resolveUrl(url: string): string {
|
||||
return window.location.origin + url
|
||||
}
|
||||
|
||||
/**
|
||||
* 库 fitMode:'contain' 自动按宽度缩放(宽度变化时自动重渲染),
|
||||
* 但不考虑高度约束。当高度溢出时,用 setZoom 缩小使宽高都适应。
|
||||
* zoom=100 表示库默认(按宽度 fit),<100 表示需进一步缩小以适应高度。
|
||||
*/
|
||||
function calcFitZoom(): number {
|
||||
if (!viewer || !containerRef.value) return 100
|
||||
const cW = containerRef.value.clientWidth
|
||||
const cH = containerRef.value.clientHeight
|
||||
const sW = viewer.slideWidth
|
||||
const sH = viewer.slideHeight
|
||||
if (!cW || !cH || !sW || !sH) return 100
|
||||
|
||||
// 库 contain 模式基础 scale = cW / sW(按宽度 fit)
|
||||
// 检查按此 scale 渲染后高度是否溢出
|
||||
const scaleByW = cW / sW
|
||||
const scaleByH = cH / sH
|
||||
if (scaleByH < scaleByW) {
|
||||
// 高度是瓶颈,需缩小到按高度 fit
|
||||
return Math.round((scaleByH / scaleByW) * 100)
|
||||
}
|
||||
return 100
|
||||
}
|
||||
|
||||
/** 容器尺寸变化时重算 zoom(库已自动按宽度 fit,只需修正高度溢出) */
|
||||
async function handleResize() {
|
||||
if (!viewer) return
|
||||
const zoom = calcFitZoom()
|
||||
if (zoom !== viewer.zoomPercent) {
|
||||
await viewer.setZoom(zoom)
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleResize() {
|
||||
if (resizeTimer) clearTimeout(resizeTimer)
|
||||
resizeTimer = setTimeout(() => handleResize(), 200)
|
||||
}
|
||||
|
||||
function goPrev() {
|
||||
if (!viewer || currentSlide.value <= 0) return
|
||||
viewer.goToSlide(currentSlide.value - 1)
|
||||
@ -111,7 +151,8 @@ async function loadPresentation(fileUrl: string) {
|
||||
|
||||
if (!containerRef.value) return
|
||||
|
||||
// slide 模式:一次渲染一页,配合自定义导航
|
||||
// fitMode:'contain' 库自动按宽度 fit(宽度变化时自动重渲染),
|
||||
// 高度溢出由 calcFitZoom + setZoom 修正
|
||||
viewer = await PptxViewer.open(buffer, containerRef.value, {
|
||||
renderMode: 'slide',
|
||||
zipLimits: RECOMMENDED_ZIP_LIMITS,
|
||||
@ -124,6 +165,12 @@ async function loadPresentation(fileUrl: string) {
|
||||
totalSlides.value = viewer.slideCount
|
||||
currentSlide.value = viewer.currentSlideIndex
|
||||
|
||||
// 初始修正高度溢出
|
||||
const zoom = calcFitZoom()
|
||||
if (zoom < 100) {
|
||||
await viewer.setZoom(zoom)
|
||||
}
|
||||
|
||||
loading.value = false
|
||||
emit('loaded')
|
||||
} catch (e) {
|
||||
@ -139,12 +186,20 @@ watch(() => props.fileUrl, (newUrl) => {
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(() => {
|
||||
// 自动聚焦以接收键盘事件
|
||||
const el = containerRef.value?.closest('.pptx-renderer-preview') as HTMLElement
|
||||
el?.focus()
|
||||
|
||||
// 容器尺寸变化时重算 zoom
|
||||
if (containerRef.value) {
|
||||
resizeObserver = new ResizeObserver(() => scheduleResize())
|
||||
resizeObserver.observe(containerRef.value)
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (resizeTimer) clearTimeout(resizeTimer)
|
||||
resizeObserver?.disconnect()
|
||||
resizeObserver = null
|
||||
viewer?.destroy()
|
||||
viewer = null
|
||||
})
|
||||
@ -168,6 +223,9 @@ onBeforeUnmount(() => {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ===== 导航栏 ===== */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user