Skip to content

Commit a1abdc7

Browse files
authored
Merge pull request #28 from sgratzl/release/v2.4.2
Release v2.4.2
2 parents 59338d0 + 875b410 commit a1abdc7

File tree

12 files changed

+131
-68
lines changed

12 files changed

+131
-68
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "cytoscape-layers",
33
"description": "Cytoscape.js plugin for rendering layers in SVG, DOM, or Canvas",
4-
"version": "2.4.1",
4+
"version": "2.4.2",
55
"author": {
66
"name": "Samuel Gratzl",
77
"email": "sam@sgratzl.com",

samples/annotations.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
</head>
1313
<body>
1414
<button id="png">PNG</button>
15+
<button id="png2">PNG Full</button>
1516
<a id="url" download="file.png"></a>
1617
<div id="app"></div>
1718
<script src="https://cdn.jsdelivr.net/npm/cytoscape"></script>

samples/annotations.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,18 @@ namespace Annotations {
7474
a.click();
7575
});
7676
});
77+
document.getElementById('png2')?.addEventListener('click', () => {
78+
layers
79+
.png({
80+
output: 'blob-promise',
81+
ignoreUnsupportedLayerOrder: true,
82+
full: true,
83+
})
84+
.then((r) => {
85+
const url = URL.createObjectURL(r);
86+
const a = document.getElementById('url') as HTMLAnchorElement;
87+
a.href = url;
88+
a.click();
89+
});
90+
});
7791
}

samples/edge.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Sample</title>
5+
<style>
6+
#app {
7+
width: 600px;
8+
height: 400px;
9+
display: block;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<button id="png">PNG</button>
15+
<a id="url" download="file.png"></a>
16+
<div id="app"></div>
17+
<script src="https://cdn.jsdelivr.net/npm/cytoscape"></script>
18+
<script src="../build/index.umd.js"></script>
19+
<script src="edge.js"></script>
20+
</body>
21+
</html>

samples/edge.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* eslint-disable @typescript-eslint/no-namespace */
2+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
3+
namespace AnimatedEdges {
4+
declare const cytoscape: typeof import('cytoscape');
5+
declare const CytoscapeLayers: typeof import('../build');
6+
7+
const cy = cytoscape({
8+
container: document.getElementById('app'),
9+
elements: fetch('./grid-data.json').then((r) => r.json()),
10+
// elements: Promise.resolve([
11+
// { data: { id: 'a' } },
12+
// { data: { id: 'b' } },
13+
// {
14+
// data: {
15+
// id: 'ab',
16+
// source: 'a',
17+
// target: 'b',
18+
// },
19+
// },
20+
// ]),
21+
layout: {
22+
name: 'grid',
23+
},
24+
style: [
25+
{
26+
selector: 'edge',
27+
style: {
28+
'line-color': 'white',
29+
opacity: 0.01,
30+
},
31+
},
32+
],
33+
});
34+
35+
const layers = CytoscapeLayers.layers(cy);
36+
37+
const layer = layers.nodeLayer.insertBefore('canvas');
38+
39+
layers.renderPerEdge(layer, (ctx, _, path) => {
40+
ctx.strokeStyle = 'red';
41+
ctx.lineWidth = 5;
42+
ctx.lineCap = 'round';
43+
ctx.stroke(path);
44+
});
45+
}

src/LayersPlugin.ts

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -337,24 +337,6 @@ export default class LayersPlugin {
337337
);
338338
}
339339

340-
const renderer = (
341-
this.cy as unknown as {
342-
renderer(): {
343-
bufferCanvasImage(
344-
o: cy.ExportJpgStringOptions | cy.ExportJpgBlobOptions | cy.ExportJpgBlobPromiseOptions
345-
): HTMLCanvasElement;
346-
};
347-
}
348-
).renderer();
349-
350-
const bg = options.bg;
351-
352-
const canvas = renderer.bufferCanvasImage({ ...options, bg: undefined });
353-
354-
const width = canvas.width;
355-
const height = canvas.height;
356-
const ctx = canvas.getContext('2d')!;
357-
358340
const before = layers
359341
.slice(0, nodeIndex)
360342
.reverse()
@@ -364,27 +346,30 @@ export default class LayersPlugin {
364346
.slice(nodeIndex + 1)
365347
.filter((d) => d.supportsRender() && d !== this.dragLayer && d !== this.selectBoxLayer);
366348

367-
const scale = options.scale ?? 1;
368-
369-
const hint = { scale, width, height, full: options.full ?? false };
370-
371-
ctx.globalCompositeOperation = 'destination-over';
372-
for (const l of before) {
373-
l.renderInto(ctx, hint);
374-
}
375-
376-
ctx.globalCompositeOperation = 'source-over';
377-
for (const l of after) {
378-
l.renderInto(ctx, hint);
379-
}
380-
381-
if (bg) {
382-
ctx.globalCompositeOperation = 'destination-over';
383-
ctx.fillStyle = bg;
384-
ctx.rect(0, 0, width, height);
385-
ctx.fill();
386-
}
349+
const renderer = (
350+
this.cy as unknown as {
351+
renderer(): {
352+
bufferCanvasImage(
353+
o: cy.ExportJpgStringOptions | cy.ExportJpgBlobOptions | cy.ExportJpgBlobPromiseOptions
354+
): HTMLCanvasElement;
355+
drawElements(ctx: CanvasRenderingContext2D, elems: cy.Collection): void;
356+
};
357+
}
358+
).renderer();
387359

360+
const drawElements = renderer.drawElements;
361+
// patch with all levels
362+
renderer.drawElements = function (ctx, elems) {
363+
for (const l of before) {
364+
l.renderInto(ctx);
365+
}
366+
drawElements.call(this, ctx, elems);
367+
for (const l of after) {
368+
l.renderInto(ctx);
369+
}
370+
};
371+
const canvas = renderer.bufferCanvasImage(options);
372+
renderer.drawElements = drawElements;
388373
return canvas;
389374
}
390375

