Skip to content

Commit 01ee146

Browse files
committed
Add PNG export button
1 parent 57fac09 commit 01ee146

10 files changed

Lines changed: 95 additions & 6 deletions

example/axes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { scaleLinear } from 'd3-scale';
55
import { select } from 'd3-selection';
66

77
import createScatterplot from '../src';
8+
import { saveAsPng } from './utils';
89

910
const parentWrapper = document.querySelector('#parent-wrapper');
1011
const canvasWrapper = document.querySelector('#canvas-wrapper');
@@ -17,6 +18,7 @@ const opacityEl = document.querySelector('#opacity');
1718
const opacityValEl = document.querySelector('#opacity-value');
1819
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
1920
const resetEl = document.querySelector('#reset');
21+
const exportEl = document.querySelector('#export');
2022
const exampleBg = document.querySelector('#example-axes');
2123

2224
exampleBg.setAttribute('class', 'active');
@@ -83,6 +85,8 @@ const scatterplot = createScatterplot({
8385
showRecticle: true,
8486
});
8587

88+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
89+
8690
console.log(`Scatterplot v${scatterplot.get('version')}`);
8791

8892
scatterplot.subscribe('select', selectHandler);

example/connected-points-by-segments.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint no-console: 0 */
22

33
import createScatterplot from '../src';
4+
import { saveAsPng } from './utils';
45

56
const canvas = document.querySelector('#canvas');
67
const numPointsEl = document.querySelector('#num-points');
@@ -11,6 +12,7 @@ const opacityEl = document.querySelector('#opacity');
1112
const opacityValEl = document.querySelector('#opacity-value');
1213
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
1314
const resetEl = document.querySelector('#reset');
15+
const exportEl = document.querySelector('#export');
1416
const exampleEl = document.querySelector(
1517
'#example-connected-points-by-segment'
1618
);
@@ -61,6 +63,8 @@ const scatterplot = createScatterplot({
6163
pointConnectionSize,
6264
});
6365

66+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
67+
6468
console.log(`Scatterplot v${scatterplot.get('version')}`);
6569

6670
scatterplot.subscribe('select', selectHandler);

example/connected-points.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint no-console: 0 */
22

33
import createScatterplot from '../src';
4+
import { saveAsPng } from './utils';
45

56
const canvas = document.querySelector('#canvas');
67
const numPointsEl = document.querySelector('#num-points');
@@ -11,6 +12,7 @@ const opacityEl = document.querySelector('#opacity');
1112
const opacityValEl = document.querySelector('#opacity-value');
1213
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
1314
const resetEl = document.querySelector('#reset');
15+
const exportEl = document.querySelector('#export');
1416
const exampleEl = document.querySelector('#example-connected-points');
1517

1618
exampleEl.setAttribute('class', 'active');
@@ -59,6 +61,8 @@ const scatterplot = createScatterplot({
5961
pointConnectionSize,
6062
});
6163

64+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
65+
6266
console.log(`Scatterplot v${scatterplot.get('version')}`);
6367

6468
scatterplot.subscribe('select', selectHandler);

example/index.html

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,18 @@
176176
text-decoration-color: rgba(255, 255, 255, 1);
177177
}
178178

179+
.two-button-group {
180+
display: flex;
181+
justify-content: space-between;
182+
}
183+
184+
.two-button-group > button,
185+
.two-button-group > a.button-like {
186+
box-sizing: border-box;
187+
width: calc(50% - 0.125rem);
188+
text-align: center;
189+
}
190+
179191
.smaller {
180192
font-size: 0.9em;
181193
}
@@ -459,12 +471,15 @@ <h1 id="title">Regl Scatterplot</h1>
459471
</li>
460472
</ul>
461473
<hr />
462-
<a
463-
class="button-like"
464-
href="https://github.com/flekschas/regl-scatterplot"
465-
target="_blank"
466-
>Source code</a
467-
>
474+
<div class="two-button-group">
475+
<button id="export">Export as PNG</button>
476+
<a
477+
class="button-like"
478+
href="https://github.com/flekschas/regl-scatterplot"
479+
target="_blank"
480+
>Source code</a
481+
>
482+
</div>
468483
</aside>
469484
</header>
470485
<div id="parent-wrapper">

example/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint no-console: 0 */
22

33
import createScatterplot from '../src';
4+
import { saveAsPng } from './utils';
45

56
const canvas = document.querySelector('#canvas');
67
const numPointsEl = document.querySelector('#num-points');
@@ -11,6 +12,7 @@ const opacityEl = document.querySelector('#opacity');
1112
const opacityValEl = document.querySelector('#opacity-value');
1213
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
1314
const resetEl = document.querySelector('#reset');
15+
const exportEl = document.querySelector('#export');
1416
const exampleEl = document.querySelector('#example-basic');
1517

1618
exampleEl.setAttribute('class', 'active');
@@ -69,6 +71,8 @@ const scatterplot = createScatterplot({
6971
recticleColor,
7072
});
7173

74+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
75+
7276
console.log(`Scatterplot v${scatterplot.get('version')}`);
7377

7478
scatterplot.subscribe('pointover', pointoverHandler);

example/performance-mode.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint no-console: 0 */
22
import { createWorker } from '@flekschas/utils';
3+
import { saveAsPng } from './utils';
34

