med-viewer-sdk 0.1.0 → 0.1.2

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
@@ -46,9 +46,9 @@ MedViewer SDK 是一个强大且灵活的医学图像查看 SDK,它基于 Open
46
46
 
47
47
  ```bash
48
48
  # 假设使用 npm 或 yarn 进行包管理
49
- npm install openseadragon konva # 根据需要添加其他依赖
49
+ npm install openseadragon # 根据需要添加其他依赖
50
50
  # 或
51
- yarn add openseadragon konva
51
+ yarn add openseadragon
52
52
  ```
53
53
 
54
54
  然后,在您的项目中引入 SDK:
@@ -165,6 +165,180 @@ const engine = new MedViewerEngine({
165
165
  });
166
166
  ```
167
167
 
168
+ ### 在 Vue 项目中使用
169
+
170
+ `med-viewer-sdk` 可以在 Vue 2 或 Vue 3 项目中使用。由于 `vue` 是 `med-viewer-sdk` 的一个 `peerDependency`,您需要确保您的项目中已经安装了 `vue`。
171
+
172
+ #### 1. 安装依赖
173
+
174
+ 在您的 Vue 项目中安装 `med-viewer-sdk` 及其 `peerDependencies`。
175
+
176
+ ```bash
177
+ npm install med-viewer-sdk openseadragon vue # 根据您的 Vue 版本安装相应的 Vue 包
178
+ # 或者使用 yarn
179
+ yarn add med-viewer-sdk openseadragon vue
180
+ ```
181
+
182
+ #### 2. 创建 Vue 组件
183
+
184
+ 创建一个 Vue 组件来封装 `MedViewerEngine` 的实例化和生命周期管理。
185
+
186
+ **Vue 3 (Composition API):**
187
+
188
+ ```vue
189
+ <template>
190
+ <div ref="viewerContainer" class="med-viewer-container"></div>
191
+ </template>
192
+
193
+ <script setup lang="ts">
194
+ import { ref, onMounted, onBeforeUnmount } from 'vue';
195
+ import { MedViewerEngine } from 'med-viewer-sdk';
196
+ import OpenSeadragon from 'openseadragon'; // 如果需要 OpenSeadragon 类型
197
+
198
+ const viewerContainer = ref<HTMLElement | null>(null);
199
+ let medViewer: MedViewerEngine | null = null;
200
+
201
+ onMounted(() => {
202
+ if (viewerContainer.value) {
203
+ medViewer = new MedViewerEngine({
204
+ element: viewerContainer.value,
205
+ viewerOptions: {
206
+ id: 'openseadragon-viewer',
207
+ prefixUrl: 'https://openseadragon.github.io/openseadragon/images/',
208
+ tileSources: {
209
+ type: 'image',
210
+ url: 'https://openseadragon.github.io/example-images/duomo/duomo.dzi'
211
+ },
212
+ },
213
+ plugins: {
214
+ toolbar: true,
215
+ selection: true,
216
+ scalebar: {
217
+ type: 'MICROSCOPY',
218
+ location: 'BOTTOM_LEFT',
219
+ color: 'rgb(255, 255, 255)',
220
+ fontColor: 'rgb(255, 255, 255)',
221
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
222
+ },
223
+ anno: true,
224
+ konva: true,
225
+ colorAdjust: {
226
+ initial: {
227
+ brightness: 1.2,
228
+ contrast: 0.8,
229
+ invert: true,
230
+ }
231
+ }
232
+ }
233
+ });
234
+
235
+ // 可以访问插件并进行操作
236
+ if (medViewer.selection) {
237
+ medViewer.selection.enable();
238
+ medViewer.selection.setSelectionMode('RECTANGLE');
239
+ }
240
+ }
241
+ });
242
+
243
+ onBeforeUnmount(() => {
244
+ if (medViewer) {
245
+ medViewer.destroy();
246
+ medViewer = null;
247
+ }
248
+ });
249
+ </script>
250
+
251
+ <style scoped>
252
+ .med-viewer-container {
253
+ width: 100%;
254
+ height: 80vh; /* 根据需要调整高度 */
255
+ background-color: #000;
256
+ }
257
+ </style>
258
+ ```
259
+
260
+ **Vue 2 (Options API):**
261
+
262
+ ```vue
263
+ <template>
264
+ <div ref="viewerContainer" class="med-viewer-container"></div>
265
+ </template>
266
+
267
+ <script lang="ts">
268
+ import Vue from 'vue';
269
+ import { MedViewerEngine } from 'med-viewer-sdk';
270
+ import OpenSeadragon from 'openseadragon'; // 如果需要 OpenSeadragon 类型
271
+
272
+ export default Vue.extend({
273
+ data() {
274
+ return {
275
+ medViewer: null as MedViewerEngine | null,
276
+ };
277
+ },
278
+ mounted() {
279
+ if (this.$refs.viewerContainer) {
280
+ this.medViewer = new MedViewerEngine({
281
+ element: this.$refs.viewerContainer as HTMLElement,
282
+ viewerOptions: {
283
+ id: 'openseadragon-viewer',
284
+ prefixUrl: 'https://openseadragon.github.io/openseadragon/images/',
285
+ tileSources: {
286
+ type: 'image',
287
+ url: 'https://openseadragon.github.io/example-images/duomo/duomo.dzi'
288
+ },
289
+ },
290
+ plugins: {
291
+ toolbar: true,
292
+ selection: true,
293
+ scalebar: {
294
+ type: 'MICROSCOPY',
295
+ location: 'BOTTOM_LEFT',
296
+ color: 'rgb(255, 255, 255)',
297
+ fontColor: 'rgb(255, 255, 255)',
298
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
299
+ },
300
+ anno: true,
301
+ konva: true,
302
+ colorAdjust: {
303
+ initial: {
304
+ brightness: 1.2,
305
+ contrast: 0.8,
306
+ invert: true,
307
+ }
308
+ }
309
+ }
310
+ });
311
+
312
+ if (this.medViewer.selection) {
313
+ this.medViewer.selection.enable();
314
+ this.medViewer.selection.setSelectionMode('RECTANGLE');
315
+ }
316
+ }
317
+ },
318
+ beforeDestroy() { // Vue 2 使用 beforeDestroy
319
+ if (this.medViewer) {
320
+ this.medViewer.destroy();
321
+ this.medViewer = null;
322
+ }
323
+ },
324
+ });
325
+ </script>
326
+
327
+ <style scoped>
328
+ .med-viewer-container {
329
+ width: 100%;
330
+ height: 80vh; /* 根据需要调整高度 */
331
+ background-color: #000;
332
+ }
333
+ </style>
334
+ ```
335
+
336
+ #### 3. 注意事项
337
+
338
+ - **CSS 导入**:如果 `med-viewer-sdk` 包含任何 CSS 样式,您可能需要在您的 Vue 项目的入口文件或相关组件中导入它。例如:`import 'med-viewer-sdk/dist/style.css';`
339
+ - **全局样式**:确保 `med-viewer-sdk` 的容器元素有足够的宽高,以便查看器能够正确渲染。
340
+ - **响应式数据**:`MedViewerEngine` 实例本身不是响应式的。如果您需要其内部状态在 Vue 中响应式,您可能需要手动将其属性或方法包装在 Vue 的响应式系统中。
341
+
168
342
 
169
343
  ## 引用方式
170
344
 
@@ -1,5 +1,4 @@
1
1
  import OpenSeadragon from "openseadragon";
2
- import { KonvaAnnotator } from "./KonvaAnnotator";
3
2
  import { AnnoAnnotator } from "./AnnoAnnotator";
4
3
  import { MedToolbar, type ToolbarOptions } from "./Toolbar";
5
4
  import { ColorAdjustPlugin, type ColorAdjustOptions } from "./ColorAdjustPlugin";
@@ -14,7 +13,6 @@ export interface MedEngineOptions {
14
13
  navigatorBorderRadius?: number;
15
14
  prefixUrl?: string;
16
15
  plugins?: {
17
- konva?: boolean | any;
18
16
  annotorious?: boolean | any;
19
17
  toolbar?: boolean | ToolbarOptions;
20
18
  colorAdjust?: boolean | ColorAdjustOptions;
@@ -28,7 +26,6 @@ export interface MedEngineOptions {
28
26
  */
29
27
  export declare class MedViewerEngine {
30
28
  viewer: OpenSeadragon.Viewer;
31
- konva: KonvaAnnotator | null;
32
29
  anno: AnnoAnnotator | null;
33
30
  toolbar: MedToolbar | null;
34
31
  colorAdjust: ColorAdjustPlugin | null;
@@ -1,6 +1,5 @@
1
1
  import { MedViewerEngine } from './core/Engine';
2
- import { KonvaAnnotator } from './core/KonvaAnnotator';
3
2
  import { AnnoAnnotator } from './core/AnnoAnnotator';
4
3
  import { MedToolbar } from './core/Toolbar';
5
4
  import { SelectionPlugin } from './core/SelectionPlugin';
6
- export { MedViewerEngine, KonvaAnnotator, AnnoAnnotator, MedToolbar, SelectionPlugin };
5
+ export { MedViewerEngine, AnnoAnnotator, MedToolbar, SelectionPlugin };
@@ -5,162 +5,6 @@ var __publicField = (obj, key, value) => {
5
5
  return value;
6
6
  };
7
7
  import OpenSeadragon from "openseadragon";
8
- import Konva from "konva";
9
- class BaseAnnotator {
10
- constructor(engine) {
11
- __publicField(this, "engine");
12
- __publicField(this, "isEnabled", false);
13
- this.engine = engine;
14
- }
15
- }
16
- class KonvaAnnotator extends BaseAnnotator {
17
- constructor(engine) {
18
- super(engine);
19
- __publicField(this, "stage");
20
- __publicField(this, "layer");
21
- __publicField(this, "overlay");
22
- // 内部绘图状态
23
- __publicField(this, "isDrawing", false);
24
- __publicField(this, "currentShape", null);
25
- __publicField(this, "currentTool", null);
26
- __publicField(this, "startPoint", { x: 0, y: 0 });
27
- const viewerElement = this.engine.viewer.element;
28
- this.overlay = document.createElement("div");
29
- this.overlay.className = "konva-annotator-overlay";
30
- this.overlay.style.cssText = `
31
- position: absolute;
32
- top: 0; left: 0;
33
- width: 100%; height: 100%;
34
- pointer-events: none;
35
- z-index: 10;
36
- `;
37
- viewerElement.appendChild(this.overlay);
38
- this.stage = new Konva.Stage({
39
- container: this.overlay,
40
- width: viewerElement.clientWidth,
41
- height: viewerElement.clientHeight
42
- });
43
- this.layer = new Konva.Layer();
44
- this.stage.add(this.layer);
45
- this.initDrawingEvents();
46
- }
47
- /**
48
- * 实现基类方法:启用/禁用标注层
49
- */
50
- setEnabled(enabled) {
51
- this.isEnabled = enabled;
52
- this.overlay.style.pointerEvents = enabled ? "auto" : "none";
53
- this.engine.viewer.setMouseNavEnabled(!enabled);
54
- if (enabled) {
55
- this.stage.container().style.cursor = "crosshair";
56
- } else {
57
- this.stage.container().style.cursor = "default";
58
- this.setTool(null);
59
- }
60
- }
61
- /**
62
- * 实现基类方法:设置工具
63
- */
64
- setTool(tool) {
65
- this.currentTool = tool;
66
- }
67
- /**
68
- * 核心同步逻辑:保持 Canvas 与 OSD 图片位置重合
69
- */
70
- // private initSync(): void {
71
- // const sync = () => {
72
- // const viewport = this.engine.viewer.viewport;
73
- // const zoom = viewport.getZoom(true);
74
- // const containerWidth = viewport.getContainerSize().x;
75
- // const pixelPos = viewport.pixelFromPoint(new Konva.util.Point(0, 0), true);
76
- // const konvaScale = zoom * containerWidth;
77
- // this.stage.position({ x: pixelPos.x, y: pixelPos.y });
78
- // this.stage.scale({ x: konvaScale, y: konvaScale });
79
- // this.stage.batchDraw();
80
- // };
81
- // this.engine.viewer.addHandler('animation', sync);
82
- // this.engine.viewer.addHandler('canvas-drag', sync);
83
- // this.engine.viewer.addHandler('viewport-change', sync);
84
- // this.engine.viewer.addHandler('resize', () => {
85
- // const el = this.engine.viewer.element;
86
- // this.stage.width(el.clientWidth);
87
- // this.stage.height(el.clientHeight);
88
- // sync();
89
- // });
90
- // sync();
91
- // }
92
- /**
93
- * 监听绘图相关的鼠标事件
94
- */
95
- initDrawingEvents() {
96
- this.stage.on("mousedown touchstart", (e) => {
97
- if (!this.isEnabled || !this.currentTool)
98
- return;
99
- this.isDrawing = true;
100
- const pos = this.getRelativePointerPosition();
101
- this.startPoint = pos;
102
- if (this.currentTool === "rect") {
103
- this.currentShape = new Konva.Rect({
104
- x: pos.x,
105
- y: pos.y,
106
- width: 0,
107
- height: 0,
108
- stroke: "#00ff00",
109
- strokeWidth: 2,
110
- strokeScaleEnabled: false,
111
- // 关键:保证缩放时线宽不变
112
- draggable: true
113
- });
114
- }
115
- if (this.currentShape) {
116
- this.layer.add(this.currentShape);
117
- }
118
- });
119
- this.stage.on("mousemove touchmove", () => {
120
- if (!this.isDrawing || !this.currentShape)
121
- return;
122
- const pos = this.getRelativePointerPosition();
123
- if (this.currentTool === "rect") {
124
- const shape = this.currentShape;
125
- shape.width(pos.x - this.startPoint.x);
126
- shape.height(pos.y - this.startPoint.y);
127
- }
128
- this.layer.batchDraw();
129
- });
130
- this.stage.on("mouseup touchend", () => {
131
- this.isDrawing = false;
132
- this.currentShape = null;
133
- });
134
- }
135
- /**
136
- * 工具方法:获取相对于图像(0~1)空间的鼠标位置
137
- */
138
- getRelativePointerPosition() {
139
- const transform = this.stage.getAbsoluteTransform().copy().invert();
140
- const pos = this.stage.getPointerPosition() || { x: 0, y: 0 };
141
- return transform.point(pos);
142
- }
143
- // --- 实现基类要求的其他数据方法 ---
144
- getAnnotations() {
145
- return this.layer.getChildren().map((node) => node.toObject());
146
- }
147
- setAnnotations(data) {
148
- this.clear();
149
- data.forEach((obj) => {
150
- const node = Konva.Node.create(obj);
151
- this.layer.add(node);
152
- });
153
- this.layer.draw();
154
- }
155
- clear() {
156
- this.layer.destroyChildren();
157
- this.layer.draw();
158
- }
159
- destroy() {
160
- this.stage.destroy();
161
- this.overlay.remove();
162
- }
163
- }
164
8
  function getDefaultExportFromCjs(x) {
165
9
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
166
10
  }
@@ -12585,6 +12429,13 @@ var openseadragonAnnotorious_min = { exports: {} };
12585
12429
  var openseadragonAnnotorious_minExports = openseadragonAnnotorious_min.exports;
12586
12430
  const Annotorious = /* @__PURE__ */ getDefaultExportFromCjs(openseadragonAnnotorious_minExports);
12587
12431
  const annotorious_min = "";
12432
+ class BaseAnnotator {
12433
+ constructor(engine) {
12434
+ __publicField(this, "engine");
12435
+ __publicField(this, "isEnabled", false);
12436
+ this.engine = engine;
12437
+ }
12438
+ }
12588
12439
  class AnnoAnnotator extends BaseAnnotator {
12589
12440
  constructor(engine, config = {}) {
12590
12441
  super(engine);
@@ -14065,7 +13916,7 @@ class ScalebarPlugin {
14065
13916
  class MedViewerEngine {
14066
13917
  constructor(options) {
14067
13918
  __publicField(this, "viewer");
14068
- __publicField(this, "konva", null);
13919
+ // public konva: KonvaAnnotator | null = null;
14069
13920
  __publicField(this, "anno", null);
14070
13921
  __publicField(this, "toolbar", null);
14071
13922
  __publicField(this, "colorAdjust", null);
@@ -14138,7 +13989,6 @@ class MedViewerEngine {
14138
13989
  contextMenu: true
14139
13990
  },
14140
13991
  plugins: {
14141
- konva: false,
14142
13992
  annotorious: false,
14143
13993
  toolbar: false,
14144
13994
  selection: false
@@ -14159,10 +14009,6 @@ class MedViewerEngine {
14159
14009
  const { plugins } = this.options;
14160
14010
  if (!plugins)
14161
14011
  return;
14162
- if (plugins.konva) {
14163
- this.konva = new KonvaAnnotator(this);
14164
- console.log("[MedEngine] Konva plugin initialized.");
14165
- }
14166
14012
  if (plugins.annotorious) {
14167
14013
  const annoConfig = typeof plugins.annotorious === "object" ? plugins.annotorious : {};
14168
14014
  if (this.viewer.isOpen()) {
@@ -14202,15 +14048,11 @@ class MedViewerEngine {
14202
14048
  * @param mode 'browse' | 'konva' | 'anno'
14203
14049
  */
14204
14050
  setInteractionEffect(mode) {
14205
- var _a, _b, _c, _d;
14206
- (_a = this.konva) == null ? void 0 : _a.setEnabled(false);
14207
- (_b = this.anno) == null ? void 0 : _b.setEnabled(false);
14051
+ var _a, _b;
14052
+ (_a = this.anno) == null ? void 0 : _a.setEnabled(false);
14208
14053
  switch (mode) {
14209
- case "konva":
14210
- (_c = this.konva) == null ? void 0 : _c.setEnabled(true);
14211
- break;
14212
14054
  case "anno":
14213
- (_d = this.anno) == null ? void 0 : _d.setEnabled(true);
14055
+ (_b = this.anno) == null ? void 0 : _b.setEnabled(true);
14214
14056
  break;
14215
14057
  }
14216
14058
  }
@@ -14218,13 +14060,12 @@ class MedViewerEngine {
14218
14060
  * 销毁引擎与所有插件
14219
14061
  */
14220
14062
  destroy() {
14221
- var _a, _b, _c, _d, _e, _f;
14222
- (_a = this.konva) == null ? void 0 : _a.destroy();
14223
- (_b = this.anno) == null ? void 0 : _b.destroy();
14224
- (_c = this.toolbar) == null ? void 0 : _c.destroy();
14225
- (_d = this.selection) == null ? void 0 : _d.destroy();
14226
- (_e = this.scalebar) == null ? void 0 : _e.destroy();
14227
- (_f = this.colorAdjust) == null ? void 0 : _f.destroy();
14063
+ var _a, _b, _c, _d, _e;
14064
+ (_a = this.anno) == null ? void 0 : _a.destroy();
14065
+ (_b = this.toolbar) == null ? void 0 : _b.destroy();
14066
+ (_c = this.selection) == null ? void 0 : _c.destroy();
14067
+ (_d = this.scalebar) == null ? void 0 : _d.destroy();
14068
+ (_e = this.colorAdjust) == null ? void 0 : _e.destroy();
14228
14069
  this.viewer.destroy();
14229
14070
  this.options.element.innerHTML = "";
14230
14071
  }
@@ -14232,7 +14073,6 @@ class MedViewerEngine {
14232
14073
  if (typeof window !== "undefined") {
14233
14074
  window.MedViewerSDK = {
14234
14075
  MedViewerEngine,
14235
- KonvaAnnotator,
14236
14076
  AnnoAnnotator,
14237
14077
  MedToolbar,
14238
14078
  SelectionPlugin
@@ -14241,7 +14081,6 @@ if (typeof window !== "undefined") {
14241
14081
  }
14242
14082
  export {
14243
14083
  AnnoAnnotator,
14244
- KonvaAnnotator,
14245
14084
  MedToolbar,
14246
14085
  MedViewerEngine,
14247
14086
  SelectionPlugin