src/elements/edges.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type cy from 'cytoscape';
2-
import type { ICanvasLayer, IPoint } from '../layers';
2+
import type { ICanvasLayer, IPoint, IRenderHint } from '../layers';
33
import { ICallbackRemover, registerCallback } from './utils';
44
import { IElementLayerOptions, defaultElementLayerOptions } from './common';
55

@@ -66,12 +66,13 @@ export function renderPerEdge(
6666

6767
if (o.updateOn === 'render') {
6868
layer.updateOnRender = true;
69-
} else {
69+
}
70+
if (!o.queryEachTime) {
7071
edges = reevaluateCollection(edges);
7172
layer.cy.on('add remove', o.selector, revaluateAndUpdateOnce);
7273
}
7374

74-
const renderer = (ctx: CanvasRenderingContext2D) => {
75+
const renderer = (ctx: CanvasRenderingContext2D, hint: IRenderHint) => {
7576
if (o.queryEachTime) {
7677
edges = reevaluateCollection(edges);
7778
}
@@ -90,7 +91,12 @@ export function renderPerEdge(
9091
impl && impl.startX != null && impl.startY != null ? { x: impl.startX, y: impl.startY } : edge.sourceEndpoint();
9192
const t = impl && impl.endX != null && impl.endY != null ? { x: impl.endX, y: impl.endY } : edge.targetEndpoint();
9293

93-
if (o.checkBounds && o.checkBoundsPointCount > 0 && !anyVisible(layer, s, t, o.checkBoundsPointCount)) {
94+
if (
95+
!hint.forExport &&
96+
o.checkBounds &&
97+
o.checkBoundsPointCount > 0 &&
98+
!anyVisible(layer, s, t, o.checkBoundsPointCount)
99+
) {
94100
return;
95101
}
96102
if (impl && impl.pathCache) {

src/elements/nodes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type cy from 'cytoscape';
2-
import type { ICanvasLayer, IHTMLLayer, ISVGLayer, ILayer } from '../layers';
2+
import type { ICanvasLayer, IHTMLLayer, ISVGLayer, ILayer, IRenderHint } from '../layers';
33
import { SVG_NS } from '../layers/SVGLayer';
44
import { matchNodes, registerCallback, ICallbackRemover, IMatchOptions } from './utils';
55
import { IElementLayerOptions, defaultElementLayerOptions } from './common';
@@ -140,7 +140,7 @@ export function renderPerNode(
140140

141141
if (layer.type === 'canvas') {
142142
const oCanvas = o as INodeCanvasLayerOption;
143-
const renderer = (ctx: CanvasRenderingContext2D) => {
143+
const renderer = (ctx: CanvasRenderingContext2D, hint: IRenderHint) => {
144144
const t = ctx.getTransform();
145145
if (o.queryEachTime) {
146146
nodes = reevaluateCollection(nodes);
@@ -150,7 +150,7 @@ export function renderPerNode(
150150
return;
151151
}
152152
const bb = node.boundingBox(o.boundingBox);
153-
if (oCanvas.checkBounds && !layer.inVisibleBounds(bb)) {
153+
if (!hint.forExport && oCanvas.checkBounds && !layer.inVisibleBounds(bb)) {
154154
return;
155155
}
156156
if (oCanvas.position === 'top-left') {

src/layers/ABaseLayer.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type {
77
IHTMLStaticLayer,
88
ISVGStaticLayer,
99
ICanvasStaticLayer,
10-
IRenderHint,
1110
} from './interfaces';
1211
import type cy from 'cytoscape';
1312
import type { ICanvasLayerOptions, ISVGLayerOptions, IHTMLLayerOptions } from './public';
@@ -39,7 +38,7 @@ export abstract class ABaseLayer implements IMoveAbleLayer {
3938
return false;
4039
}
4140

42-
renderInto(_ctx: CanvasRenderingContext2D, _hint: IRenderHint): void {
41+
renderInto(_ctx: CanvasRenderingContext2D): void {
4342
// dummy
4443
}
4544

src/layers/CanvasLayer.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
import type {
2-
ICanvasLayer,
3-
ILayerElement,
4-
ILayerImpl,
5-
IRenderFunction,
6-
ICanvasStaticLayer,
7-
IRenderHint,
8-
} from './interfaces';
1+
import type { ICanvasLayer, ILayerElement, ILayerImpl, IRenderFunction, ICanvasStaticLayer } from './interfaces';
92
import { layerStyle, stopClicks } from './utils';
103
import { ABaseLayer, ILayerAdapter } from './ABaseLayer';
114
import type { ICanvasLayerOptions } from './public';
@@ -86,7 +79,7 @@ export class CanvasBaseLayer extends ABaseLayer implements ILayerImpl {
8679
ctx.scale(this.transform.zoom * scale, this.transform.zoom * scale);
8780

8881
for (const r of this.callbacks) {
89-
r(ctx);
82+
r(ctx, {});
9083
}
9184

9285
ctx.restore();
@@ -96,8 +89,10 @@ export class CanvasBaseLayer extends ABaseLayer implements ILayerImpl {
9689
return true;
9790
}
9891

99-
renderInto(ctx: CanvasRenderingContext2D, hint: IRenderHint): void {
100-
this.drawImpl(ctx, hint.scale);
92+
renderInto(ctx: CanvasRenderingContext2D): void {
93+
for (const r of this.callbacks) {
94+
r(ctx, { forExport: true });
95+
}
10196
}
10297

10398
resize(width: number, height: number) {

0 commit comments

Comments
 (0)