45
import createScatterplot from '../src';
56
import pointWorkerFn from './performance-mode-point-worker';
@@ -15,6 +16,7 @@ const opacityEl = document.querySelector('#opacity');
1516
const opacityValEl = document.querySelector('#opacity-value');
1617
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
1718
const resetEl = document.querySelector('#reset');
19+
const exportEl = document.querySelector('#export');
1820
const exampleEl = document.querySelector('#example-performance-mode');
1921

2022
exampleEl.setAttribute('class', 'active');
@@ -75,6 +77,8 @@ const scatterplot = createScatterplot({
7577
performanceMode: true,
7678
});
7779

80+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
81+
7882
console.log(`Scatterplot v${scatterplot.get('version')}`);
7983

8084
scatterplot.subscribe('select', selectHandler);

example/size-encoding.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { scaleLog } from 'd3-scale';
44
import { randomExponential } from 'd3-random';
55

66
import createScatterplot from '../src';
7+
import { saveAsPng } from './utils';
78

89
const canvas = document.querySelector('#canvas');
910
const numPointsEl = document.querySelector('#num-points');
@@ -14,6 +15,7 @@ const opacityEl = document.querySelector('#opacity');
1415
const opacityValEl = document.querySelector('#opacity-value');
1516
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
1617
const resetEl = document.querySelector('#reset');
18+
const exportEl = document.querySelector('#export');
1719
const exampleEl = document.querySelector('#example-size-encoding');
1820

1921
exampleEl.setAttribute('class', 'active');
@@ -55,6 +57,8 @@ const scatterplot = createScatterplot({
5557
recticleColor,
5658
});
5759

60+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
61+
5862
console.log(`Scatterplot v${scatterplot.get('version')}`);
5963

6064
scatterplot.subscribe('select', selectHandler);

example/texture-background.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint no-console: 0 */
22

33
import createScatterplot from '../src';
4+
import { saveAsPng } from './utils';
45

56
const canvas = document.querySelector('#canvas');
67
const numPointsEl = document.querySelector('#num-points');
@@ -11,6 +12,7 @@ const opacityEl = document.querySelector('#opacity');
1112
const opacityValEl = document.querySelector('#opacity-value');
1213
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
1314
const resetEl = document.querySelector('#reset');
15+
const exportEl = document.querySelector('#export');
1416
const exampleEl = document.querySelector('#example-background');
1517

1618
exampleEl.setAttribute('class', 'active');
@@ -50,6 +52,8 @@ const scatterplot = createScatterplot({
5052
)}/?random`,
5153
});
5254

55+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
56+
5357
console.log(`Scatterplot v${scatterplot.get('version')}`);
5458

5559
scatterplot.subscribe('select', selectHandler);

example/transition.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint no-console: 0 */
22

33
import createScatterplot from '../src';
4+
import { saveAsPng } from './utils';
45

56
const canvas = document.querySelector('#canvas');
67
const numPointsEl = document.querySelector('#num-points');
@@ -11,6 +12,7 @@ const opacityEl = document.querySelector('#opacity');
1112
const opacityValEl = document.querySelector('#opacity-value');
1213
const clickLassoInitiatorEl = document.querySelector('#click-lasso-initiator');
1314
const resetEl = document.querySelector('#reset');
15+
const exportEl = document.querySelector('#export');
1416
const exampleEl = document.querySelector('#example-transition');
1517

1618
exampleEl.setAttribute('class', 'active');
@@ -53,6 +55,8 @@ const scatterplot = createScatterplot({
5355
recticleColor,
5456
});
5557

58+
exportEl.addEventListener('click', () => saveAsPng(scatterplot));
59+
5660
console.log(`Scatterplot v${scatterplot.get('version')}`);
5761

5862
scatterplot.subscribe('select', selectHandler);

example/utils.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
export function downloadBlob(blob, name = 'file.txt') {
2+
const link = document.createElement('a');
3+
link.href = URL.createObjectURL(blob);
4+
link.download = name;
5+
6+
document.body.appendChild(link);
7+
8+
link.dispatchEvent(
9+
new MouseEvent('click', {
10+
bubbles: true,
11+
cancelable: true,
12+
view: window,
13+
})
14+
);
15+
16+
document.body.removeChild(link);
17+
}
18+
19+
export function saveAsPng(scatterplot) {
20+
const { pixels, width, height } = scatterplot.export();
21+
22+
const c = document.createElement('canvas');
23+
c.width = width;
24+
c.height = height;
25+
26+
const ctx = c.getContext('2d');
27+
ctx.putImageData(new ImageData(pixels, width, height), 0, 0);
28+
29+
// The following is only needed to flip the image vertically. Since `ctx.scale`
30+
// only affects `draw*()` calls and not `put*()` calls we have to draw the
31+
// image twice...
32+
const imageObject = new Image();
33+
imageObject.onload = () => {
34+
ctx.clearRect(0, 0, width, height);
35+
ctx.scale(1, -1);
36+
ctx.drawImage(imageObject, 0, -height);
37+
c.toBlob((blob) => {
38+
downloadBlob(blob, 'scatter.png');
39+
});
40+
};
41+
imageObject.src = c.toDataURL();
42+
}

0 commit comments

Comments
 (0)