@netless/app-presentation 0.1.9-beta.1 → 0.1.9-beta.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netless/app-presentation",
3
- "version": "0.1.9-beta.1",
3
+ "version": "0.1.9-beta.3",
4
4
  "description": "Netless App for presentation slides (images)",
5
5
  "author": "netless",
6
6
  "license": "MIT",
@@ -33,6 +33,7 @@
33
33
  "@hyrious/esbuild-dev": "^0.10.5",
34
34
  "@netless/fastboard": "^1.0.6",
35
35
  "@netless/window-manager": "^1.0.4",
36
+ "@types/lodash": "^4.17.20",
36
37
  "@types/node": "^20.11.30",
37
38
  "@wopjs/disposable": "^0.1.5",
38
39
  "@wopjs/dom": "^0.1.3",
@@ -42,5 +43,8 @@
42
43
  "sass": "^1.72.0",
43
44
  "vanilla-lazyload": "^18.0.0",
44
45
  "vite": "^5.2.6"
46
+ },
47
+ "dependencies": {
48
+ "lodash": "^4.17.21"
45
49
  }
46
50
  }
@@ -6,7 +6,7 @@ import { listen } from '@wopjs/dom'
6
6
  import styles from './style.scss?inline'
7
7
  import { Presentation, type PresentationConfig, type PresentationPage } from "./presentation";
8
8
  import { readable, type Readable } from "./store";
9
- import { Scrollbar } from "./scrollbar";
9
+ import { Scrollbar, type ScrollbarEventCallback } from "./scrollbar";
10
10
 
11
11
  export type Logger = (...data: any[]) => void
12
12
 
@@ -45,6 +45,7 @@ export interface PresentationAppOptions {
45
45
  useScrollbar?: boolean;
46
46
  /** debounceSync is used to set the presentation debounce sync, it will be used in the presentation, and the presentation will be debounce sync when the app is initialized */
47
47
  debounceSync?: boolean;
48
+ scrollbarEventCallback?: ScrollbarEventCallback
48
49
  }
49
50
 
50
51
  export interface PresentationController {
@@ -67,6 +68,10 @@ export interface PresentationController {
67
68
  setDocsViewReadonly: (bol: boolean) => void;
68
69
  /** set the presentation readonly */
69
70
  setReadonly: (bol: boolean) => void;
71
+ /** move the camera */
72
+ moveCamera: (camera: { centerX: number, centerY: number, scale: number }) => void;
73
+ /** get the origin scale */
74
+ getOriginScale: () => number;
70
75
  }
71
76
 
72
77
  export { styles }
@@ -375,16 +380,35 @@ export const NetlessAppPresentation: NetlessApp<{}, {}, PresentationAppOptions,
375
380
  app.setReadonly(!isWritable)
376
381
  }))
377
382
 
383
+ const getPageSize = () => {
384
+ const { width, height } = app.page() || {}
385
+ return { width: width || 0, height: height || 0 }
386
+ }
387
+
388
+ const getOriginScale = () => {
389
+ const { size } = view;
390
+ const { width, height } = getPageSize();
391
+ return Math.min(size.height / height, size.width / width);
392
+ }
393
+
394
+ const moveCamera = (camera: { centerX: number, centerY: number, scale: number }) => {
395
+ if (context.getIsWritable()) {
396
+ view.moveCamera({...camera, animationMode: 'immediately' as AnimationMode});
397
+ syncView();
398
+ return;
399
+ }
400
+ throw new Error('[Presentation]: moveCamera must be called in writable room')
401
+ }
402
+
378
403
  let scrollbar:Scrollbar | undefined;
