cytoscape-canvas-underlay 1.2.4 → 1.3.1
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 +15 -7
- package/package.json +1 -1
- package/src/DrawingOverlay.js +43 -44
- package/src/index.js +1 -2
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A [Cytoscape.js](https://js.cytoscape.org) plugin for rendering image/PDF backgr
|
|
|
8
8
|
- **Pan clamping** — hard boundary or iOS-style rubber-band with spring-back
|
|
9
9
|
- **Minimap** — DOM-based crisp image rendering, two viewport styles (`dim` / `rect`), auto-hide, full customization
|
|
10
10
|
- **Adaptive PDF quality** — low-quality render during interaction, high-quality on idle
|
|
11
|
-
- **Rich navigation** — `fit`, `fitAll`, `
|
|
11
|
+
- **Rich navigation** — `fit`, `fitAll`, `panToRegion`, coordinate conversion
|
|
12
12
|
- **Layer visibility** — independently show/hide drawing background and graph layer
|
|
13
13
|
- **CSS filters** — invert, brightness, contrast, saturate, grayscale (invert applied first so brightness/contrast work correctly on inverted images)
|
|
14
14
|
- **Rotation** — rotate main or additional drawings (0, 90, 180, 270 degrees)
|
|
@@ -147,13 +147,14 @@ api.off('zoom'); // remove all zoom handlers
|
|
|
147
147
|
### Navigation
|
|
148
148
|
|
|
149
149
|
```js
|
|
150
|
-
api.fit(); // fit main drawing
|
|
150
|
+
api.fit(); // fit main drawing (instant)
|
|
151
|
+
api.fit({ animate: true }); // fit with animation (300ms)
|
|
152
|
+
api.fit({ padding: 50 }); // fit with 50px padding
|
|
153
|
+
api.fit({ animate: true, duration: 500, easing: 'ease-out' });
|
|
151
154
|
api.fitToDrawing('trace-1'); // fit specific drawing
|
|
152
155
|
api.fitAll(); // fit all drawings
|
|
153
156
|
api.panTo(500, 300); // center on world coordinate
|
|
154
157
|
api.panTo(500, 300, 2.0); // center + set zoom
|
|
155
|
-
api.panToElement('node-id'); // center on cytoscape element
|
|
156
|
-
api.panToElement(cy.$('#node-id')); // also accepts element reference
|
|
157
158
|
api.panToRegion(100, 100, 400, 300); // fit a world-coordinate region
|
|
158
159
|
```
|
|
159
160
|
|
|
@@ -255,7 +256,7 @@ const inside = api.isPointInDrawing(world.x, world.y);
|
|
|
255
256
|
| Option | Type | Default | Description |
|
|
256
257
|
|--------|------|---------|-------------|
|
|
257
258
|
| `fitOnLoad` | `boolean` | `true` | Auto-fit drawing to viewport on load |
|
|
258
|
-
| `fitPadding` | `number` | `
|
|
259
|
+
| `fitPadding` | `number` | `0` | Default padding for fit operations (px) |
|
|
259
260
|
|
|
260
261
|
### Pan Clamping
|
|
261
262
|
|
|
@@ -368,11 +369,10 @@ These still work but the `on()`/`off()` event emitter is preferred.
|
|
|
368
369
|
|
|
369
370
|
| Method | Description |
|
|
370
371
|
|--------|-------------|
|
|
371
|
-
| `fit(
|
|
372
|
+
| `fit(opts?)` | Fit main drawing to viewport. Options: `{ animate, padding, duration, easing }` |
|
|
372
373
|
| `fitToDrawing(id, padding?)` | Fit a specific additional drawing to viewport |
|
|
373
374
|
| `fitAll(padding?)` | Fit all drawings (main + additional) to viewport |
|
|
374
375
|
| `panTo(x, y, zoom?)` | Center viewport on world coordinate, optionally set zoom |
|
|
375
|
-
| `panToElement(eleOrId, padding?)` | Fit a cytoscape element in viewport (accepts ID string or element) |
|
|
376
376
|
| `panToRegion(x, y, w, h, padding?)` | Fit a world-coordinate region in viewport |
|
|
377
377
|
|
|
378
378
|
### Visibility
|
|
@@ -440,6 +440,14 @@ These still work but the `on()`/`off()` event emitter is preferred.
|
|
|
440
440
|
|
|
441
441
|
## Changelog
|
|
442
442
|
|
|
443
|
+
### 1.3.0
|
|
444
|
+
|
|
445
|
+
- **Breaking**: `fit()` now accepts an options object instead of a padding number: `fit({ animate, padding, duration, easing })`. Calling `fit()` with no arguments still works (uses defaults).
|
|
446
|
+
- **Breaking**: Removed `panToElement()` — use application-level implementation for element navigation with custom zoom/animation logic.
|
|
447
|
+
- **Changed**: Default `fitPadding` changed from `50` to `0`.
|
|
448
|
+
- **Added**: `fit()` now supports `animate` option with configurable `duration` (default 300ms) and `easing` (default `ease-in-out-cubic`).
|
|
449
|
+
- **Added**: `fit()` now calls `cy.resize()` before calculating dimensions.
|
|
450
|
+
|
|
443
451
|
### 1.2.3
|
|
444
452
|
|
|
445
453
|
- **Fix**: `_springBackIfNeeded` now respects `panClamp: false`. Previously, setting `panClamp: false` at runtime disabled hard clamping but the soft spring-back animation on mouse release still fired, snapping the viewport back to drawing bounds. This made it impossible to programmatically disable pan clamping (e.g., during `panToElement` animations).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cytoscape-canvas-underlay",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Cytoscape.js plugin for rendering image/PDF canvas underlay behind graph nodes with synchronized zoom and pan",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"module": "src/index.js",
|
package/src/DrawingOverlay.js
CHANGED
|
@@ -24,7 +24,7 @@ const DEFAULTS = {
|
|
|
24
24
|
// ── Layout ──
|
|
25
25
|
zIndex: 0, // Canvas z-index within overlay container
|
|
26
26
|
fitOnLoad: true, // Auto-fit drawing to viewport on load
|
|
27
|
-
fitPadding:
|
|
27
|
+
fitPadding: 0, // Padding (px) for fit operations
|
|
28
28
|
|
|
29
29
|
// ── Pan Clamping ──
|
|
30
30
|
panClamp: false, // Prevent panning too far from drawing bounds
|
|
@@ -207,6 +207,9 @@ export class DrawingOverlay {
|
|
|
207
207
|
// Additional drawings
|
|
208
208
|
this._drawings = new Map();
|
|
209
209
|
|
|
210
|
+
// PDF 존재 여부 캐시 (zoom/pan마다 순회 방지)
|
|
211
|
+
this._hasPdfCached = false;
|
|
212
|
+
|
|
210
213
|
// RAF / debounce
|
|
211
214
|
this._rafId = null;
|
|
212
215
|
this._qualityTimer = null;
|
|
@@ -297,7 +300,7 @@ export class DrawingOverlay {
|
|
|
297
300
|
if (this._minimap) this._minimap.render();
|
|
298
301
|
|
|
299
302
|
// Schedule high-quality PDF re-render at current zoom level
|
|
300
|
-
const hasPdf = this.
|
|
303
|
+
const hasPdf = this._hasPdfCached;
|
|
301
304
|
if (hasPdf) {
|
|
302
305
|
if (this._qualityTimer) clearTimeout(this._qualityTimer);
|
|
303
306
|
this._qualityTimer = setTimeout(() => {
|
|
@@ -649,6 +652,7 @@ export class DrawingOverlay {
|
|
|
649
652
|
}
|
|
650
653
|
}
|
|
651
654
|
this._loading = false;
|
|
655
|
+
this._updateHasPdf();
|
|
652
656
|
|
|
653
657
|
if (this.opts.fitOnLoad) this.fit();
|
|
654
658
|
this._draw();
|
|
@@ -721,22 +725,31 @@ export class DrawingOverlay {
|
|
|
721
725
|
═══════════════════════════════════════ */
|
|
722
726
|
|
|
723
727
|
/** Fit the main drawing to viewport. */
|
|
724
|
-
fit(
|
|
728
|
+
fit({
|
|
729
|
+
animate = false,
|
|
730
|
+
padding = this.opts.fitPadding,
|
|
731
|
+
duration = 300,
|
|
732
|
+
easing = 'ease-in-out-cubic',
|
|
733
|
+
} = {}) {
|
|
725
734
|
if (!this._main.w || !this._main.h) return;
|
|
726
|
-
const
|
|
727
|
-
const container = this.cy.container().getBoundingClientRect();
|
|
728
|
-
const scaleX = (container.width - pad * 2) / this._main.w;
|
|
729
|
-
const scaleY = (container.height - pad * 2) / this._main.h;
|
|
730
|
-
const zoom = Math.max(this.cy.minZoom(), Math.min(this.cy.maxZoom(),Math.min(scaleX, scaleY)));
|
|
735
|
+
const cy = this.cy;
|
|
731
736
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
737
|
+
cy.resize();
|
|
738
|
+
|
|
739
|
+
const container = cy.container().getBoundingClientRect();
|
|
740
|
+
const cw = container.width - padding * 2;
|
|
741
|
+
const ch = container.height - padding * 2;
|
|
742
|
+
if (cw <= 0 || ch <= 0) return;
|
|
743
|
+
|
|
744
|
+
const scale = Math.min(cw / this._main.w, ch / this._main.h);
|
|
745
|
+
const panX = Math.round(padding + (cw - this._main.w * scale) / 2);
|
|
746
|
+
const panY = Math.round(padding + (ch - this._main.h * scale) / 2);
|
|
747
|
+
|
|
748
|
+
if (animate) {
|
|
749
|
+
cy.animate({ zoom: scale, pan: { x: panX, y: panY }, duration, easing });
|
|
750
|
+
} else {
|
|
751
|
+
cy.viewport({ zoom: scale, pan: { x: panX, y: panY } });
|
|
752
|
+
}
|
|
740
753
|
}
|
|
741
754
|
|
|
742
755
|
/** Fit a specific additional drawing to viewport. */
|
|
@@ -802,34 +815,6 @@ export class DrawingOverlay {
|
|
|
802
815
|
});
|
|
803
816
|
}
|
|
804
817
|
|
|
805
|
-
/** Pan to center a cytoscape element in viewport. */
|
|
806
|
-
panToElement(eleOrId, padding) {
|
|
807
|
-
const cy = this.cy;
|
|
808
|
-
const ele = typeof eleOrId === 'string' ? cy.getElementById(eleOrId) : eleOrId;
|
|
809
|
-
if (!ele || ele.empty?.()) return;
|
|
810
|
-
|
|
811
|
-
const bb = ele.boundingBox();
|
|
812
|
-
const pad = padding ?? this.opts.fitPadding;
|
|
813
|
-
const container = cy.container().getBoundingClientRect();
|
|
814
|
-
|
|
815
|
-
// Calculate zoom to fit element
|
|
816
|
-
const scaleX = (container.width - pad * 2) / bb.w;
|
|
817
|
-
const scaleY = (container.height - pad * 2) / bb.h;
|
|
818
|
-
const zoom = Math.max(this.cy.minZoom(), Math.min(this.cy.maxZoom(),Math.min(scaleX, scaleY)));
|
|
819
|
-
|
|
820
|
-
const cx = (bb.x1 + bb.x2) / 2;
|
|
821
|
-
const cy_ = (bb.y1 + bb.y2) / 2;
|
|
822
|
-
|
|
823
|
-
this.cy.zoom({
|
|
824
|
-
level: zoom,
|
|
825
|
-
renderedPosition: { x: container.width / 2, y: container.height / 2 },
|
|
826
|
-
});
|
|
827
|
-
this.cy.pan({
|
|
828
|
-
x: container.width / 2 - cx * zoom,
|
|
829
|
-
y: container.height / 2 - cy_ * zoom,
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
|
|
833
818
|
/** Pan to a specific region of the drawing. */
|
|
834
819
|
panToRegion(x, y, w, h, padding) {
|
|
835
820
|
const pad = padding ?? this.opts.fitPadding;
|
|
@@ -910,6 +895,16 @@ export class DrawingOverlay {
|
|
|
910
895
|
Public API: Utility
|
|
911
896
|
═══════════════════════════════════════ */
|
|
912
897
|
|
|
898
|
+
/** PDF 존재 여부 캐시 갱신 */
|
|
899
|
+
_updateHasPdf() {
|
|
900
|
+
this._hasPdfCached = this._main.isPdf;
|
|
901
|
+
if (!this._hasPdfCached) {
|
|
902
|
+
for (const d of this._drawings.values()) {
|
|
903
|
+
if (d.isPdf && d.visible) { this._hasPdfCached = true; break; }
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
913
908
|
/** Force redraw. */
|
|
914
909
|
refresh() {
|
|
915
910
|
this._setupCanvas();
|
|
@@ -1063,6 +1058,7 @@ export class DrawingOverlay {
|
|
|
1063
1058
|
}
|
|
1064
1059
|
|
|
1065
1060
|
this._drawings.set(id, state);
|
|
1061
|
+
this._updateHasPdf();
|
|
1066
1062
|
this._draw();
|
|
1067
1063
|
this._emit('drawingAdd', id, { x: state.x, y: state.y, width: state.width, height: state.height });
|
|
1068
1064
|
}
|
|
@@ -1078,6 +1074,7 @@ export class DrawingOverlay {
|
|
|
1078
1074
|
const state = this._drawings.get(id);
|
|
1079
1075
|
if (!state) return;
|
|
1080
1076
|
state.visible = visible;
|
|
1077
|
+
if (state.isPdf) this._updateHasPdf();
|
|
1081
1078
|
this._draw();
|
|
1082
1079
|
}
|
|
1083
1080
|
|
|
@@ -1088,12 +1085,14 @@ export class DrawingOverlay {
|
|
|
1088
1085
|
|
|
1089
1086
|
removeDrawing(id) {
|
|
1090
1087
|
this._drawings.delete(id);
|
|
1088
|
+
this._updateHasPdf();
|
|
1091
1089
|
this._draw();
|
|
1092
1090
|
this._emit('drawingRemove', id);
|
|
1093
1091
|
}
|
|
1094
1092
|
|
|
1095
1093
|
clearDrawings() {
|
|
1096
1094
|
this._drawings.clear();
|
|
1095
|
+
this._updateHasPdf();
|
|
1097
1096
|
this._draw();
|
|
1098
1097
|
}
|
|
1099
1098
|
|
package/src/index.js
CHANGED
|
@@ -38,11 +38,10 @@ function register(cytoscape) {
|
|
|
38
38
|
getRotation() { return overlay.getRotation(); },
|
|
39
39
|
|
|
40
40
|
// ── Navigation ──
|
|
41
|
-
fit(
|
|
41
|
+
fit(opts) { overlay.fit(opts); },
|
|
42
42
|
fitToDrawing(id, padding) { overlay.fitToDrawing(id, padding); },
|
|
43
43
|
fitAll(padding) { overlay.fitAll(padding); },
|
|
44
44
|
panTo(x, y, zoom) { overlay.panTo(x, y, zoom); },
|
|
45
|
-
panToElement(eleOrId, padding) { overlay.panToElement(eleOrId, padding); },
|
|
46
45
|
panToRegion(x, y, w, h, padding) { overlay.panToRegion(x, y, w, h, padding); },
|
|
47
46
|
|
|
48
47
|
// ── Visibility ──
|