@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.
@@ -28838,6 +28838,14 @@ var Camera = class extends import_eventemitter38.default {
28838
28838
  hasPermission;
28839
28839
  gesture;
28840
28840
  inherentScale = 1;
28841
+ maxScale;
28842
+ initSize;
28843
+ bound;
28844
+ boundTiemoutId;
28845
+ enableByMouse = true;
28846
+ enableByTouch = true;
28847
+ boundaryColor = "#F44336";
28848
+ enableBoundaryHighlight = true;
28841
28849
  get inherentMatrix() {
28842
28850
  const inherentMatrix = new this.scope.Matrix();
28843
28851
  inherentMatrix.scale(this.inherentScale, [0, 0]);
@@ -28847,8 +28855,12 @@ var Camera = class extends import_eventemitter38.default {
28847
28855
  const view = this.scope.project.view;
28848
28856
  return 1 / view.matrix.scaling.x;
28849
28857
  }
28850
- constructor(dom, userManager, scope, whiteboardAttrsMap, hasPermission, requestUserMap, paperSize, domSize) {
28858
+ constructor(initSize, maxScale, dom, userManager, scope, whiteboardAttrsMap, hasPermission, requestUserMap, paperSize, domSize) {
28851
28859
  super();
28860
+ this.maxScale = maxScale;
28861
+ this.bound = window.document.createElement("div");
28862
+ this.bound.style.cssText = `transition: box-shadow 100ms;pointer-events:none;z-index:99;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);`;
28863
+ this.initSize = initSize;
28852
28864
  this.hasPermission = hasPermission;
28853
28865
  this.paperSize = paperSize;
28854
28866
  this.domSize = domSize;
@@ -28857,6 +28869,7 @@ var Camera = class extends import_eventemitter38.default {
28857
28869
  this.requestUserMap = requestUserMap;
28858
28870
  this.userManager = userManager;
28859
28871
  this.dom = dom;
28872
+ this.dom.appendChild(this.bound);
28860
28873
  this.dom.addEventListener("wheel", this.handleWheel, { passive: false, capture: true });
28861
28874
  this.dom.addEventListener("touchstart", (e) => {
28862
28875
  e.preventDefault();
@@ -28877,17 +28890,32 @@ var Camera = class extends import_eventemitter38.default {
28877
28890
  this.whiteboardAttrsMap.observe(this.handleMainCameraChange);
28878
28891
  this.gesture = new Gesture(this.dom, this.scope);
28879
28892
  this.gesture.on("offset", (x, y) => {
28893
+ if (!this.enableByTouch) {
28894
+ return;
28895
+ }
28880
28896
  const matrix = new this.scope.Matrix();
28881
28897
  matrix.translate({ x: x * this.translateScale, y: y * this.translateScale });
28882
- this.appendViewMatrix(matrix);
28898
+ this.updateViewMatrix(matrix);
28883
28899
  });
28884
28900
  this.gesture.on("scale", (scale, center) => {
28901
+ if (!this.enableByTouch) {
28902
+ return;
28903
+ }
28885
28904
  const matrix = new this.scope.Matrix();
28886
28905
  const paperPoint = this.scope.project.view.viewToProject(center);
28887
28906
  matrix.scale(scale, paperPoint);
28888
- this.appendViewMatrix(matrix);
28907
+ this.updateViewMatrix(matrix);
28889
28908
  });
28890
28909
  }
28910
+ highlightBound() {
28911
+ if (this.enableBoundaryHighlight) {
28912
+ this.bound.style.boxShadow = `inset 0px 0px 6px 2px ${this.boundaryColor}`;
28913
+ window.clearTimeout(this.boundTiemoutId);
28914
+ this.boundTiemoutId = window.setTimeout(() => {
28915
+ this.bound.style.boxShadow = `none`;
28916
+ }, 100);
28917
+ }
28918
+ }
28891
28919
  updateInherentScale(scale) {
28892
28920
  this.inherentScale = scale;
28893
28921
  }
@@ -28904,6 +28932,9 @@ var Camera = class extends import_eventemitter38.default {
28904
28932
  return new this.scope.Matrix(matrixValue);
28905
28933
  }
28906
28934
  triggerZoom() {
28935
+ const [width, height] = this.domSize();
28936
+ this.bound.style.width = `${width}px`;
28937
+ this.bound.style.height = `${height}px`;
28907
28938
  this.emit("zoom", this.inherentMatrix.appended(this.getActiveMatrix()));
28908
28939
  }
28909
28940
  resetViewMatrixToFlow(userId) {
@@ -28982,29 +29013,89 @@ var Camera = class extends import_eventemitter38.default {
28982
29013
  }
28983
29014
  }
28984
29015
  };
28985
- appendViewMatrix(matrix) {
29016
+ validNextMatrix(next) {
29017
+ let shouldHighlightBound = false;
29018
+ const maxTranslate = (this.maxScale - 1) / 2;
29019
+ const maxScale = maxTranslate * 2 + 1;
29020
+ const minScale = 1 / maxScale;
29021
+ if (next.a > maxScale) {
29022
+ const tx = next.tx / next.a;
29023
+ next.a = maxScale;
29024
+ next.tx = tx * maxScale;
29025
+ shouldHighlightBound = true;
29026
+ }
29027
+ if (next.a < minScale) {
29028
+ const tx = next.tx / next.a;
29029
+ next.a = minScale;
29030
+ next.tx = tx * minScale;
29031
+ shouldHighlightBound = true;
29032
+ }
29033
+ if (next.d > maxScale) {
29034
+ const ty = next.ty / next.d;
29035
+ next.d = maxScale;
29036
+ next.ty = ty * maxScale;
29037
+ shouldHighlightBound = true;
29038
+ }
29039
+ if (next.d < minScale) {
29040
+ const ty = next.ty / next.d;
29041
+ next.d = minScale;
29042
+ next.ty = ty * minScale;
29043
+ shouldHighlightBound = true;
29044
+ }
29045
+ const maxTranslateX = this.initSize.width * maxTranslate;
29046
+ if (next.tx / next.a > maxTranslateX) {
29047
+ next.tx = maxTranslateX * next.a;
29048
+ shouldHighlightBound = true;
29049
+ }
29050
+ const minTx = (this.initSize.width * (maxTranslate + 1) - this.scope.project.view.bounds.width) * -next.a;
29051
+ if (next.tx < minTx) {
29052
+ next.tx = minTx;
29053
+ shouldHighlightBound = true;
29054
+ }
29055
+ const maxTranslateY = this.initSize.height * maxTranslate;
29056
+ if (next.ty / next.d > maxTranslateY) {
29057
+ next.ty = maxTranslateY * next.d;
29058
+ shouldHighlightBound = true;
29059
+ }
29060
+ const minTy = (this.initSize.height * (maxTranslate + 1) - this.scope.project.view.bounds.height) * -next.d;
29061
+ if (next.ty < minTy) {
29062
+ next.ty = minTy;
29063
+ shouldHighlightBound = true;
29064
+ }
29065
+ if (shouldHighlightBound) {
29066
+ this.highlightBound();
29067
+ }
29068
+ return next;
29069
+ }
29070
+ updateViewMatrix(matrix, append = true) {
28986
29071
  const userMap = this.requestUserMap(this.userManager.selfId);
28987
29072
  const cameraMode = userMap.get(WhiteboardKeys.cameraMode);
28988
- if (cameraMode === "free" || cameraMode === "main") {
29073
+ let next = matrix;
29074
+ if (append) {
28989
29075
  const currentMatrixValue = userMap.get(WhiteboardKeys.viewMatrix);
28990
29076
  const current = new this.scope.Matrix(currentMatrixValue);
28991
- const next = current.appended(matrix);
28992
- if (next.scaling.x <= 3 && next.scaling.x >= 0.3) {
28993
- userMap.set(WhiteboardKeys.viewMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
29077
+ next = current.appended(matrix);
29078
+ }
29079
+ if (this.maxScale >= 1) {
29080
+ next = this.validNextMatrix(next);
29081
+ } else {
29082
+ if (!(next.scaling.x <= 3 && next.scaling.x >= 0.2) && append) {
29083
+ return;
28994
29084
  }
28995
29085
  }
29086
+ if (cameraMode === "free" || cameraMode === "main") {
29087
+ userMap.set(WhiteboardKeys.viewMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
29088
+ }
28996
29089
  if (cameraMode === "main") {
28997
29090
  if (this.hasPermission(32 /* mainView */)) {
28998
- const currentMatrixValue = this.whiteboardAttrsMap.get(WhiteboardKeys.viewMatrix);
28999
- const current = new this.scope.Matrix(currentMatrixValue);
29000
- const next = current.appended(matrix);
29001
- if (next.scaling.x <= 3 && next.scaling.x >= 0.3) {
29002
- this.whiteboardAttrsMap.set(WhiteboardKeys.viewMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
29003
- }
29091
+ this.whiteboardAttrsMap.set(WhiteboardKeys.viewMatrix, [next.a, next.b, next.c, next.d, next.tx, next.ty]);
29004
29092
  }
29005
29093
  }
29006
29094
  }
29007
29095
  handleWheel = (evt) => {
29096
+ if (!this.enableByMouse) {
29097
+ return;
29098
+ }
29008
29099
  evt.preventDefault();
29009
29100
  evt.stopImmediatePropagation();
29010
29101
  evt.stopPropagation();
@@ -29028,22 +29119,33 @@ var Camera = class extends import_eventemitter38.default {
29028
29119
  } else {
29029
29120
  scale = 1 + Math.abs(deltaY / wheelDelta);
29030
29121
  }
29122
+ scale = Math.max(0.9, scale);
29123
+ scale = Math.min(1.1, scale);
29031
29124
  matrix.scale(scale, paperPoint);
29032
- this.appendViewMatrix(matrix);
29125
+ this.updateViewMatrix(matrix);
29033
29126
  } else {
29034
29127
  const deltaX = this.lastDelta.x + evt.deltaX;
29035
29128
  const deltaY = this.lastDelta.y + evt.deltaY;
29036
29129
  const matrix = new this.scope.Matrix();
29037
29130
  matrix.translate({ x: -deltaX * window.devicePixelRatio, y: -deltaY * window.devicePixelRatio });
29038
- this.appendViewMatrix(matrix);
29131
+ this.updateViewMatrix(matrix);
29039
29132
  }
29040
29133
  this.lastTriggerTime = Date.now();
29041
29134
  this.lastDelta = { x: 0, y: 0 };
29042
29135
  };
29136
+ scale(value) {
29137
+ const matrix = new this.scope.Matrix();
29138
+ matrix.scale(value, this.scope.project.view.bounds.center);
29139
+ this.updateViewMatrix(matrix);
29140
+ }
29043
29141
  translate(offsetX, offsetY) {
29044
29142
  const matrix = new this.scope.Matrix();
29045
29143
  matrix.translate({ x: offsetX, y: offsetY });
29046
- this.appendViewMatrix(matrix);
29144
+ this.updateViewMatrix(matrix);
29145
+ }
29146
+ reset() {
29147
+ this.updateViewMatrix(new this.scope.Matrix(), false);
29148
+ this.lastDelta = { x: 0, y: 0 };
29047
29149
  }
29048
29150
  };
29049
29151
 
@@ -29152,6 +29254,22 @@ var Whiteboard = class extends import_eventemitter39.default {
29152
29254
  * 描边宽度
29153
29255
  */
29154
29256
  strokeWidth;
29257
+ /**
29258
+ * 是否允许用鼠标/触控板进行缩放和拖动画布
29259
+ */
29260
+ enableCameraByMouse;
29261
+ /**
29262
+ * 是否允许触摸屏上触摸事件进行缩放和拖动画布
29263
+ */
29264
+ enableCameraByTouch;
29265
+ /**
29266
+ * 相机超出边界时, 警示阴影的颜色, 默认 "#F44336"
29267
+ */
29268
+ cameraBoundaryColor;
29269
+ /**
29270
+ * 是否允许相机超出边界时, 警示高亮阴影, 默认 true
29271
+ */
29272
+ enableCameraBoundaryHighlight;
29155
29273
  getElementAttribute;
29156
29274
  setElementAttribute;
29157
29275
  getCurrentTool;
@@ -29193,6 +29311,21 @@ var Whiteboard = class extends import_eventemitter39.default {
29193
29311
  * 清空当前页面
29194
29312
  */
29195
29313
  clearPage;
29314
+ /**
29315
+ * 平移画布
29316
+ * @param {number} offsetX - x 轴偏移量
29317
+ * @param {number} offsetY - y 轴偏移量
29318
+ */
29319
+ translateCamera;
29320
+ /**
29321
+ * 以画布中心为固定点缩放画布
29322
+ * @param {number} scale - 缩放比例
29323
+ */
29324
+ scaleCamera;
29325
+ /**
29326
+ * 重置画布, 将画布缩放比例重置为 1, 平移量重置为 [0, 0]
29327
+ */
29328
+ resetCamera;
29196
29329
  /**
29197
29330
  * 为 userId 指定的用户设置页面, 调用需要有 `WhiteboardPermissionFlag.setOthersView` 权限,
29198
29331
  * userId 指定的用户的视角模式应为 `free`, 否则调用无效
@@ -29230,17 +29363,33 @@ var Whiteboard = class extends import_eventemitter39.default {
29230
29363
  */
29231
29364
  redo;
29232
29365
  /**
29233
- * 光栅化指定页面
29234
- * @param {string | number | undefined} page 指定页码, string 指示页码 id, number 指示 IndexedNavigation 页面索引, undefined 指示光栅化当前页
29235
- * @return {Promise<string>} 异步返回光栅化结果, 图片 png 对应的 base64 url, 如果页面不存在返回 null
29366
+ * 光栅化指定页面的可视区域
29367
+ * @param {number} scale 缩放比例
29368
+ * @param {string=} page string 指示页码 id; number 指示 IndexedNavigation 页面索引; undefined 指示光栅化当前页
29369
+ * @return {Promise<string>} 异步返回光栅化结果, 图片 png 对应的 base64 url. 如果页面不存在返回 null
29370
+ */
29371
+ rasterizeViewport;
29372
+ /**
29373
+ * 光栅化指定页面包含所有元素的包围盒
29374
+ * @param {number} outputMaxWidth 输出图片最大宽度, 在保证高宽比的情况下限值输出图片的宽度小于等于此值
29375
+ * @param {number} outputMaxHeight 输出图片最大高度, 在保证高宽比的情况下限值输出图片的高度小于等于此值
29376
+ * @param {string=} page string 指示页码 id; number 指示 IndexedNavigation 页面索引; undefined 指示光栅化当前页
29377
+ * @return {Promise<string>} 异步返回光栅化结果, 图片 png 对应的 base64 url. 如果页面不存在返回 null
29378
+ */
29379
+ rasterizeElementsBounds;
29380
+ /**
29381
+ * 光栅化指定页面最大缩放区域, 如果没有指定 `WhiteboardOption.maxScaleRatio` 或者指定的值为 -1, 则按 `rasterizeViewport` 返回结果.
29382
+ * @param {number} scale 缩放比例
29383
+ * @param {string=} page string 指示页码 id; number 指示 IndexedNavigation 页面索引; undefined 指示光栅化当前页
29384
+ * @return {Promise<string>} 异步返回光栅化结果, 图片 png 对应的 base64 url. 如果页面不存在返回 null
29236
29385
  */
29237
- rasterize;
29386
+ rasterizeMaxBounds;
29238
29387
  /**
29239
29388
  * 设置输入类型, "pen" 仅接受手写笔输入, "any" 接受任意输入, 当首次侦测到手写笔输入时, 将自动切换为 "pen"
29240
29389
  * @param {"pen" | "any"} type 输入类型
29241
29390
  */
29242
29391
  setInputType;
29243
- insertImage;
29392
+ // public insertImage!: (src: string) => void;
29244
29393
  constructor(view) {
29245
29394
  super();
29246
29395
  this.view = view;
@@ -30142,13 +30291,28 @@ var WhiteboardApplication = class extends import_forge_room3.AbstractApplication
30142
30291
  this.emitter.redo = () => {
30143
30292
  this.undoManager?.redo();
30144
30293
  };
30145
- this.emitter.rasterize = (page) => {
30146
- return this.rasterize(page);
30294
+ this.emitter.rasterizeViewport = (scale, page) => {
30295
+ return this.rasterize(1, 1, scale, "viewport", page);
30296
+ };
30297
+ this.emitter.rasterizeElementsBounds = (outputMaxWidth, outputMaxHeight, page) => {
30298
+ return this.rasterize(outputMaxWidth, outputMaxHeight, 1, "bounds", page);
30299
+ };
30300
+ this.emitter.rasterizeMaxBounds = (scale, page) => {
30301
+ return this.rasterize(1, 1, scale, "maxScale", page);
30147
30302
  };
30148
30303
  this.emitter.setInputType = (type) => {
30149
30304
  this.inputType = type;
30150
30305
  this.emitter.emit("inputTypeChange", this.inputType);
30151
30306
  };
30307
+ this.emitter.translateCamera = (offsetX, offsetY) => {
30308
+ this.camera.translate(offsetX, offsetY);
30309
+ };
30310
+ this.emitter.scaleCamera = (scale) => {
30311
+ this.camera.scale(scale);
30312
+ };
30313
+ this.emitter.resetCamera = () => {
30314
+ this.camera.reset();
30315
+ };
30152
30316
  this.emitter.on("error", (errorCode, errorMessage) => {
30153
30317
  (0, import_forge_room3.log)("WhiteboardApplicationError", {
30154
30318
  errorCode,
@@ -30212,6 +30376,39 @@ var WhiteboardApplication = class extends import_forge_room3.AbstractApplication
30212
30376
  that.toolbarModel.dashArray = v;
30213
30377
  }
30214
30378
  });
30379
+ Object.defineProperty(this.emitter, "enableCameraByMouse", {
30380
+ get() {
30381
+ return that.camera.enableByMouse;
30382
+ },
30383
+ set(value) {
30384
+ that.camera.enableByMouse = value;
30385
+ }
30386
+ });
30387
+ Object.defineProperty(this.emitter, "enableCameraByTouch", {
30388
+ get() {
30389
+ return that.camera.enableByTouch;
30390
+ },
30391
+ set(value) {
30392
+ that.camera.enableByTouch = value;
30393
+ }
30394
+ });
30395
+ Object.defineProperty(this.emitter, "cameraBoundaryColor", {
30396
+ get() {
30397
+ return that.camera.boundaryColor;
30398
+ },
30399
+ set(value) {
30400
+ that.camera.boundaryColor = value;
30401
+ }
30402
+ });
30403
+ Object.defineProperty(this.emitter, "enableCameraBoundaryHighlight", {
30404
+ get() {
30405
+ return that.camera.enableBoundaryHighlight;
30406
+ },
30407
+ set(value) {
30408
+ that.camera.enableBoundaryHighlight = value;
30409
+ }
30410
+ });
30411
+ window["__wb"] = this;
30215
30412
  }
30216
30413
  userMap(userId) {
30217
30414
  return this.getMap(`user/${userId}`);
@@ -30239,7 +30436,12 @@ var WhiteboardApplication = class extends import_forge_room3.AbstractApplication
30239
30436
  );
30240
30437
  this.pageModel.on("pagesChange", this.handleLayersChange);
30241
30438
  this.pageModel.on("switchPage", this.handlePageSwitch);
30439
+ if (option.maxScaleRatio && option.maxScaleRatio < 1 && option.maxScaleRatio !== -1) {
30440
+ throw new Error(`[@netless/forge-whiteboard] invalid maxScaleRatio ${option.maxScaleRatio}`);
30441
+ }
30242
30442
  this.camera = new Camera(
30443
+ new this.paperScope.Size(option.width, option.height),
30444
+ option.maxScaleRatio ?? -1,
30243
30445
  this.rootElement,
30244
30446
  this.userManager,
30245
30447
  this.paperScope,
@@ -30655,7 +30857,10 @@ var WhiteboardApplication = class extends import_forge_room3.AbstractApplication
30655
30857
  this.emitter.emit("elementDeselected", userId);
30656
30858
  }
30657
30859
  };
30658
- rasterize(page) {
30860
+ ensureScale(outputWidth, outputHeight, originSize) {
30861
+ return Math.min(outputWidth / originSize.width, outputHeight / originSize.height);
30862
+ }
30863
+ rasterize(outputMaxWidth, outputMaxHeight, scale, area, page) {
30659
30864
  let pageId;
30660
30865
  if (typeof page === "string") {
30661
30866
  pageId = page;
@@ -30669,8 +30874,6 @@ var WhiteboardApplication = class extends import_forge_room3.AbstractApplication
30669
30874
  return Promise.resolve(null);
30670
30875
  }
30671
30876
  this.snapshotScope.project.activeLayer.removeChildren();
30672
- this.snapshotScope.view.matrix = this.paperScope.project.view.matrix.clone();
30673
- this.snapshotScope.view.viewSize = this.paperScope.project.view.viewSize.clone();
30674
30877
  for (const key of Array.from(renderableModel.elements.keys())) {
30675
30878
  let elementModel = null;
30676
30879
  if (renderableModel.elementModels.has(key)) {
@@ -30691,6 +30894,29 @@ var WhiteboardApplication = class extends import_forge_room3.AbstractApplication
30691
30894
  }
30692
30895
  }
30693
30896
  }
30897
+ if (area === "bounds") {
30898
+ const bounds = this.snapshotScope.project.activeLayer.bounds;
30899
+ const scale2 = this.ensureScale(outputMaxWidth, outputMaxHeight, bounds.size);
30900
+ const matrix = new this.paperScope.Matrix();
30901
+ matrix.scale(scale2);
30902
+ matrix.translate({ x: -bounds.x, y: -bounds.y });
30903
+ this.snapshotScope.view.viewSize = bounds.size.multiply(scale2);
30904
+ this.snapshotScope.view.matrix = matrix;
30905
+ } else if (area === "maxScale" && this.option.maxScaleRatio && this.option.maxScaleRatio !== -1) {
30906
+ const width = this.option.width * this.option.maxScaleRatio;
30907
+ const height = this.option.height * this.option.maxScaleRatio;
30908
+ const offsetX = this.option.width * (this.option.maxScaleRatio - 1) / 2;
30909
+ const offsetY = this.option.height * (this.option.maxScaleRatio - 1) / 2;
30910
+ const matrix = new this.paperScope.Matrix();
30911
+ matrix.scale(scale);
30912
+ matrix.translate({ x: offsetX, y: offsetY });
30913
+ this.snapshotScope.view.viewSize = new this.snapshotScope.Size(width, height).multiply(scale);
30914
+ this.snapshotScope.view.matrix = matrix;
30915
+ } else {
30916
+ this.snapshotScope.view.matrix = this.paperScope.project.view.matrix.clone();
30917
+ this.snapshotScope.view.matrix.scale(scale, this.paperScope.project.view.bounds.topLeft);
30918
+ this.snapshotScope.view.viewSize = this.paperScope.project.view.viewSize.clone().multiply(scale);
30919
+ }
30694
30920
  return new Promise((resolve) => {
30695
30921
  setTimeout(() => {
30696
30922
  resolve(this.snapshotScope.view.element.toDataURL("image/png"));