kviewer 0.0.8 → 0.0.10
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/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/runtime/annotation/engine/editor/editor.d.ts +11 -5
- package/dist/runtime/annotation/engine/editor/editor.js +27 -11
- package/dist/runtime/annotation/engine/editor/selector.d.ts +20 -1
- package/dist/runtime/annotation/engine/editor/selector.js +132 -33
- package/dist/runtime/annotation/engine/painter.d.ts +4 -1
- package/dist/runtime/annotation/engine/painter.js +21 -5
- package/dist/runtime/annotation/engine/tools/freehand.js +2 -2
- package/dist/runtime/components/PdfPage.d.vue.ts +6 -1
- package/dist/runtime/components/PdfPage.vue +51 -18
- package/dist/runtime/components/PdfPage.vue.d.ts +6 -1
- package/dist/runtime/components/ToolButton.d.vue.ts +4 -2
- package/dist/runtime/components/ToolButton.vue.d.ts +4 -2
- package/dist/runtime/components/Viewer.d.vue.ts +3 -0
- package/dist/runtime/components/Viewer.vue +225 -165
- package/dist/runtime/components/Viewer.vue.d.ts +3 -0
- package/dist/runtime/components/form-fields/FormSignatureField.vue +23 -1
- package/dist/runtime/components/modals/FreeTextModal.vue +27 -3
- package/dist/runtime/components/tools/ActionTools.vue +4 -5
- package/dist/runtime/components/tools/DrawingTools.vue +1 -1
- package/dist/runtime/composables/useAnnotationEngine.d.ts +2 -0
- package/dist/runtime/composables/useAnnotationEngine.js +5 -1
- package/dist/runtime/composables/useAnnotationHistory.d.ts +1 -0
- package/dist/runtime/composables/useAnnotationHistory.js +10 -3
- package/dist/runtime/composables/useInertiaPanzoom.d.ts +40 -0
- package/dist/runtime/composables/useInertiaPanzoom.js +472 -0
- package/dist/runtime/composables/usePageVirtualization.d.ts +3 -0
- package/dist/runtime/composables/usePageVirtualization.js +23 -0
- package/dist/runtime/composables/useViewerSearch.d.ts +1 -0
- package/dist/runtime/composables/useViewerSearch.js +15 -5
- package/dist/runtime/public-types.d.ts +2 -0
- package/dist/types.d.mts +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
import { onBeforeUnmount } from "vue";
|
|
2
|
+
const DECELERATION = 0.998;
|
|
3
|
+
const RUBBER_BAND_C = 0.55;
|
|
4
|
+
const BOUNCE_BACK_RATE = 0.04;
|
|
5
|
+
const BOUNCE_VELOCITY_DECAY = 0.04;
|
|
6
|
+
const MIN_VELOCITY = 0.01;
|
|
7
|
+
export function useInertiaPanzoom(options = {}) {
|
|
8
|
+
const {
|
|
9
|
+
minScale = 0.5,
|
|
10
|
+
maxScale = 4,
|
|
11
|
+
axisLockThreshold = 10,
|
|
12
|
+
axisLockRatio = 2
|
|
13
|
+
} = options;
|
|
14
|
+
let controller = null;
|
|
15
|
+
function attach(target) {
|
|
16
|
+
detach();
|
|
17
|
+
const parent = target.parentElement;
|
|
18
|
+
if (!parent) return;
|
|
19
|
+
let scale = 1;
|
|
20
|
+
let tx = 0;
|
|
21
|
+
let ty = 0;
|
|
22
|
+
let gesturesEnabled = true;
|
|
23
|
+
const pointers = /* @__PURE__ */ new Map();
|
|
24
|
+
let pinchStart = null;
|
|
25
|
+
let panStart = null;
|
|
26
|
+
let lastMoveTime = 0;
|
|
27
|
+
let lastMoveX = 0;
|
|
28
|
+
let lastMoveY = 0;
|
|
29
|
+
let vx = 0;
|
|
30
|
+
let vy = 0;
|
|
31
|
+
let momentumRaf = 0;
|
|
32
|
+
let lastStepTime = 0;
|
|
33
|
+
const subscribers = /* @__PURE__ */ new Set();
|
|
34
|
+
target.style.transformOrigin = "0 0";
|
|
35
|
+
target.style.touchAction = "none";
|
|
36
|
+
parent.style.touchAction = "none";
|
|
37
|
+
target.style.userSelect = "none";
|
|
38
|
+
let willChangeActive = false;
|
|
39
|
+
function setWillChangeActive(active) {
|
|
40
|
+
if (willChangeActive === active) return;
|
|
41
|
+
willChangeActive = active;
|
|
42
|
+
target.style.willChange = active ? "transform" : "";
|
|
43
|
+
}
|
|
44
|
+
const BAR_SIZE = 10;
|
|
45
|
+
const vBar = document.createElement("div");
|
|
46
|
+
vBar.setAttribute("data-inertia-scrollbar", "y");
|
|
47
|
+
vBar.style.cssText = `position:absolute;right:2px;top:0;width:${BAR_SIZE}px;background:rgba(0,0,0,0.35);border-radius:${BAR_SIZE / 2}px;opacity:0;transition:opacity 300ms,background-color 150ms;z-index:10;touch-action:none;cursor:grab;`;
|
|
48
|
+
const hBar = document.createElement("div");
|
|
49
|
+
hBar.setAttribute("data-inertia-scrollbar", "x");
|
|
50
|
+
hBar.style.cssText = `position:absolute;bottom:2px;left:0;height:${BAR_SIZE}px;background:rgba(0,0,0,0.35);border-radius:${BAR_SIZE / 2}px;opacity:0;transition:opacity 300ms,background-color 150ms;z-index:10;touch-action:none;cursor:grab;`;
|
|
51
|
+
parent.appendChild(vBar);
|
|
52
|
+
parent.appendChild(hBar);
|
|
53
|
+
let hideTimer = 0;
|
|
54
|
+
let barDrag = null;
|
|
55
|
+
function updateIndicators() {
|
|
56
|
+
const pw = parent.clientWidth;
|
|
57
|
+
const ph = parent.clientHeight;
|
|
58
|
+
const cw = target.scrollWidth * scale;
|
|
59
|
+
const ch = target.scrollHeight * scale;
|
|
60
|
+
if (ch > ph) {
|
|
61
|
+
const thumbH = Math.max(30, ph / ch * ph);
|
|
62
|
+
const scrollable = ch - ph;
|
|
63
|
+
const st = Math.max(0, Math.min(scrollable, -ty));
|
|
64
|
+
vBar.style.height = `${thumbH}px`;
|
|
65
|
+
vBar.style.top = `${st / scrollable * (ph - thumbH)}px`;
|
|
66
|
+
vBar.style.opacity = "1";
|
|
67
|
+
vBar.style.display = "";
|
|
68
|
+
} else {
|
|
69
|
+
vBar.style.opacity = "0";
|
|
70
|
+
vBar.style.display = "none";
|
|
71
|
+
}
|
|
72
|
+
if (cw > pw) {
|
|
73
|
+
const thumbW = Math.max(30, pw / cw * pw);
|
|
74
|
+
const scrollable = cw - pw;
|
|
75
|
+
const sl = Math.max(0, Math.min(scrollable, -tx));
|
|
76
|
+
hBar.style.width = `${thumbW}px`;
|
|
77
|
+
hBar.style.left = `${sl / scrollable * (pw - thumbW)}px`;
|
|
78
|
+
hBar.style.opacity = "1";
|
|
79
|
+
hBar.style.display = "";
|
|
80
|
+
} else {
|
|
81
|
+
hBar.style.opacity = "0";
|
|
82
|
+
hBar.style.display = "none";
|
|
83
|
+
}
|
|
84
|
+
if (barDrag) return;
|
|
85
|
+
clearTimeout(hideTimer);
|
|
86
|
+
hideTimer = window.setTimeout(() => {
|
|
87
|
+
vBar.style.opacity = "0";
|
|
88
|
+
hBar.style.opacity = "0";
|
|
89
|
+
}, 800);
|
|
90
|
+
}
|
|
91
|
+
function onBarDown(axis) {
|
|
92
|
+
return (e) => {
|
|
93
|
+
e.stopPropagation();
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
stopMomentum();
|
|
96
|
+
const pw = parent.clientWidth;
|
|
97
|
+
const ph = parent.clientHeight;
|
|
98
|
+
const cw = target.scrollWidth * scale;
|
|
99
|
+
const ch = target.scrollHeight * scale;
|
|
100
|
+
if (axis === "y") {
|
|
101
|
+
if (ch <= ph) return;
|
|
102
|
+
const thumbH = Math.max(30, ph / ch * ph);
|
|
103
|
+
const trackH = ph - thumbH;
|
|
104
|
+
const scrollable = ch - ph;
|
|
105
|
+
barDrag = {
|
|
106
|
+
axis: "y",
|
|
107
|
+
startPointer: e.clientY,
|
|
108
|
+
startT: ty,
|
|
109
|
+
ratio: scrollable / trackH,
|
|
110
|
+
pointerId: e.pointerId
|
|
111
|
+
};
|
|
112
|
+
vBar.style.background = "rgba(0,0,0,0.55)";
|
|
113
|
+
} else {
|
|
114
|
+
if (cw <= pw) return;
|
|
115
|
+
const thumbW = Math.max(30, pw / cw * pw);
|
|
116
|
+
const trackW = pw - thumbW;
|
|
117
|
+
const scrollable = cw - pw;
|
|
118
|
+
barDrag = {
|
|
119
|
+
axis: "x",
|
|
120
|
+
startPointer: e.clientX,
|
|
121
|
+
startT: tx,
|
|
122
|
+
ratio: scrollable / trackW,
|
|
123
|
+
pointerId: e.pointerId
|
|
124
|
+
};
|
|
125
|
+
hBar.style.background = "rgba(0,0,0,0.55)";
|
|
126
|
+
}
|
|
127
|
+
e.target.setPointerCapture?.(e.pointerId);
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function onBarMove(e) {
|
|
131
|
+
if (!barDrag || e.pointerId !== barDrag.pointerId) return;
|
|
132
|
+
e.stopPropagation();
|
|
133
|
+
if (barDrag.axis === "y") {
|
|
134
|
+
const dy = e.clientY - barDrag.startPointer;
|
|
135
|
+
const b = getBounds();
|
|
136
|
+
ty = Math.max(b.minY, Math.min(b.maxY, barDrag.startT - dy * barDrag.ratio));
|
|
137
|
+
} else {
|
|
138
|
+
const dx = e.clientX - barDrag.startPointer;
|
|
139
|
+
const b = getBounds();
|
|
140
|
+
tx = Math.max(b.minX, Math.min(b.maxX, barDrag.startT - dx * barDrag.ratio));
|
|
141
|
+
}
|
|
142
|
+
apply();
|
|
143
|
+
}
|
|
144
|
+
function onBarUp(e) {
|
|
145
|
+
if (!barDrag || e.pointerId !== barDrag.pointerId) return;
|
|
146
|
+
e.stopPropagation();
|
|
147
|
+
vBar.style.background = "rgba(0,0,0,0.35)";
|
|
148
|
+
hBar.style.background = "rgba(0,0,0,0.35)";
|
|
149
|
+
barDrag = null;
|
|
150
|
+
updateIndicators();
|
|
151
|
+
}
|
|
152
|
+
vBar.addEventListener("pointerdown", onBarDown("y"));
|
|
153
|
+
hBar.addEventListener("pointerdown", onBarDown("x"));
|
|
154
|
+
vBar.addEventListener("pointermove", onBarMove);
|
|
155
|
+
hBar.addEventListener("pointermove", onBarMove);
|
|
156
|
+
vBar.addEventListener("pointerup", onBarUp);
|
|
157
|
+
hBar.addEventListener("pointerup", onBarUp);
|
|
158
|
+
vBar.addEventListener("pointercancel", onBarUp);
|
|
159
|
+
hBar.addEventListener("pointercancel", onBarUp);
|
|
160
|
+
function apply() {
|
|
161
|
+
target.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`;
|
|
162
|
+
updateIndicators();
|
|
163
|
+
if (subscribers.size > 0) {
|
|
164
|
+
const t = { scale, tx, ty };
|
|
165
|
+
subscribers.forEach((cb) => cb(t));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function scrollToElement(elem, opts) {
|
|
169
|
+
const block = opts?.block ?? "start";
|
|
170
|
+
const animate = opts?.animate ?? true;
|
|
171
|
+
const elemRect = elem.getBoundingClientRect();
|
|
172
|
+
const parentRect = parent.getBoundingClientRect();
|
|
173
|
+
const targetOffset = block === "center" ? (parentRect.height - elemRect.height) / 2 : 0;
|
|
174
|
+
const delta = elemRect.top - parentRect.top - targetOffset;
|
|
175
|
+
let newTy = ty - delta;
|
|
176
|
+
const b = getBounds();
|
|
177
|
+
newTy = Math.max(b.minY, Math.min(b.maxY, newTy));
|
|
178
|
+
stopMomentum();
|
|
179
|
+
if (!animate) {
|
|
180
|
+
ty = newTy;
|
|
181
|
+
apply();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const startTy = ty;
|
|
185
|
+
const startTime = performance.now();
|
|
186
|
+
const duration = Math.min(600, 200 + Math.abs(newTy - startTy) * 0.5);
|
|
187
|
+
setWillChangeActive(true);
|
|
188
|
+
function frame() {
|
|
189
|
+
const t = Math.min(1, (performance.now() - startTime) / duration);
|
|
190
|
+
const eased = 1 - Math.pow(1 - t, 3);
|
|
191
|
+
ty = startTy + (newTy - startTy) * eased;
|
|
192
|
+
apply();
|
|
193
|
+
if (t < 1) {
|
|
194
|
+
momentumRaf = requestAnimationFrame(frame);
|
|
195
|
+
} else {
|
|
196
|
+
momentumRaf = 0;
|
|
197
|
+
setWillChangeActive(false);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
momentumRaf = requestAnimationFrame(frame);
|
|
201
|
+
}
|
|
202
|
+
function getBounds() {
|
|
203
|
+
const pw = parent.clientWidth;
|
|
204
|
+
const ph = parent.clientHeight;
|
|
205
|
+
const cw = target.scrollWidth * scale;
|
|
206
|
+
const ch = target.scrollHeight * scale;
|
|
207
|
+
let minX, maxX, minY, maxY;
|
|
208
|
+
if (cw <= pw) {
|
|
209
|
+
minX = maxX = (pw - cw) / 2;
|
|
210
|
+
} else {
|
|
211
|
+
minX = pw - cw;
|
|
212
|
+
maxX = 0;
|
|
213
|
+
}
|
|
214
|
+
if (ch <= ph) {
|
|
215
|
+
minY = maxY = (ph - ch) / 2;
|
|
216
|
+
} else {
|
|
217
|
+
minY = ph - ch;
|
|
218
|
+
maxY = 0;
|
|
219
|
+
}
|
|
220
|
+
return { minX, maxX, minY, maxY };
|
|
221
|
+
}
|
|
222
|
+
function rubberBandAxis(v, min, max, dim) {
|
|
223
|
+
if (v < min) return min - (1 - 1 / ((min - v) * RUBBER_BAND_C / dim + 1)) * dim;
|
|
224
|
+
if (v > max) return max + (1 - 1 / ((v - max) * RUBBER_BAND_C / dim + 1)) * dim;
|
|
225
|
+
return v;
|
|
226
|
+
}
|
|
227
|
+
function stopMomentum() {
|
|
228
|
+
if (momentumRaf) {
|
|
229
|
+
cancelAnimationFrame(momentumRaf);
|
|
230
|
+
momentumRaf = 0;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function startMomentum() {
|
|
234
|
+
stopMomentum();
|
|
235
|
+
lastStepTime = performance.now();
|
|
236
|
+
function step() {
|
|
237
|
+
const now = performance.now();
|
|
238
|
+
const dt = Math.min(32, now - lastStepTime);
|
|
239
|
+
lastStepTime = now;
|
|
240
|
+
const b = getBounds();
|
|
241
|
+
if (tx < b.minX) {
|
|
242
|
+
tx += (b.minX - tx) * (1 - Math.exp(-BOUNCE_BACK_RATE * dt));
|
|
243
|
+
vx *= Math.exp(-BOUNCE_VELOCITY_DECAY * dt);
|
|
244
|
+
} else if (tx > b.maxX) {
|
|
245
|
+
tx += (b.maxX - tx) * (1 - Math.exp(-BOUNCE_BACK_RATE * dt));
|
|
246
|
+
vx *= Math.exp(-BOUNCE_VELOCITY_DECAY * dt);
|
|
247
|
+
} else {
|
|
248
|
+
vx *= Math.pow(DECELERATION, dt);
|
|
249
|
+
tx += vx * dt;
|
|
250
|
+
}
|
|
251
|
+
if (ty < b.minY) {
|
|
252
|
+
ty += (b.minY - ty) * (1 - Math.exp(-BOUNCE_BACK_RATE * dt));
|
|
253
|
+
vy *= Math.exp(-BOUNCE_VELOCITY_DECAY * dt);
|
|
254
|
+
} else if (ty > b.maxY) {
|
|
255
|
+
ty += (b.maxY - ty) * (1 - Math.exp(-BOUNCE_BACK_RATE * dt));
|
|
256
|
+
vy *= Math.exp(-BOUNCE_VELOCITY_DECAY * dt);
|
|
257
|
+
} else {
|
|
258
|
+
vy *= Math.pow(DECELERATION, dt);
|
|
259
|
+
ty += vy * dt;
|
|
260
|
+
}
|
|
261
|
+
apply();
|
|
262
|
+
const stopped = Math.abs(vx) < MIN_VELOCITY && Math.abs(vy) < MIN_VELOCITY;
|
|
263
|
+
const inBounds = tx >= b.minX - 0.5 && tx <= b.maxX + 0.5 && ty >= b.minY - 0.5 && ty <= b.maxY + 0.5;
|
|
264
|
+
if (stopped && inBounds) {
|
|
265
|
+
tx = Math.max(b.minX, Math.min(b.maxX, tx));
|
|
266
|
+
ty = Math.max(b.minY, Math.min(b.maxY, ty));
|
|
267
|
+
vx = vy = 0;
|
|
268
|
+
apply();
|
|
269
|
+
momentumRaf = 0;
|
|
270
|
+
setWillChangeActive(false);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
momentumRaf = requestAnimationFrame(step);
|
|
274
|
+
}
|
|
275
|
+
momentumRaf = requestAnimationFrame(step);
|
|
276
|
+
}
|
|
277
|
+
function onPenFilter(e) {
|
|
278
|
+
if (e instanceof PointerEvent && e.pointerType === "pen") {
|
|
279
|
+
e.stopPropagation();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function onDown(e) {
|
|
283
|
+
if (e.pointerType === "pen") return;
|
|
284
|
+
if (!gesturesEnabled) return;
|
|
285
|
+
stopMomentum();
|
|
286
|
+
setWillChangeActive(true);
|
|
287
|
+
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
288
|
+
if (pointers.size === 2) {
|
|
289
|
+
const pts = [...pointers.values()];
|
|
290
|
+
pinchStart = {
|
|
291
|
+
dist: Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y),
|
|
292
|
+
midX: (pts[0].x + pts[1].x) / 2,
|
|
293
|
+
midY: (pts[0].y + pts[1].y) / 2,
|
|
294
|
+
s: scale,
|
|
295
|
+
tx,
|
|
296
|
+
ty
|
|
297
|
+
};
|
|
298
|
+
panStart = null;
|
|
299
|
+
} else if (pointers.size === 1) {
|
|
300
|
+
panStart = { x: e.clientX, y: e.clientY, tx, ty, axisLock: "none" };
|
|
301
|
+
lastMoveTime = performance.now();
|
|
302
|
+
lastMoveX = e.clientX;
|
|
303
|
+
lastMoveY = e.clientY;
|
|
304
|
+
vx = vy = 0;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function onMove(e) {
|
|
308
|
+
if (!pointers.has(e.pointerId)) return;
|
|
309
|
+
pointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
310
|
+
if (pointers.size === 2 && pinchStart) {
|
|
311
|
+
e.preventDefault();
|
|
312
|
+
const pts = [...pointers.values()];
|
|
313
|
+
const dist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
|
|
314
|
+
const midX = (pts[0].x + pts[1].x) / 2;
|
|
315
|
+
const midY = (pts[0].y + pts[1].y) / 2;
|
|
316
|
+
const newScale = Math.max(minScale, Math.min(maxScale, pinchStart.s * (dist / pinchStart.dist)));
|
|
317
|
+
const rect = parent.getBoundingClientRect();
|
|
318
|
+
const focalX = (pinchStart.midX - rect.left - pinchStart.tx) / pinchStart.s;
|
|
319
|
+
const focalY = (pinchStart.midY - rect.top - pinchStart.ty) / pinchStart.s;
|
|
320
|
+
tx = midX - rect.left - newScale * focalX;
|
|
321
|
+
ty = midY - rect.top - newScale * focalY;
|
|
322
|
+
scale = newScale;
|
|
323
|
+
apply();
|
|
324
|
+
} else if (pointers.size === 1 && panStart) {
|
|
325
|
+
e.preventDefault();
|
|
326
|
+
const dx = e.clientX - panStart.x;
|
|
327
|
+
const dy = e.clientY - panStart.y;
|
|
328
|
+
if (panStart.axisLock === "none") {
|
|
329
|
+
const absDx = Math.abs(dx);
|
|
330
|
+
const absDy = Math.abs(dy);
|
|
331
|
+
if (Math.max(absDx, absDy) >= axisLockThreshold) {
|
|
332
|
+
panStart.axisLock = absDy > absDx * axisLockRatio ? "y" : "free";
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const rawTx = panStart.axisLock === "y" ? panStart.tx : panStart.tx + dx;
|
|
336
|
+
const rawTy = panStart.ty + dy;
|
|
337
|
+
const b = getBounds();
|
|
338
|
+
tx = rubberBandAxis(rawTx, b.minX, b.maxX, parent.clientWidth);
|
|
339
|
+
ty = rubberBandAxis(rawTy, b.minY, b.maxY, parent.clientHeight);
|
|
340
|
+
apply();
|
|
341
|
+
const now = performance.now();
|
|
342
|
+
const dt = Math.max(1, now - lastMoveTime);
|
|
343
|
+
const instVx = (e.clientX - lastMoveX) / dt;
|
|
344
|
+
const instVy = (e.clientY - lastMoveY) / dt;
|
|
345
|
+
const alpha = 0.85;
|
|
346
|
+
vx = vx * (1 - alpha) + instVx * alpha;
|
|
347
|
+
vy = vy * (1 - alpha) + instVy * alpha;
|
|
348
|
+
lastMoveTime = now;
|
|
349
|
+
lastMoveX = e.clientX;
|
|
350
|
+
lastMoveY = e.clientY;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function onUp(e) {
|
|
354
|
+
pointers.delete(e.pointerId);
|
|
355
|
+
if (pointers.size < 2) pinchStart = null;
|
|
356
|
+
if (pointers.size === 0) {
|
|
357
|
+
const hadPan = panStart !== null;
|
|
358
|
+
const lockedAxis = panStart?.axisLock;
|
|
359
|
+
panStart = null;
|
|
360
|
+
const idleMs = performance.now() - lastMoveTime;
|
|
361
|
+
if (idleMs > 60) {
|
|
362
|
+
vx = 0;
|
|
363
|
+
vy = 0;
|
|
364
|
+
}
|
|
365
|
+
if (hadPan && lockedAxis === "y") vx = 0;
|
|
366
|
+
startMomentum();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function onWheelZoom(e) {
|
|
370
|
+
if (!e.ctrlKey && !e.metaKey) return;
|
|
371
|
+
e.preventDefault();
|
|
372
|
+
stopMomentum();
|
|
373
|
+
const rect = parent.getBoundingClientRect();
|
|
374
|
+
const mx = e.clientX - rect.left;
|
|
375
|
+
const my = e.clientY - rect.top;
|
|
376
|
+
const delta = e.deltaY < 0 ? 1 : -1;
|
|
377
|
+
const newScale = Math.max(minScale, Math.min(maxScale, scale * Math.exp(delta * 0.1)));
|
|
378
|
+
const focalX = (mx - tx) / scale;
|
|
379
|
+
const focalY = (my - ty) / scale;
|
|
380
|
+
tx = mx - newScale * focalX;
|
|
381
|
+
ty = my - newScale * focalY;
|
|
382
|
+
scale = newScale;
|
|
383
|
+
apply();
|
|
384
|
+
}
|
|
385
|
+
function scrollBy(dx, dy) {
|
|
386
|
+
stopMomentum();
|
|
387
|
+
const b = getBounds();
|
|
388
|
+
tx = Math.max(b.minX, Math.min(b.maxX, tx - dx));
|
|
389
|
+
ty = Math.max(b.minY, Math.min(b.maxY, ty - dy));
|
|
390
|
+
apply();
|
|
391
|
+
}
|
|
392
|
+
parent.addEventListener("pointerdown", onDown);
|
|
393
|
+
parent.addEventListener("pointermove", onMove);
|
|
394
|
+
parent.addEventListener("pointerup", onUp);
|
|
395
|
+
parent.addEventListener("pointercancel", onUp);
|
|
396
|
+
parent.addEventListener("wheel", onWheelZoom, { passive: false });
|
|
397
|
+
target.addEventListener("pointerdown", onPenFilter);
|
|
398
|
+
apply();
|
|
399
|
+
controller = {
|
|
400
|
+
cleanup: () => {
|
|
401
|
+
stopMomentum();
|
|
402
|
+
clearTimeout(hideTimer);
|
|
403
|
+
parent.removeEventListener("pointerdown", onDown);
|
|
404
|
+
parent.removeEventListener("pointermove", onMove);
|
|
405
|
+
parent.removeEventListener("pointerup", onUp);
|
|
406
|
+
parent.removeEventListener("pointercancel", onUp);
|
|
407
|
+
parent.removeEventListener("wheel", onWheelZoom);
|
|
408
|
+
target.removeEventListener("pointerdown", onPenFilter);
|
|
409
|
+
vBar.remove();
|
|
410
|
+
hBar.remove();
|
|
411
|
+
target.style.transform = "";
|
|
412
|
+
target.style.transformOrigin = "";
|
|
413
|
+
target.style.willChange = "";
|
|
414
|
+
target.style.touchAction = "";
|
|
415
|
+
target.style.userSelect = "";
|
|
416
|
+
parent.style.touchAction = "";
|
|
417
|
+
},
|
|
418
|
+
reset: () => {
|
|
419
|
+
stopMomentum();
|
|
420
|
+
scale = 1;
|
|
421
|
+
tx = 0;
|
|
422
|
+
ty = 0;
|
|
423
|
+
vx = vy = 0;
|
|
424
|
+
pointers.clear();
|
|
425
|
+
pinchStart = null;
|
|
426
|
+
panStart = null;
|
|
427
|
+
setWillChangeActive(false);
|
|
428
|
+
apply();
|
|
429
|
+
},
|
|
430
|
+
stop: () => {
|
|
431
|
+
stopMomentum();
|
|
432
|
+
setWillChangeActive(false);
|
|
433
|
+
},
|
|
434
|
+
getScale: () => scale,
|
|
435
|
+
getTransform: () => ({ scale, tx, ty }),
|
|
436
|
+
scrollToElement,
|
|
437
|
+
scrollBy,
|
|
438
|
+
setGesturesEnabled: (enabled) => {
|
|
439
|
+
gesturesEnabled = enabled;
|
|
440
|
+
if (!enabled) {
|
|
441
|
+
pointers.clear();
|
|
442
|
+
panStart = null;
|
|
443
|
+
pinchStart = null;
|
|
444
|
+
stopMomentum();
|
|
445
|
+
setWillChangeActive(false);
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
onChange: (cb) => {
|
|
449
|
+
subscribers.add(cb);
|
|
450
|
+
return () => subscribers.delete(cb);
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function detach() {
|
|
455
|
+
controller?.cleanup();
|
|
456
|
+
controller = null;
|
|
457
|
+
}
|
|
458
|
+
onBeforeUnmount(detach);
|
|
459
|
+
return {
|
|
460
|
+
attach,
|
|
461
|
+
detach,
|
|
462
|
+
reset: () => controller?.reset(),
|
|
463
|
+
stop: () => controller?.stop(),
|
|
464
|
+
getScale: () => controller?.getScale() ?? 1,
|
|
465
|
+
getTransform: () => controller?.getTransform() ?? { scale: 1, tx: 0, ty: 0 },
|
|
466
|
+
scrollToElement: (elem, opts) => controller?.scrollToElement(elem, opts),
|
|
467
|
+
scrollBy: (dx, dy) => controller?.scrollBy(dx, dy),
|
|
468
|
+
setGesturesEnabled: (enabled) => controller?.setGesturesEnabled(enabled),
|
|
469
|
+
onChange: (cb) => controller?.onChange(cb) ?? (() => {
|
|
470
|
+
})
|
|
471
|
+
};
|
|
472
|
+
}
|
|
@@ -13,6 +13,9 @@ export interface PageVirtualization {
|
|
|
13
13
|
observePage: (pageNumber: number, element: HTMLElement) => void;
|
|
14
14
|
unobservePage: (pageNumber: number) => void;
|
|
15
15
|
scrollToPage: (pageNumber: number) => void;
|
|
16
|
+
getPageElement: (pageNumber: number) => HTMLElement | undefined;
|
|
17
|
+
/** Recompute currentPage using visual bounding rects. Works with CSS transforms. */
|
|
18
|
+
updateCurrentPageByRect: () => void;
|
|
16
19
|
init: (doc: PDFDocumentProxy, scrollRoot: HTMLElement) => Promise<void>;
|
|
17
20
|
destroy: () => void;
|
|
18
21
|
}
|
|
@@ -96,6 +96,27 @@ export function createPageVirtualization() {
|
|
|
96
96
|
currentPage.value = pageNumber;
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
+
function getPageElement(pageNumber) {
|
|
100
|
+
return pageToElement.get(pageNumber);
|
|
101
|
+
}
|
|
102
|
+
function updateCurrentPageByRect() {
|
|
103
|
+
if (!scrollRoot) return;
|
|
104
|
+
const rootRect = scrollRoot.getBoundingClientRect();
|
|
105
|
+
const rootMid = rootRect.top + rootRect.height / 2;
|
|
106
|
+
let bestPage = currentPage.value;
|
|
107
|
+
let bestDist = Infinity;
|
|
108
|
+
for (const [pageNum, el] of pageToElement) {
|
|
109
|
+
const rect = el.getBoundingClientRect();
|
|
110
|
+
const dist = rootMid < rect.top ? rect.top - rootMid : rootMid > rect.bottom ? rootMid - rect.bottom : 0;
|
|
111
|
+
if (dist < bestDist) {
|
|
112
|
+
bestDist = dist;
|
|
113
|
+
bestPage = pageNum;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (bestPage !== currentPage.value) {
|
|
117
|
+
currentPage.value = bestPage;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
99
120
|
async function fetchPageDimensions(doc, pageNumber) {
|
|
100
121
|
const proxy = await doc.getPage(pageNumber);
|
|
101
122
|
const view = proxy.view;
|
|
@@ -197,6 +218,8 @@ export function createPageVirtualization() {
|
|
|
197
218
|
observePage,
|
|
198
219
|
unobservePage,
|
|
199
220
|
scrollToPage,
|
|
221
|
+
getPageElement,
|
|
222
|
+
updateCurrentPageByRect,
|
|
200
223
|
init,
|
|
201
224
|
destroy
|
|
202
225
|
};
|
|
@@ -27,6 +27,7 @@ export interface ViewerSearchState {
|
|
|
27
27
|
totalPages: number;
|
|
28
28
|
}) => void;
|
|
29
29
|
setScrollToPage: (fn: (pageNumber: number) => void) => void;
|
|
30
|
+
setScrollToElement: (fn: (el: HTMLElement) => void) => void;
|
|
30
31
|
}
|
|
31
32
|
export declare function provideViewerSearch(): ViewerSearchState;
|
|
32
33
|
export declare function useViewerSearch(): ViewerSearchState;
|
|
@@ -26,12 +26,16 @@ export function provideViewerSearch() {
|
|
|
26
26
|
let inputElement = null;
|
|
27
27
|
let _searchIndex = null;
|
|
28
28
|
let _scrollToPage = null;
|
|
29
|
+
let _scrollToElement = null;
|
|
29
30
|
function setSearchIndex(index) {
|
|
30
31
|
_searchIndex = index;
|
|
31
32
|
}
|
|
32
33
|
function setScrollToPage(fn) {
|
|
33
34
|
_scrollToPage = fn;
|
|
34
35
|
}
|
|
36
|
+
function setScrollToElement(fn) {
|
|
37
|
+
_scrollToElement = fn;
|
|
38
|
+
}
|
|
35
39
|
function getSortedPageNumbers() {
|
|
36
40
|
return [...pageLayers.keys()].sort((a, b) => a - b);
|
|
37
41
|
}
|
|
@@ -186,10 +190,14 @@ export function provideViewerSearch() {
|
|
|
186
190
|
}
|
|
187
191
|
prevEnd = end;
|
|
188
192
|
if (isSelected && shouldScrollSelected && selectedSpan) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
+
if (_scrollToElement) {
|
|
194
|
+
_scrollToElement(selectedSpan);
|
|
195
|
+
} else {
|
|
196
|
+
selectedSpan.scrollIntoView({
|
|
197
|
+
block: "start",
|
|
198
|
+
inline: "center"
|
|
199
|
+
});
|
|
200
|
+
}
|
|
193
201
|
}
|
|
194
202
|
}
|
|
195
203
|
if (prevEnd) {
|
|
@@ -309,6 +317,7 @@ export function provideViewerSearch() {
|
|
|
309
317
|
inputElement = null;
|
|
310
318
|
_searchIndex = null;
|
|
311
319
|
_scrollToPage = null;
|
|
320
|
+
_scrollToElement = null;
|
|
312
321
|
}
|
|
313
322
|
function setQuery(value) {
|
|
314
323
|
query.value = value;
|
|
@@ -404,7 +413,8 @@ export function provideViewerSearch() {
|
|
|
404
413
|
setInputElement,
|
|
405
414
|
focusInput,
|
|
406
415
|
setSearchIndex,
|
|
407
|
-
setScrollToPage
|
|
416
|
+
setScrollToPage,
|
|
417
|
+
setScrollToElement
|
|
408
418
|
};
|
|
409
419
|
provide(VIEWER_SEARCH_KEY, state);
|
|
410
420
|
return state;
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export type { ExportPdfOptions } from './annotation/pdf-export/export.js';
|
|
2
2
|
export type { ViewerTabItem, AddTabOptions } from './components/ViewerTabs.vue.js';
|
|
3
|
+
export type { SignatureData, SignatureHandlers } from './annotation/engine/types.js';
|
|
4
|
+
export type { ViewMode } from './composables/viewMode.js';
|
package/dist/types.d.mts
CHANGED