@agilehub/vue-pdf-flipbook 0.1.1 → 1.0.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/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  基于 Vue 3、PDF.js 和 PageFlip 的 PDF 翻页组件。核心只负责 PDF 加载、页面渲染、单页 / 双页布局和点击、拖拽翻页,内置阅读区两侧翻页按钮和可选的底部缩略图列表,不包含工具栏、缩放、全屏、快捷键或加载提示 UI,也不依赖 UI 框架。
4
4
 
5
+ ## 灵感来源
6
+ * [The Online Flipbook Maker](https://www.paperturn.com/)
7
+ * [turnjs](https://www.turnjs.cn/)
8
+
5
9
  ## 安装与使用
6
10
 
7
11
  ```bash
@@ -52,6 +56,12 @@ const state = ref<PdfFlipbookState>()
52
56
 
53
57
  双页模式在空间不足时自动显示单页。`visiblePages` 反映实际显示页,而 `mode` 表示选择的模式。
54
58
 
59
+ 双页模式两侧显示纸张层叠边缘,厚度按当前页组之前和之后的页数分配:前半本右厚左薄,后半本左厚右薄,翻页或跳页后平滑变化。每侧最多 16px,短文档按页数减薄,单页模式隐藏。布局在双页状态下每侧预留 18px,防止页叠被裁切;封面及封底的页叠随闭合位置移动。
60
+
61
+ 双页模式采用书本封面布局:第 1 页单独居中显示,向后翻动展开为第 2–3 页,之后依次为第 4–5 页、第 6–7 页。返回第 1 页(上一页、缩略图或 `goToPage(1)`)会合上封面。开合使用翻页引擎动画,纸张尺寸保持一致;封面不显示中缝阴影。偶数总页数的最后一页作为单独封底显示。单页模式与窄屏下仍逐页阅读。`visiblePages` 分组相应变为 `[1]`、`[2, 3]`、`[4, 5]`,`page` 在双页内页表示该组左页。
62
+
63
+ 默认 Worker 以 data URL 内嵌在库中,与主线程 PDF.js 一起构建,不依赖业务项目的 `/assets` 路径或资源复制配置,支持 Vite 依赖预打包和子路径部署。代价是 JS 文件包含 Worker 内容。若业务 CSP 限制 data/blob 模块,可通过 `workerSrc` 指定同版本的外部 Worker 地址,并按业务 CSP 规则提供资源。
64
+
55
65
  ### 自适应父容器
56
66
 
57
67
  父容器设置明确宽高后,组件默认占满父容器。书页按 PDF 比例等比缩放:宽度受限时铺满宽度,高度受限时铺满高度,另一方向居中留白,不裁剪或拉伸。双页按两页合计宽高比计算;可用宽度小于 520px 时自动退回单页。容器尺寸变化、全屏及缩略图显隐会自动重新计算。
@@ -64,7 +74,7 @@ const state = ref<PdfFlipbookState>()
64
74
 
65
75
  `height` 默认值已从 `720` 改为 `'100%'`,因此父容器必须有明确高度(使用百分比时,其祖先也需要确定高度)。需要原来的固定高度时传入 `:height="720"`。已取消单页 500px、双页 1120px 及 PDF 原始尺寸的显示上限;此处调整显示尺寸,Canvas 渲染分辨率策略保持不变。
66
76
 
67
- 鼠标移入阅读区时,左侧显示“上一页”、右侧显示“下一页”按钮。按钮复用现有翻页与预加载流程,首页/末页以及目标页加载期间禁用相应操作。键盘聚焦时也会显示按钮;无悬停能力的触屏设备上保持显示。缩略图列表不占用按钮的定位区域。
77
+ 鼠标移入阅读区时,左侧显示“上一页”、右侧显示“下一页”按钮。双页模式首页只显示“下一页”,显示最后一页的页面组只显示“上一页”;封面打开或合上的动画过程中同时隐藏两个按钮,动画结束后再按当前页面恢复,避免按钮先于书本开合换位。单页模式在首尾保留对应禁用按钮。按钮复用现有翻页与预加载流程,目标页加载期间禁用相应操作。键盘聚焦时也会显示按钮;无悬停能力的触屏设备上保持显示。缩略图列表不占用按钮的定位区域。
68
78
 
69
79
  ## 事件
70
80
 
@@ -88,7 +98,7 @@ const state = ref<PdfFlipbookState>()
88
98
  | 方法 | 说明 |
89
99
  | --- | --- |
90
100
  | `next()` / `previous()` | 翻到下一组 / 上一组页面 |
91
- | `goToPage(page)` | 先准备目标及动画必需页面再启动翻页,返回 `Promise<void>`(不等待动画结束);页码从 1 开始 |
101
+ | `goToPage(page)` | 跳转到指定页,参数必须为大于 0 且不超过总页数的整数;返回 `Promise<void>`(不等待动画结束) |
92
102
  | `setMode(mode)` | 切换单 / 双页,返回 `Promise<void>` |
93
103
  | `reload()` | 重新加载当前 URL,返回 `Promise<void>`,错误通过事件报告 |
94
104
  | `getState()` | 获取当前状态快照 |
@@ -96,6 +106,39 @@ const state = ref<PdfFlipbookState>()
96
106
 
97
107
  文档由组件管理,外部不要调用 `destroy()` 或 `cleanup()`。URL 变化、重新加载或组件卸载后应丢弃旧引用;Vue 中使用 `shallowRef` 保存代理对象。
98
108
 
109
+ 外部项目通过组件 ref 调用跳页,建议在 `loaded` 事件之后调用:
110
+
111
+ ```vue
112
+ <script setup lang="ts">
113
+ import { ref } from 'vue'
114
+ import { VuePdfFlipbook } from '@agilehub/vue-pdf-flipbook'
115
+ import type { PdfFlipbookExpose } from '@agilehub/vue-pdf-flipbook'
116
+ import '@agilehub/vue-pdf-flipbook/style.css'
117
+
118
+ const reader = ref<PdfFlipbookExpose>()
119
+ const targetPage = ref(1)
120
+ const jumpError = ref('')
121
+
122
+ async function jump() {
123
+ jumpError.value = ''
124
+ await reader.value?.goToPage(targetPage.value)
125
+ }
126
+
127
+ function onError(error: unknown) {
128
+ jumpError.value = error instanceof Error ? error.message : String(error)
129
+ }
130
+ </script>
131
+
132
+ <template>
133
+ <VuePdfFlipbook ref="reader" url="https://example.com/catalog.pdf" :height="720" @error="onError" />
134
+ <input v-model.number="targetPage" type="number" min="1" step="1" />
135
+ <button @click="jump">跳转</button>
136
+ <p v-if="jumpError">{{ jumpError }}</p>
137
+ </template>
138
+ ```
139
+
140
+ 组件会在运行时严格校验:`0`、负数、小数、`NaN`、`Infinity`、字符串和超过总页数的值均不跳转,也不会取消已有的有效跳页请求,并通过 `error` 事件报告 `RangeError`(Promise 正常结束)。不再将非法值自动取整或调整到首尾页。双页模式显示包含目标页的那一组页面;目标页已经可见时无需翻动。
141
+
99
142
  ## 大文件按需预览
100
143
 
101
144
  默认只渲染当前可见页面,以及前后各 5 页。单页模式稳定状态最多 11 个页面 Canvas,双页模式最多 12 个;首页、末页会按文档边界裁剪。翻页后移出窗口的页面取消渲染并释放 Canvas 位图,翻回时重新渲染。所有页码保留轻量 DOM 占位以维持翻页引擎索引。
@@ -128,6 +171,8 @@ PDF 的页与字节段不是一一对应:解析索引、字体及共享资源
128
171
 
129
172
  未提供插槽时,默认列表以半透明浮层覆盖实际书页底部,宽度跟随书页,不占用布局高度,显示或隐藏不会缩小书页;单项宽度为 30px,预览图高度按 PDF 宽高比自适应。提供自定义插槽时保留组件底部列表布局和原有单项尺寸。每次打开会将当前页缩略图居中定位。默认内容只渲染当前页窗口,点击任意页码仍可按需跳转。
130
173
 
174
+ 默认缩略图支持鼠标左键按住列表内容左右拖动滚动,也可使用原生滚动条。横向移动达到 5px 后视为拖拽,松开不会触发跳页;普通点击和键盘操作仍可选择页面。缩略图区域会隔离鼠标和触摸事件,移动或拖动到书页右下角时不会触发折页预览或翻页动画。
175
+
131
176
  `thumbnail` 作用域插槽自定义每一项的内容,外层按钮、选中状态及跳页行为由组件提供,请勿在插槽内嵌套按钮或链接。参数类型 `PdfThumbnailSlotProps` 已导出:`page`(页码)、`pdf`(借用的 PDF 文档)、`isActive`(当前可见页)、`shouldRender`(位于预览窗口且对应正文页已完成渲染)。自定义 Canvas 时请使用 `shouldRender`,避免加载全书。
132
177
 
133
178
  ```vue
@@ -198,7 +243,3 @@ npm run test:package
198
243
  ## License
199
244
 
200
245
  MIT
201
-
202
-
203
-
204
-
@@ -7,9 +7,10 @@ import type { ReaderMode } from './types';
7
7
  * @param pageWidth PDF 原始页宽。
8
8
  * @param pageHeight PDF 原始页高。
9
9
  * @param mode 用户选择的模式;宽度不足 520px 时双页退为单页。
10
+ * @param edgeSpace 双页模式每侧为纸叠预留的空间,不改变单双页断点。
10
11
  * @returns 包含书页容器 width、height 和实际 orientation 的对象。
11
12
  */
12
- export declare function fitBook(width: number, height: number, pageWidth: number, pageHeight: number, mode: ReaderMode): {
13
+ export declare function fitBook(width: number, height: number, pageWidth: number, pageHeight: number, mode: ReaderMode, edgeSpace?: number): {
13
14
  width: number;
14
15
  height: number;
15
16
  orientation: ReaderMode;
@@ -22,6 +22,6 @@ export declare function usePageFlip(flipbookElement: Ref<HTMLElement | undefined
22
22
  isReady: () => boolean;
23
23
  update: () => void;
24
24
  /** 导航准备完成后调用;pageIndex 为零基目标索引,corner 为起翻页角;返回 void。 */
25
- flip: (pageIndex: number, corner: "top" | "bottom") => void | undefined;
25
+ flip: (pageIndex: number, corner: "top" | "bottom") => void;
26
26
  };
27
27
  export {};
@@ -23,6 +23,16 @@ export declare function usePdfFlipbook(props: ResolvedFlipbookProps, emit: Flipb
23
23
  width: string;
24
24
  height: string;
25
25
  }>;
26
+ coverClass: import("vue").ComputedRef<"" | "vpf-book-shell--front-cover" | "vpf-book-shell--back-cover">;
27
+ showPageNavigation: import("vue").ComputedRef<boolean>;
28
+ pageEdgesStyle: import("vue").ComputedRef<{
29
+ '--vpf-edges-opacity': string;
30
+ '--vpf-edges-transition': string;
31
+ '--vpf-edge-left': string;
32
+ '--vpf-edge-right': string;
33
+ '--vpf-edge-left-visibility': string;
34
+ '--vpf-edge-right-visibility': string;
35
+ }>;
26
36
  pdf: Readonly<import("vue").ShallowRef<import("pdfjs-dist/types/src/display/api").PDFDocumentProxy | undefined, import("pdfjs-dist/types/src/display/api").PDFDocumentProxy | undefined>>;
27
37
  loading: Readonly<import("vue").Ref<boolean, boolean>>;
28
38
  pageCount: Readonly<import("vue").Ref<number, number>>;
@@ -0,0 +1,16 @@
1
+ import type { ReaderMode } from './types';
2
+ export declare const pageEdgeSpace = 18;
3
+ /** 准备页面时保留原页叠,真正开始翻动后与目标页同步;零厚度不保留退场动画。 */
4
+ export declare function getPageEdgesStyle(page: number, pages: number, mode: ReaderMode, target: number | undefined, state: string): {
5
+ '--vpf-edges-opacity': string;
6
+ '--vpf-edges-transition': string;
7
+ '--vpf-edge-left': string;
8
+ '--vpf-edge-right': string;
9
+ '--vpf-edge-left-visibility': string;
10
+ '--vpf-edge-right-visibility': string;
11
+ };
12
+ /** 按当前页组前后的页数分配纸叠厚度;少页文档不会显示厚书效果。 */
13
+ export declare function getPageEdges(page: number, pages: number, mode: ReaderMode): {
14
+ left: number;
15
+ right: number;
16
+ };
package/dist/types.d.ts CHANGED
@@ -46,7 +46,7 @@ export interface PdfFlipbookExpose {
46
46
  /**
47
47
  * 请求跳页,内部依次执行防抖、必需页准备和翻页。
48
48
  * 调用逻辑:外部页码输入、缩略图或其他导航控件通过组件 ref 调用。
49
- * @param page 一基目标页码,自动取整并限制范围。
49
+ * @param page 大于 0 且不超过总页数的整数;非法值不跳页,通过 error 事件报告 RangeError。
50
50
  * @returns Promise<void>;动画启动或请求取消/忽略后完成,不等待动画结束。
51
51
  */
52
52
  goToPage: (page: number) => Promise<void>;
@@ -1,2 +1,2 @@
1
- .thumbnail-container[data-v-025f840c]{width:100%;position:relative}.page-num[data-v-025f840c]{pointer-events:none;z-index:9;background:#b5b5b540;padding:2px 4px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.vpf-reader,.vpf-reader *{box-sizing:border-box}.vpf-reader{width:100%;height:var(--vpf-height,100%);background:var(--vpf-background,transparent);flex-direction:column;min-width:0;min-height:0;display:flex;overflow:hidden}.vpf-viewport{flex:1;width:100%;min-height:0;position:relative;overflow:hidden}.vpf-page-nav{z-index:10;color:#fff;cursor:pointer;opacity:0;pointer-events:none;background:#141c1bbf;border:1px solid #ffffff40;border-radius:8px;place-items:center;width:44px;height:56px;padding:10px;transition:opacity .15s,background .15s;display:grid;position:absolute;top:50%;transform:translateY(-50%)}.vpf-page-nav--previous{left:12px}.vpf-page-nav--next{right:12px}.vpf-page-nav svg{fill:none;stroke:currentColor;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round;width:24px;height:24px}.vpf-viewport:hover .vpf-page-nav,.vpf-viewport:focus-within .vpf-page-nav{opacity:1;pointer-events:auto}.vpf-page-nav:hover:not(:disabled){background:#141c1bf2}.vpf-page-nav:focus-visible{outline-offset:2px;outline:2px solid #fff}.vpf-viewport:hover .vpf-page-nav:disabled,.vpf-viewport:focus-within .vpf-page-nav:disabled{opacity:.35;cursor:default}@media (hover:none){.vpf-page-nav{opacity:1;pointer-events:auto}.vpf-page-nav:disabled{opacity:.35}}@media (prefers-reduced-motion:reduce){.vpf-page-nav{transition:none}}.vpf-thumbnails{background:#edf0eb;flex:none;gap:12px;max-height:35%;padding:10px;display:flex;overflow:auto}.vpf-thumbnails,.vpf-default-thumbnails{scrollbar-width:thin;scrollbar-color:#a5aea8 transparent}.vpf-thumbnails:hover,.vpf-thumbnails:focus-within,.vpf-default-thumbnails:hover{scrollbar-color:#7d8b82 transparent}@supports selector(::-webkit-scrollbar){.vpf-thumbnails,.vpf-default-thumbnails,.vpf-thumbnails:hover,.vpf-thumbnails:focus-within,.vpf-default-thumbnails:hover{scrollbar-width:auto;scrollbar-color:auto}.vpf-thumbnails::-webkit-scrollbar{width:8px;height:8px}.vpf-default-thumbnails::-webkit-scrollbar{width:8px;height:8px}.vpf-thumbnails::-webkit-scrollbar-track{background:0 0}.vpf-default-thumbnails::-webkit-scrollbar-track{background:0 0}.vpf-thumbnails::-webkit-scrollbar-thumb{background:#a5aea8 padding-box padding-box;border:2px solid #0000;border-radius:999px;min-width:32px}.vpf-default-thumbnails::-webkit-scrollbar-thumb{background:#a5aea8 padding-box padding-box;border:2px solid #0000;border-radius:999px;min-width:32px}.vpf-thumbnails::-webkit-scrollbar-thumb:hover{background-color:#7d8b82}.vpf-default-thumbnails::-webkit-scrollbar-thumb:hover{background-color:#7d8b82}.vpf-thumbnails::-webkit-scrollbar-thumb:active{background-color:#5f7165}.vpf-default-thumbnails::-webkit-scrollbar-thumb:active{background-color:#5f7165}.vpf-thumbnails::-webkit-scrollbar-button{width:0;height:0;display:none}.vpf-default-thumbnails::-webkit-scrollbar-button{width:0;height:0;display:none}.vpf-thumbnails::-webkit-scrollbar-corner{background:0 0}.vpf-default-thumbnails::-webkit-scrollbar-corner{background:0 0}}.vpf-thumbnail{color:#333;cursor:pointer;font:inherit;background:#fff;border:2px solid #0000;flex:0 0 86px;padding:4px}.vpf-thumbnail[aria-current]{border-color:#987044}.vpf-thumbnail:focus-visible{outline-offset:-2px;outline:2px solid #987044}.vpf-thumbnail-preview{aspect-ratio:3/4;background:#fff;width:100%}.vpf-book-stage{flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;display:flex}.vpf-default-thumbnails{z-index:11;pointer-events:auto;background:#edf0ebd9;max-height:100%;position:absolute;bottom:0;left:0;right:0;overflow:auto}.vpf-thumbnails--compact{background:0 0;align-items:flex-start;gap:6px;max-height:none;padding:6px}.vpf-thumbnails--compact .vpf-thumbnail{border-width:1px;flex:0 0 30px;width:30px;min-width:0;padding:0;font-size:10px;line-height:16px}.vpf-book-shell{isolation:isolate;flex:none;position:relative}.vpf-flipbook.stf__parent{min-width:0!important;min-height:0!important}.vpf-flipbook{width:100%;height:100%;position:relative}.vpf-turn-page{color:#545454;transform-style:preserve-3d;backface-visibility:hidden;background:#fff;position:relative;box-shadow:inset 0 0 28px #2026250f;overflow:hidden!important}.vpf-turn-page:before{content:"";z-index:0;pointer-events:none;backface-visibility:hidden;background:#fff;position:absolute;inset:0}.vpf-turn-page.--left{border-radius:5px 0 0 5px}.vpf-turn-page.--right{border-radius:0 5px 5px 0}.vpf-book-stage.is-single .vpf-turn-page{border-radius:5px}.vpf-flipbook.stf__parent,.vpf-flipbook .stf__wrapper,.vpf-flipbook .stf__block{box-sizing:border-box}.vpf-flipbook .stf__wrapper{width:100%;height:100%;position:relative}.vpf-flipbook .stf__wrapper.--portrait{clip-path:inset(0)}.vpf-flipbook .stf__wrapper.--landscape:before{content:"";pointer-events:none;background:#fff;width:2px;position:absolute;top:0;bottom:0;left:calc(50% - 1px)}.vpf-flipbook .stf__block{perspective:2400px}.vpf-flipbook .stf__item{transform-style:preserve-3d;backface-visibility:hidden;will-change:transform, clip-path;position:absolute;background:#fff!important}.vpf-flipbook .stf__outerShadow,.vpf-flipbook .stf__innerShadow,.vpf-flipbook .stf__hardShadow,.vpf-flipbook .stf__hardInnerShadow{pointer-events:none;mix-blend-mode:normal}.vpf-page-canvas{z-index:1;background:#fff;width:100%;min-width:0;height:100%;max-height:100%;display:flex;position:relative}.vpf-page-canvas.is-rendering canvas{visibility:hidden}.vpf-reader .stf__parent .vpf-page-canvas canvas,.vpf-page-canvas canvas{object-fit:contain;background:#fff;width:100%;max-width:100%;height:100%;display:block;position:relative}
1
+ .thumbnail-container[data-v-186303b4]{width:100%;position:relative}.page-num[data-v-186303b4]{pointer-events:none;z-index:9;background:#b5b5b540;padding:2px 4px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.vpf-reader,.vpf-reader *{box-sizing:border-box}.vpf-reader{width:100%;height:var(--vpf-height,100%);background:var(--vpf-background,transparent);flex-direction:column;min-width:0;min-height:0;display:flex;overflow:hidden}.vpf-viewport{flex:1;width:100%;min-height:0;position:relative;overflow:hidden}.vpf-page-nav{z-index:10;color:#fff;cursor:pointer;opacity:0;pointer-events:none;background:#141c1bbf;border:1px solid #ffffff40;border-radius:8px;place-items:center;width:44px;height:56px;padding:10px;transition:opacity .15s,background .15s;display:grid;position:absolute;top:50%;transform:translateY(-50%)}.vpf-page-nav--previous{left:12px}.vpf-page-nav--next{right:12px}.vpf-page-nav svg{fill:none;stroke:currentColor;stroke-width:2px;stroke-linecap:round;stroke-linejoin:round;width:24px;height:24px}.vpf-viewport:hover .vpf-page-nav,.vpf-viewport:focus-within .vpf-page-nav{opacity:1;pointer-events:auto}.vpf-page-nav:hover:not(:disabled){background:#141c1bf2}.vpf-page-nav:focus-visible{outline-offset:2px;outline:2px solid #fff}.vpf-viewport:hover .vpf-page-nav:disabled,.vpf-viewport:focus-within .vpf-page-nav:disabled{opacity:.35;cursor:default}@media (hover:none){.vpf-page-nav{opacity:1;pointer-events:auto}.vpf-page-nav:disabled{opacity:.35}}@media (prefers-reduced-motion:reduce){.vpf-page-nav{transition:none}}.vpf-thumbnails{background:#edf0eb;flex:none;gap:12px;max-height:35%;padding:10px;display:flex;overflow:auto}.vpf-thumbnails,.vpf-default-thumbnails{scrollbar-width:thin;scrollbar-color:#a5aea8 transparent}.vpf-thumbnails:hover,.vpf-thumbnails:focus-within,.vpf-default-thumbnails:hover{scrollbar-color:#7d8b82 transparent}@supports selector(::-webkit-scrollbar){.vpf-thumbnails,.vpf-default-thumbnails,.vpf-thumbnails:hover,.vpf-thumbnails:focus-within,.vpf-default-thumbnails:hover{scrollbar-width:auto;scrollbar-color:auto}.vpf-thumbnails::-webkit-scrollbar{width:8px;height:8px}.vpf-default-thumbnails::-webkit-scrollbar{width:8px;height:8px}.vpf-thumbnails::-webkit-scrollbar-track{background:0 0}.vpf-default-thumbnails::-webkit-scrollbar-track{background:0 0}.vpf-thumbnails::-webkit-scrollbar-thumb{background:#a5aea8 padding-box padding-box;border:2px solid #0000;border-radius:999px;min-width:32px}.vpf-default-thumbnails::-webkit-scrollbar-thumb{background:#a5aea8 padding-box padding-box;border:2px solid #0000;border-radius:999px;min-width:32px}.vpf-thumbnails::-webkit-scrollbar-thumb:hover{background-color:#7d8b82}.vpf-default-thumbnails::-webkit-scrollbar-thumb:hover{background-color:#7d8b82}.vpf-thumbnails::-webkit-scrollbar-thumb:active{background-color:#5f7165}.vpf-default-thumbnails::-webkit-scrollbar-thumb:active{background-color:#5f7165}.vpf-thumbnails::-webkit-scrollbar-button{width:0;height:0;display:none}.vpf-default-thumbnails::-webkit-scrollbar-button{width:0;height:0;display:none}.vpf-thumbnails::-webkit-scrollbar-corner{background:0 0}.vpf-default-thumbnails::-webkit-scrollbar-corner{background:0 0}}.vpf-thumbnail{color:#333;cursor:pointer;font:inherit;background:#fff;border:2px solid #0000;flex:0 0 86px;padding:4px}.vpf-thumbnail[aria-current]{border-color:#987044}.vpf-thumbnail:focus-visible{outline-offset:-2px;outline:2px solid #987044}.vpf-thumbnail-preview{aspect-ratio:3/4;background:#fff;width:100%}.vpf-book-stage{flex-direction:column;justify-content:center;align-items:center;width:100%;height:100%;display:flex}.vpf-default-thumbnails{z-index:11;pointer-events:auto;background:#edf0ebd9;max-height:100%;position:absolute;bottom:0;left:0;right:0;overflow:auto}.vpf-thumbnails--compact{background:0 0;align-items:flex-start;gap:6px;max-height:none;padding:6px}.vpf-thumbnails--compact .vpf-thumbnail{border-width:1px;flex:0 0 30px;width:30px;min-width:0;padding:0;font-size:10px;line-height:16px}.vpf-thumbnails--compact,.vpf-thumbnails--compact .vpf-thumbnail{cursor:grab;-webkit-user-select:none;user-select:none}.vpf-thumbnails--dragging,.vpf-thumbnails--dragging .vpf-thumbnail{cursor:grabbing}.vpf-book-shell{isolation:isolate;--vpf-cover-duration:.78s;--vpf-cover-easing:cubic-bezier(.45, 0, .2, 1);flex:none;position:relative}.vpf-page-edges{pointer-events:none;z-index:0;opacity:var(--vpf-edges-opacity,1);transition:transform var(--vpf-cover-duration) var(--vpf-cover-easing), var(--vpf-edges-transition,opacity 0s);position:absolute;inset:0}.vpf-page-edge{filter:drop-shadow(0 1px 1px #191f1c33);background:repeating-linear-gradient(90deg,#a7aaa6 0,#e5e5df .7px,#fffef8 1.4px,#c4c6c0 2px);transition:width .42s;position:absolute;top:1px;bottom:1px}.vpf-page-edge--left{visibility:var(--vpf-edge-left-visibility,hidden);width:var(--vpf-edge-left,0px);clip-path:polygon(0 7px,100% 0,100% 100%,0 calc(100% - 7px));right:100%}.vpf-page-edge--right{visibility:var(--vpf-edge-right-visibility,hidden);width:var(--vpf-edge-right,0px);clip-path:polygon(0 0,100% 7px,100% calc(100% - 7px),0 100%);left:100%}.vpf-flipbook{z-index:1}.vpf-flipbook.stf__parent{min-width:0!important;min-height:0!important}.vpf-flipbook{width:100%;height:100%;transition:transform var(--vpf-cover-duration) var(--vpf-cover-easing);position:relative}.vpf-book-shell--front-cover .vpf-page-edges,.vpf-book-shell--front-cover .vpf-flipbook{transform:translate(-25%)}.vpf-book-shell--back-cover .vpf-page-edges,.vpf-book-shell--back-cover .vpf-flipbook{transform:translate(25%)}.vpf-book-shell--front-cover .vpf-page-nav--previous,.vpf-book-shell--back-cover .vpf-page-nav--previous{left:calc(25% + 12px)}.vpf-book-shell--front-cover .vpf-page-nav--next,.vpf-book-shell--back-cover .vpf-page-nav--next{right:calc(25% + 12px)}.vpf-book-shell--front-cover .vpf-default-thumbnails,.vpf-book-shell--back-cover .vpf-default-thumbnails{left:25%;right:25%}.vpf-book-shell--front-cover .stf__wrapper:before,.vpf-book-shell--back-cover .stf__wrapper:before,.vpf-flipbook .stf__wrapper.--landscape .vpf-turn-page[data-page="1"]:after,.vpf-book-shell--back-cover .vpf-turn-page:after{display:none}@media (prefers-reduced-motion:reduce){.vpf-page-edges,.vpf-flipbook,.vpf-page-edge{transition:none}}.vpf-turn-page{color:#545454;transform-style:preserve-3d;backface-visibility:hidden;background:#fff;position:relative;box-shadow:inset 0 0 28px #2026250f;overflow:hidden!important}.vpf-turn-page:before{content:"";z-index:0;pointer-events:none;backface-visibility:hidden;background:#fff;position:absolute;inset:0}.vpf-turn-page.--left{border-radius:5px 0 0 5px}.vpf-turn-page.--right{border-radius:0 5px 5px 0}.vpf-flipbook .stf__wrapper.--landscape .vpf-turn-page:after{content:"";z-index:2;pointer-events:none;background:linear-gradient(90deg,#14181c3d 0%,#14181c21 18%,#14181c0e 46%,#14181c04 72%,#0000 100%);width:12%;max-width:56px;position:absolute;top:0;bottom:0}.vpf-flipbook .stf__wrapper.--landscape .vpf-turn-page.--left:after{right:0;transform:scaleX(-1)}.vpf-flipbook .stf__wrapper.--landscape .vpf-turn-page.--right:after{left:0}.vpf-book-stage.is-single .vpf-turn-page{border-radius:5px}.vpf-flipbook.stf__parent,.vpf-flipbook .stf__wrapper,.vpf-flipbook .stf__block{box-sizing:border-box}.vpf-flipbook .stf__wrapper{width:100%;height:100%;position:relative}.vpf-flipbook .stf__wrapper.--portrait{clip-path:inset(0)}.vpf-flipbook .stf__wrapper.--landscape:before{content:"";pointer-events:none;background:#fff;width:2px;position:absolute;top:0;bottom:0;left:calc(50% - 1px)}.vpf-flipbook .stf__block{perspective:2400px}.vpf-flipbook .stf__item{transform-style:preserve-3d;backface-visibility:hidden;will-change:transform, clip-path;position:absolute;background:#fff!important}.vpf-flipbook .stf__outerShadow,.vpf-flipbook .stf__innerShadow,.vpf-flipbook .stf__hardShadow,.vpf-flipbook .stf__hardInnerShadow{pointer-events:none;mix-blend-mode:normal}.vpf-page-canvas{z-index:1;background:#fff;width:100%;min-width:0;height:100%;max-height:100%;display:flex;position:relative}.vpf-page-canvas.is-rendering canvas{visibility:hidden}.vpf-reader .stf__parent .vpf-page-canvas canvas,.vpf-page-canvas canvas{object-fit:contain;background:#fff;width:100%;max-width:100%;height:100%;display:block;position:relative}
2
2
  /*$vite$:1*/