@netless/forge-whiteboard 0.1.7 → 0.1.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.
@@ -28828,6 +28828,14 @@ var Camera = class extends EventEmitter8 {
28828
28828
  hasPermission;
28829
28829
  gesture;
28830
28830
  inherentScale = 1;
28831
+ maxScale;
28832
+ initSize;
28833
+ bound;
28834
+ boundTiemoutId;
28835
+ enableByMouse = true;
28836
+ enableByTouch = true;
28837
+ boundaryColor = "#F44336";
28838
+ enableBoundaryHighlight = true;
28831
28839
  get inherentMatrix() {
28832
28840
  const inherentMatrix = new this.scope.Matrix();
28833
28841
  inherentMatrix.scale(this.inherentScale, [0, 0]);
@@ -28837,8 +28845,12 @@ var Camera = class extends EventEmitter8 {
28837
28845
  const view = this.scope.project.view;
28838
28846
  return 1 / view.matrix.scaling.x;
28839
28847
  }
28840
- constructor(dom, userManager, scope, whiteboardAttrsMap, hasPermission, requestUserMap, paperSize, domSize) {
28848
+ constructor(initSize, maxScale, dom, userManager, scope, whiteboardAttrsMap, hasPermission, requestUserMap, paperSize, domSize) {
28841
28849
  super();
28850
+ this.maxScale = maxScale;
28851
+ this.bound = window.document.createElement("div");
28852
+ this.bound.style.cssText = `transition: box-shadow 100ms;pointer-events:none;z-index:99;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);`;
28853
+ this.initSize = initSize;
28842
28854
  this.hasPermission = hasPermission;
28843
28855
  this.paperSize = paperSize;
28844
28856
  this.domSize = domSize;
@@ -28847,6 +28859,7 @@ var Camera = class extends EventEmitter8 {
28847
28859
  this.requestUserMap = requestUserMap;
28848
28860
  this.userManager = userManager;
28849
28861
  this.dom = dom;
28862
+ this.dom.appendChild(this.bound);
28850
28863
  this.dom.addEventListener("wheel", this.handleWheel, { passive: false, capture: true });
28851
28864
  this.dom.addEventListener("touchstart", (e) => {
28852
28865
  e.preventDefault();
@@ -28867,17 +28880,32 @@ var Camera = class extends EventEmitter8 {
28867
28880
  this.whiteboardAttrsMap.observe(this.handleMainCameraChange);
28868
28881
  this.gesture = new Gesture(this.dom, this.scope);
28869
28882
  this.gesture.on("offset", (x, y) => {
28883
+ if (!this.enableByTouch) {
28884
+ return;
28885
+ }
28870
28886
  const matrix = new this.scope.Matrix();
28871
28887
  matrix.translate({ x: x * this.translateScale, y: y * this.translateScale });
28872
- this.appendViewMatrix(matrix);
28888
+ this.updateViewMatrix(matrix);
28873
28889
  });
28874
28890
  this.gesture.on("scale", (scale, center) => {
28891
+ if (!this.enableByTouch) {
28892
+ return;
28893
+ }
28875
28894
  const matrix = new this.scope.Matrix();
28876
28895
  const paperPoint = this.scope.project.view.viewToProject(center);
28877
28896
  matrix.scale(scale, paperPoint);
28878
- this.appendViewMatrix(matrix);
28897
+ this.updateViewMatrix(matrix);
28879
28898
  });
28880
28899
  }
28900
+ highlightBound() {
28901
+ if (this.enableBoundaryHighlight) {
28902
+ this.bound.style.boxShadow = `inset 0px 0px 6px 2px ${this.boundaryColor}`;
28903
+ window.clearTimeout(this.boundTiemoutId);
28904
+ this.boundTiemoutId = window.setTimeout(() => {
28905
+ this.bound.style.boxShadow = `none`;
28906
+ }, 100);
28907
+ }
28908
+ }
28881
28909
  updateInherentScale(scale) {
28882
28910
  this.inherentScale = scale;
28883
28911
  }
@@ -28894,6 +28922,9 @@ var Camera = class extends EventEmitter8 {
28894
28922
  return new this.scope.Matrix(matrixValue);
28895
28923
  }
28896
28924
  triggerZoom() {
28925
+ const [width, height] = this.domSize();
28926
+ this.bound.style.width = `${width}px`;
28927
+ this.bound.style.height = `${height}px`;
28897
28928
  this.emit("zoom", this.inherentMatrix.appended(this.getActiveMatrix()));
28898
28929
  }
28899
28930
  resetViewMatrixToFlow(userId) {
@@ -28972,29 +29003,89 @@ var Camera = class extends EventEmitter8 {
28972
29003
  }
28973
29004
  }
28974
29005
  };
28975
- appendViewMatrix(matrix) {
29006
+ validNextMatrix(next) {
29007
+ let shouldHighlightBound = false;
29008
+ const maxTranslate = (this.maxScale - 1) / 2;
29009
+ const maxScale = maxTranslate * 2 + 1;
29010
+ const minScale = 1 / maxScale;
29011
+ if (next.a > maxScale) {
29012
+ const tx = next.tx / next.a;
29013
+ next.a = maxScale;
29014
+ next.tx = tx * maxScale;
29015
+ shouldHighlightBound = true;
29016
+ }
29017
+ if (next.a < minScale) {
29018
+ const tx = next.tx / next.a;
29019
+ next.a = minScale;
29020
+ next.tx = tx * minScale;
29021
+ shouldHighlightBound = true;
29022
+ }
29023
+ if (next.d > maxScale) {
29024
+ const ty = next.ty / next.d;
29025
+ next.d = maxScale;
29026
+ next.ty = ty * maxScale;
29027
+ shouldHighlightBound = true;
29028
+ }
29029
+ if (next.d < minScale) {
29030
+ const ty = next.ty / next.d;
29031
+ next.d = minScale;
29032
+ next.ty = ty * minScale;
29033
+ shouldHighlightBound = true;
29034
+ }
29035
+ const maxTranslateX = this.initSize.width * maxTranslate;
29036
+ if (next.tx / next.a > maxTranslateX) {
29037
+ next.tx = maxTranslateX * next.a;
29038
+ shouldHighlightBound = true;
29039
+ }
29040
+ const minTx = (this.initSize.width * (maxTranslate + 1) - this.scope.project.view.bounds.width) * -next.a;
29041
+ if (next.tx < minTx) {
29042
+ next.tx = minTx;
29043
+ shouldHighlightBound = true;
29044
+ }
29045
+ const maxTranslateY = this.initSize.height * maxTranslate;
29046
+ if (next.ty / next.d > maxTranslateY) {
29047
+ next.ty = maxTranslateY * next.d;
29048
+ shouldHighlightBound = true;
29049
+ }
29050
+ const minTy = (this.initSize.height * (maxTranslate + 1) - this.scope.project.view.bounds.height) * -next.d;
29051
+ if (next.ty < minTy) {
29052
+ next.ty = minTy;
29053
+ shouldHighlightBound = true;
29054
+ }
29055
+ if (shouldHighlightBound) {
29056
+ this.highlightBound();
29057
+ }
29058
+ return next;
29059
+ }
29060
+ updateViewMatrix(matrix, append = true) {
28976
29061
  const userMap = this.requestUserMap(this.userManager.selfId);
28977
29062
  const cameraMode = userMap.get(WhiteboardKeys.cameraMode);
28978
- if (cameraMode === "free" || cameraMode === "main") {
29063
+ let next = matrix;
29064
+ if (append) {
28979
29065
  const currentMatrixValue = userMap.get(WhiteboardKeys.viewMatrix);
28980
29066
  const current = new this.scope.Matrix(currentMatrixValue);
28981
- const next = current.appended(matrix);
28982
- if (next.scaling.x <= 3 && next.scaling.x >= 0.3) {
28983
- userMap.set(WhiteboardKeys.viewMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
29067
+ next = current.appended(matrix);
29068
+ }
29069
+ if (this.maxScale >= 1) {
29070
+ next = this.validNextMatrix(next);
29071
+ } else {
29072
+ if (!(next.scaling.x <= 3 && next.scaling.x >= 0.2) && append) {
29073
+ return;
28984
29074
  }
28985
29075
  }
29076
+ if (cameraMode === "free" || cameraMode === "main") {
29077
+ userMap.set(WhiteboardKeys.viewMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
29078
+ }
28986
29079
  if (cameraMode === "main") {
28987
29080
  if (this.hasPermission(32 /* mainView */)) {
28988
- const currentMatrixValue = this.whiteboardAttrsMap.get(WhiteboardKeys.viewMatrix);
28989
- const current = new this.scope.Matrix(currentMatrixValue);
28990
- const next = current.appended(matrix);
28991
- if (next.scaling.x <= 3 && next.scaling.x >= 0.3) {
28992
- this.whiteboardAttrsMap.set(WhiteboardKeys.viewMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
28993
- }
29081
+ this.whiteboardAttrsMap.set(WhiteboardKeys.viewMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
28994
29082
  }
28995
29083
  }
28996
29084
  }
28997
29085
  handleWheel = (evt) => {
29086
+ if (!this.enableByMouse) {
29087
+ return;
29088
+ }
28998
29089
  evt.preventDefault();
28999
29090
  evt.stopImmediatePropagation();
29000
29091
  evt.stopPropagation();
@@ -29018,22 +29109,33 @@ var Camera = class extends EventEmitter8 {
29018
29109
  } else {
29019
29110
  scale = 1 + Math.abs(deltaY / wheelDelta);
29020
29111
  }
29112
+ scale = Math.max(0.9, scale);
29113
+ scale = Math.min(1.1, scale);
29021
29114
  matrix.scale(scale, paperPoint);
29022
- this.appendViewMatrix(matrix);
29115
+ this.updateViewMatrix(matrix);
29023
29116
  } else {
29024
29117
  const deltaX = this.lastDelta.x + evt.deltaX;
29025
29118
  const deltaY = this.lastDelta.y + evt.deltaY;
29026
29119
  const matrix = new this.scope.Matrix();
29027
29120
  matrix.translate({ x: -deltaX * window.devicePixelRatio, y: -deltaY * window.devicePixelRatio });
29028
- this.appendViewMatrix(matrix);
29121
+ this.updateViewMatrix(matrix);
29029
29122
  }
29030
29123
  this.lastTriggerTime = Date.now();
29031
29124
  this.lastDelta = { x: 0, y: 0 };
29032
29125
  };
29126
+ scale(value) {
29127
+ const matrix = new this.scope.Matrix();
29128
+ matrix.scale(value, this.scope.project.view.bounds.center);
29129
+ this.updateViewMatrix(matrix);
29130
+ }
29033
29131
  translate(offsetX, offsetY) {
29034
29132
  const matrix = new this.scope.Matrix();
29035
29133
  matrix.translate({ x: offsetX, y: offsetY });
29036
- this.appendViewMatrix(matrix);
29134
+ this.updateViewMatrix(matrix);
29135
+ }
29136
+ reset() {
29137
+ this.updateViewMatrix(new this.scope.Matrix(), false);
29138
+ this.lastDelta = { x: 0, y: 0 };
29037
29139
  }
29038
29140
  };
29039
29141
 
@@ -29142,6 +29244,22 @@ var Whiteboard = class extends EventEmitter9 {
29142
29244
  * 描边宽度
29143
29245
  */
29144
29246
  strokeWidth;
29247
+ /**
29248
+ * 是否允许用鼠标/触控板进行缩放和拖动画布
29249
+ */
29250
+ enableCameraByMouse;
29251
+ /**
29252
+ * 是否允许触摸屏上触摸事件进行缩放和拖动画布
29253
+ */
29254
+ enableCameraByTouch;
29255
+ /**
29256
+ * 相机超出边界时, 警示阴影的颜色, 默认 "#F44336"
29257
+ */
29258
+ cameraBoundaryColor;
29259
+ /**
29260
+ * 是否允许相机超出边界时, 警示高亮阴影, 默认 true
29261
+ */
29262
+ enableCameraBoundaryHighlight;
29145
29263
  getElementAttribute;
29146
29264
  setElementAttribute;
29147
29265
  getCurrentTool;
@@ -29183,6 +29301,21 @@ var Whiteboard = class extends EventEmitter9 {
29183
29301
  * 清空当前页面
29184
29302
  */
29185
29303
  clearPage;
29304
+ /**
29305
+ * 平移画布
29306
+ * @param {number} offsetX - x 轴偏移量
29307
+ * @param {number} offsetY - y 轴偏移量
29308
+ */
29309
+ translateCamera;
29310
+ /**
29311
+ * 以画布中心为固定点缩放画布
29312
+ * @param {number} scale - 缩放比例
29313
+ */
29314
+ scaleCamera;
29315
+ /**
29316
+ * 重置画布, 将画布缩放比例重置为 1, 平移量重置为 [0, 0]
29317
+ */
29318
+ resetCamera;
29186
29319
  /**
29187
29320
  * 为 userId 指定的用户设置页面, 调用需要有 `WhiteboardPermissionFlag.setOthersView` 权限,
29188
29321
  * userId 指定的用户的视角模式应为 `free`, 否则调用无效
@@ -29220,17 +29353,33 @@ var Whiteboard = class extends EventEmitter9 {
29220
29353
  */
29221
29354
  redo;
29222
29355
  /**
29223
- * 光栅化指定页面
29224
- * @param {string | number | undefined} page 指定页码, string 指示页码 id, number 指示 IndexedNavigation 页面索引, undefined 指示光栅化当前页
29225
- * @return {Promise<string>} 异步返回光栅化结果, 图片 png 对应的 base64 url, 如果页面不存在返回 null
29356
+ * 光栅化指定页面的可视区域
29357
+ * @param {number} scale 缩放比例
29358
+ * @param {string=} page string 指示页码 id; number 指示 IndexedNavigation 页面索引; undefined 指示光栅化当前页
29359
+ * @return {Promise<string>} 异步返回光栅化结果, 图片 png 对应的 base64 url. 如果页面不存在返回 null
29360
+ */
29361
+ rasterizeViewport;
29362
+ /**
29363
+ * 光栅化指定页面包含所有元素的包围盒
29364
+ * @param {number} outputMaxWidth 输出图片最大宽度, 在保证高宽比的情况下限值输出图片的宽度小于等于此值
29365
+ * @param {number} outputMaxHeight 输出图片最大高度, 在保证高宽比的情况下限值输出图片的高度小于等于此值
29366
+ * @param {string=} page string 指示页码 id; number 指示 IndexedNavigation 页面索引; undefined 指示光栅化当前页
29367
+ * @return {Promise<string>} 异步返回光栅化结果, 图片 png 对应的 base64 url. 如果页面不存在返回 null
29368
+ */
29369
+ rasterizeElementsBounds;
29370
+ /**
29371
+ * 光栅化指定页面最大缩放区域, 如果没有指定 `WhiteboardOption.maxScaleRatio` 或者指定的值为 -1, 则按 `rasterizeViewport` 返回结果.
29372
+ * @param {number} scale 缩放比例
29373
+ * @param {string=} page string 指示页码 id; number 指示 IndexedNavigation 页面索引; undefined 指示光栅化当前页
29374
+ * @return {Promise<string>} 异步返回光栅化结果, 图片 png 对应的 base64 url. 如果页面不存在返回 null
29226
29375
  */
29227
- rasterize;
29376
+ rasterizeMaxBounds;
29228
29377
  /**
29229
29378
  * 设置输入类型, "pen" 仅接受手写笔输入, "any" 接受任意输入, 当首次侦测到手写笔输入时, 将自动切换为 "pen"
29230
29379
  * @param {"pen" | "any"} type 输入类型
29231
29380
  */
29232
29381
  setInputType;
29233
- insertImage;
29382
+ // public insertImage!: (src: string) => void;
29234
29383
  constructor(view) {
29235
29384
  super();
29236
29385
  this.view = view;
@@ -30132,13 +30281,28 @@ var WhiteboardApplication = class extends AbstractApplication {
30132
30281
  this.emitter.redo = () => {
30133
30282
  this.undoManager?.redo();
30134
30283
  };
30135
- this.emitter.rasterize = (page) => {
30136
- return this.rasterize(page);
30284
+ this.emitter.rasterizeViewport = (scale, page) => {
30285
+ return this.rasterize(1, 1, scale, "viewport", page);
30286
+ };
30287
+ this.emitter.rasterizeElementsBounds = (outputMaxWidth, outputMaxHeight, page) => {
30288
+ return this.rasterize(outputMaxWidth, outputMaxHeight, 1, "bounds", page);
30289
+ };
30290
+ this.emitter.rasterizeMaxBounds = (scale, page) => {
30291
+ return this.rasterize(1, 1, scale, "maxScale", page);
30137
30292
  };
30138
30293
  this.emitter.setInputType = (type) => {
30139
30294
  this.inputType = type;
30140
30295
  this.emitter.emit("inputTypeChange", this.inputType);
30141
30296
  };
30297
+ this.emitter.translateCamera = (offsetX, offsetY) => {
30298
+ this.camera.translate(offsetX, offsetY);
30299
+ };
30300
+ this.emitter.scaleCamera = (scale) => {
30301
+ this.camera.scale(scale);
30302
+ };
30303
+ this.emitter.resetCamera = () => {
30304
+ this.camera.reset();
30305
+ };
30142
30306
  this.emitter.on("error", (errorCode, errorMessage) => {
30143
30307
  log3("WhiteboardApplicationError", {
30144
30308
  errorCode,
@@ -30202,6 +30366,39 @@ var WhiteboardApplication = class extends AbstractApplication {
30202
30366
  that.toolbarModel.dashArray = v;
30203
30367
  }
30204
30368
  });
30369
+ Object.defineProperty(this.emitter, "enableCameraByMouse", {
30370
+ get() {
30371
+ return that.camera.enableByMouse;
30372
+ },
30373
+ set(value) {
30374
+ that.camera.enableByMouse = value;
30375
+ }
30376
+ });
30377
+ Object.defineProperty(this.emitter, "enableCameraByTouch", {
30378
+ get() {
30379
+ return that.camera.enableByTouch;
30380
+ },
30381
+ set(value) {
30382
+ that.camera.enableByTouch = value;
30383
+ }
30384
+ });
30385
+ Object.defineProperty(this.emitter, "cameraBoundaryColor", {
30386
+ get() {
30387
+ return that.camera.boundaryColor;
30388
+ },
30389
+ set(value) {
30390
+ that.camera.boundaryColor = value;
30391
+ }
30392
+ });
30393
+ Object.defineProperty(this.emitter, "enableCameraBoundaryHighlight", {
30394
+ get() {
30395
+ return that.camera.enableBoundaryHighlight;
30396
+ },
30397
+ set(value) {
30398
+ that.camera.enableBoundaryHighlight = value;
30399
+ }
30400
+ });
30401
+ window["__wb"] = this;
30205
30402
  }
30206
30403
  userMap(userId) {
30207
30404
  return this.getMap(`user/${userId}`);
@@ -30229,7 +30426,12 @@ var WhiteboardApplication = class extends AbstractApplication {
30229
30426
  );
30230
30427
  this.pageModel.on("pagesChange", this.handleLayersChange);
30231
30428
  this.pageModel.on("switchPage", this.handlePageSwitch);
30429
+ if (option.maxScaleRatio && option.maxScaleRatio < 1 && option.maxScaleRatio !== -1) {
30430
+ throw new Error(`[@netless/forge-whiteboard] invalid maxScaleRatio ${option.maxScaleRatio}`);
30431
+ }
30232
30432
  this.camera = new Camera(
30433
+ new this.paperScope.Size(option.width, option.height),
30434
+ option.maxScaleRatio ?? -1,
30233
30435
  this.rootElement,
30234
30436
  this.userManager,
30235
30437
  this.paperScope,
@@ -30645,7 +30847,10 @@ var WhiteboardApplication = class extends AbstractApplication {
30645
30847
  this.emitter.emit("elementDeselected", userId);
30646
30848
  }
30647
30849
  };
30648
- rasterize(page) {
30850
+ ensureScale(outputWidth, outputHeight, originSize) {
30851
+ return Math.min(outputWidth / originSize.width, outputHeight / originSize.height);
30852
+ }
30853
+ rasterize(outputMaxWidth, outputMaxHeight, scale, area, page) {
30649
30854
  let pageId;
30650
30855
  if (typeof page === "string") {
30651
30856
  pageId = page;
@@ -30659,8 +30864,6 @@ var WhiteboardApplication = class extends AbstractApplication {
30659
30864
  return Promise.resolve(null);
30660
30865
  }
30661
30866
  this.snapshotScope.project.activeLayer.removeChildren();
30662
- this.snapshotScope.view.matrix = this.paperScope.project.view.matrix.clone();
30663
- this.snapshotScope.view.viewSize = this.paperScope.project.view.viewSize.clone();
30664
30867
  for (const key of Array.from(renderableModel.elements.keys())) {
30665
30868
  let elementModel = null;
30666
30869
  if (renderableModel.elementModels.has(key)) {
@@ -30681,6 +30884,29 @@ var WhiteboardApplication = class extends AbstractApplication {
30681
30884
  }
30682
30885
  }
30683
30886
  }
30887
+ if (area === "bounds") {
30888
+ const bounds = this.snapshotScope.project.activeLayer.bounds;
30889
+ const scale2 = this.ensureScale(outputMaxWidth, outputMaxHeight, bounds.size);
30890
+ const matrix = new this.paperScope.Matrix();
30891
+ matrix.scale(scale2);
30892
+ matrix.translate({ x: -bounds.x, y: -bounds.y });
30893
+ this.snapshotScope.view.viewSize = bounds.size.multiply(scale2);
30894
+ this.snapshotScope.view.matrix = matrix;
30895
+ } else if (area === "maxScale" && this.option.maxScaleRatio && this.option.maxScaleRatio !== -1) {
30896
+ const width = this.option.width * this.option.maxScaleRatio;
30897
+ const height = this.option.height * this.option.maxScaleRatio;
30898
+ const offsetX = this.option.width * (this.option.maxScaleRatio - 1) / 2;
30899
+ const offsetY = this.option.height * (this.option.maxScaleRatio - 1) / 2;
30900
+ const matrix = new this.paperScope.Matrix();
30901
+ matrix.scale(scale);
30902
+ matrix.translate({ x: offsetX, y: offsetY });
30903
+ this.snapshotScope.view.viewSize = new this.snapshotScope.Size(width, height).multiply(scale);
30904
+ this.snapshotScope.view.matrix = matrix;
30905
+ } else {
30906
+ this.snapshotScope.view.matrix = this.paperScope.project.view.matrix.clone();
30907
+ this.snapshotScope.view.matrix.scale(scale, this.paperScope.project.view.bounds.topLeft);
30908
+ this.snapshotScope.view.viewSize = this.paperScope.project.view.viewSize.clone().multiply(scale);
30909
+ }
30684
30910
  return new Promise((resolve) => {
30685
30911
  setTimeout(() => {
30686
30912
  resolve(this.snapshotScope.view.element.toDataURL("image/png"));