379
404
  if (options.useScrollbar) {
380
405
  dispose.make(() => {
381
406
  scrollbar = new Scrollbar(app.contentDOM, {
382
- getPageSize: () => {
383
- const { width, height } = app.page() || {}
384
- return { width: width || 0, height: height || 0 }
385
- },
407
+ getPageSize,
408
+ getOriginScale,
409
+ syncView,
386
410
  getWritable: () => context.getIsWritable(),
387
- syncView: () => syncView()
411
+ scrollbarEventCallback: options.scrollbarEventCallback
388
412
  }, view)
389
413
  return () => scrollbar?.destroy()
390
414
  })
@@ -516,7 +540,7 @@ export const NetlessAppPresentation: NetlessApp<{}, {}, PresentationAppOptions,
516
540
  }
517
541
  }
518
542
 
519
- const controller: PresentationController = { app, view, context, jumpPage, prevPage, nextPage, pageState, toPdf, log, setDocsViewReadonly, setReadonly }
543
+ const controller: PresentationController = { app, view, context, jumpPage, prevPage, nextPage, pageState, toPdf, log, setDocsViewReadonly, setReadonly, moveCamera, getOriginScale }
520
544
 
521
545
  dispose.add(listen(window, 'message', (ev: MessageEvent<"@netless/_presentation_">) => {
522
546
  if (ev.data === "@netless/_presentation_") {
@@ -1,6 +1,7 @@
1
1
  import './style.scss?inline';
2
2
  import type { View, AnimationMode } from "@netless/window-manager";
3
3
  import { makeDraggable } from "./Draggable";
4
+ import { debounce } from 'lodash';
4
5
 
5
6
  export interface ScrollbarOption {
6
7
  readonly?: boolean;
@@ -9,9 +10,19 @@ export interface ScrollbarOption {
9
10
  height: number;
10
11
  };
11
12
  getWritable: () => boolean;
13
+ getOriginScale: () => number;
12
14
  syncView: () => void;
15
+ scrollbarEventCallback?: ScrollbarEventCallback;
13
16
  }
14
17
 
18
+ export interface ScrollbarEventCallback {
19
+ onScrollbarDragStart?: () => void;
20
+ onScrollbarDragEnd?: () => void;
21
+ onScrollbarDragX?: (x: number) => void;
22
+ onScrollbarDragY?: (y: number) => void;
23
+ onScrollCameraUpdated?: (originScale: number, scale: number) => void;
24
+ }
25
+
15
26
  export class Scrollbar {
16
27
  readonly namespace = "netless-app-presentation"
17
28
  private container: HTMLElement;
@@ -24,6 +35,7 @@ export class Scrollbar {
24
35
  }
25
36
  },
26
37
  getWritable: () => false,
38
+ getOriginScale: () => 1,
27
39
  syncView: () => void 0
28
40
  };
29
41
  private view: View;
@@ -51,12 +63,6 @@ export class Scrollbar {
51
63
  return `${this.namespace}-${className}`
52
64
  }
53
65
 
54
- private getOriginScale() {
55
- const { size } = this.view;
56
- const { width, height } = this.option.getPageSize();
57
- return Math.min(size.height / height, size.width / width);
58
- }
59
-
60
66
  setReadonly(bol: boolean){
61
67
  this.readonly = bol;
62
68
  if (this.readonly) {
@@ -66,10 +72,10 @@ export class Scrollbar {
66
72
  }
67
73
  }
68
74
 
69
- onCameraUpdated = () => {
75
+ onCameraUpdated = debounce(() => {
70
76
  if (this.readonly) return;
71
77
  const { scale, centerX, centerY } = this.view.camera;
72
- const originScale = this.getOriginScale();
78
+ const originScale = this.option.getOriginScale();
73
79
  const ratio = Math.round(originScale / scale * 1000) / 1000;
74
80
  const scrollX = Math.round(centerX * originScale);
75
81
  const scrollY = Math.round(centerY * originScale);
@@ -83,8 +89,8 @@ export class Scrollbar {
83
89
  this.scrollbarY.style.display = ratio === 1 ? 'none' : 'block';
84
90
  this.scrollbarY.style.transform = `translateY(${scrollY}px)`;
85
91
  }
86
- }
87
-
92
+ this.option.scrollbarEventCallback?.onScrollCameraUpdated?.(originScale, scale);
93
+ }, 50)
88
94
 
89
95
  onDragStart = () => {
90
96
  const camera = this.view.camera;
@@ -93,11 +99,12 @@ export class Scrollbar {
93
99
  centerY: camera.centerY,
94
100
  scale: camera.scale,
95
101
  }
102
+ this.option.scrollbarEventCallback?.onScrollbarDragStart?.();
96
103
  }
97
104
 
98
105
  getScrollXRange(camera: {scale: number}) {
99
106
  const { width } = this.option.getPageSize();
100
- const originScale = this.getOriginScale();
107
+ const originScale = this.option.getOriginScale();
101
108
  const scale = camera.scale;
102
109
  const ratio = Math.round(originScale / scale * 1000) / 1000;
103
110
  const scrollWidth = width * (1-ratio);
@@ -108,7 +115,7 @@ export class Scrollbar {
108
115
 
109
116
  getScrollYRange(camera: {scale: number}) {
110
117
  const { height } = this.option.getPageSize();
111
- const originScale = this.getOriginScale();
118
+ const originScale = this.option.getOriginScale();
112
119
  const scale = camera.scale;
113
120
  const ratio = Math.round(originScale / scale * 1000) / 1000;
114
121
  const scrollHeight = height * (1-ratio);
@@ -120,7 +127,7 @@ export class Scrollbar {
120
127
  onDragX = (transformedDrag: {x: number, y: number}) => {
121
128
  if (this.cameraCache) {
122
129
  const {x} = transformedDrag;
123
- const originScale = this.getOriginScale();
130
+ const originScale = this.option.getOriginScale();
124
131
  const { minX, maxX } = this.getScrollXRange(this.cameraCache);
125
132
  const scrollX = Math.round(x / originScale);
126
133
  let centerX = scrollX + this.cameraCache.centerX;
@@ -133,13 +140,14 @@ export class Scrollbar {
133
140
  centerX,
134
141
  animationMode: 'immediately' as AnimationMode
135
142
  })
143
+ this.option.scrollbarEventCallback?.onScrollbarDragY?.(centerX);
136
144
  }
137
145
  }
138
146
 
139
147
  onDragY = (transformedDrag: {x: number, y: number}) => {
140
148
  if(this.cameraCache){
141
149
  const {y} = transformedDrag;
142
- const originScale = this.getOriginScale();
150
+ const originScale = this.option.getOriginScale();
143
151
  const { minY, maxY } = this.getScrollYRange(this.cameraCache);
144
152
  const scrollY = Math.round(y / originScale);
145
153
  let centerY = scrollY + this.cameraCache.centerY;
@@ -151,13 +159,15 @@ export class Scrollbar {
151
159
  this.view.moveCamera({
152
160
  centerY,
153
161
  animationMode: 'immediately' as AnimationMode
154
- })
162
+ })
163
+ this.option.scrollbarEventCallback?.onScrollbarDragY?.(centerY);
155
164
  }
156
165
  }
157
166
 
158
167
  onDragEnd = () => {
159
168
  this.cameraCache = undefined
160
169
  this.option.syncView();
170
+ this.option.scrollbarEventCallback?.onScrollbarDragEnd?.();
161
171
  }
162
172
 
163
173
  private init() {
@@ -200,7 +210,7 @@ export class Scrollbar {
200
210
  this.scrollContainer.append(this.scrollbarContainerX, this.scrollbarContainerY);
201
211
  this.container.appendChild(this.scrollContainer);
202
212
  this.view.callbacks.on('onCameraUpdated', this.onCameraUpdated);
203
- this.onCameraUpdated()
213
+ this.onCameraUpdated();
204
214
  }
205
215
 
206
216
  destroy() {