Skip to content

Commit 5ceb8a5

Browse files
bluzkyDN.Wangchuk
andauthored
Feature/export image (#14)
* implement export svg * Implement export mindmap to png Co-authored-by: DN.Wangchuk <dung.nt@yeah1.vn>
1 parent 4d8ebe2 commit 5ceb8a5

File tree

3 files changed

+152
-19
lines changed

3 files changed

+152
-19
lines changed

src/js/renderer/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import drawLink from './draw-link.js'
22
import drawNode from './draw-node.js'
3+
import Two from 'two.js'
34

45
/*
56
{ width: 500, height: 500 }

src/js/structure/theme.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
import Style from "./style"
22

33
const DEFAULT_THEME = {
4-
"background": "teal",
4+
"background-color": "#fff",
55
"classes": {
66
"root": {
77
"shape": "rounded_rectangle",
8-
"background-color": "#F79F1F",
9-
"font-size": 24,
8+
"background-color": "#F6212D",
9+
"font-size": 28,
1010
"font-family": "Arial",
11-
"color": "#333",
12-
"line-color": "#F79F1F",
11+
"color": "#fff",
12+
"line-color": "#333333",
1313
"line-width": 4
1414
},
1515
"main-branch": {
1616
"shape": "rounded_rectangle",
17-
"background-color": "#A3CB38",
18-
"font-size": 18,
17+
"background-color": "#0288D1",
18+
"font-size": 20,
1919
"font-family": "Arial",
2020
"color": "#333",
21-
"line-color": "#A3CB38",
21+
"line-color": "inherit",
2222
"line-width": 2
2323
},
2424
"sub-branch": {
25-
"shape": "line",
26-
"background-color": "#fff",
25+
"shape": "rounded_rectangle",
26+
"background-color": "none",
2727
"font-size": 14,
2828
"font-family": "Arial",
2929
"color": "#333"

src/js/viewer.js

Lines changed: 141 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Two from "two.js"
22
import Renderer from "./renderer"
3-
import "two.js/extras/zui"
43

54
/*
65
{ width: 500, height: 500 }
@@ -12,14 +11,12 @@ class MindmapViewer {
1211
throw ("Invalid selector")
1312
}
1413

14+
this.container = el
1515
this.width = el.offsetWidth
1616
this.height = el.offsetHeight
1717
this.two = new Two({ width: this.width, height: this.height }).appendTo(el)
1818
this.renderer = new Renderer(this.two, options)
1919

20-
this.zui = new Two.ZUI(this.two.scene, el);
21-
this.zui.addLimits(0.06, 8);
22-
2320

2421
this.scale = 1
2522
this.zoom = 0
@@ -44,6 +41,7 @@ class MindmapViewer {
4441

4542
render(mindMap) {
4643
this.mindMap = mindMap
44+
this.container.style.backgroundColor = mindMap.theme.style.getAttribute("background-color")
4745
this.renderer.render(mindMap)
4846
this.fitView()
4947
}
@@ -60,8 +58,6 @@ class MindmapViewer {
6058
let dx = x * (this.scale + percent) - x * this.scale
6159
let dy = y * (this.scale + percent) - y * this.scale
6260
this.zoom += percent
63-
// this.setScale(this.zoom / 100)
64-
// this.zui.zoomBy(percent, x, y)
6561
this.two.scene.scale = this.scale = 1 + this.zoom
6662

6763
this.translateBy(-dx, -dy)
@@ -95,7 +91,6 @@ class MindmapViewer {
9591
}
9692

9793
translateBy(dx, dy) {
98-
// this.zui.translateSurface(dx, dy)
9994
this.two.scene.translation.add(dx, dy)
10095
this.two.update()
10196
}
@@ -116,8 +111,8 @@ class MindmapViewer {
116111
var stage = this.two.renderer.domElement;
117112

118113
var onDrag = (e) => {
119-
let dx = e.movementX * this.zui.scale
120-
let dy = e.movementY * this.zui.scale
114+
let dx = e.movementX
115+
let dy = e.movementY
121116
this.translateBy(dx, dy)
122117
}
123118

@@ -133,5 +128,142 @@ class MindmapViewer {
133128
stage.addEventListener("mouseup", onDragEnd)
134129
})
135130
}
131+
132+
getSvgData() {
133+
let svgText = this.container.innerHTML
134+
if (!svgText.match(/xmlns=\"/mi)) {
135+
svgText = svgText.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" ');
136+
}
137+
138+
return svgText
139+
140+
}
141+
142+
downloadDataAsFile(data, filename) {
143+
var element = document.createElement('a');
144+
element.setAttribute('href', data)
145+
element.setAttribute('download', filename);
146+
element.style.display = 'none';
147+
document.body.appendChild(element);
148+
element.click();
149+
document.body.removeChild(element);
150+
}
151+
152+
exportSvg(filename = "mindmap.svg") {
153+
this.downloadDataAsFile('data:image/svg+xml;utf8,' + encodeURIComponent(this.getSvgData()), filename)
154+
}
155+
156+
exportPng(filename = "mindmap.png") {
157+
let bb = this.mindMap.getBoundingBox()
158+
159+
// backup current value
160+
let scale = this.two.scene.scale
161+
let translation = this.two.scene.translation.clone()
162+
163+
// set svg full size
164+
this.two.scene.scale = 1
165+
this.two.scene.translation.set(0, 0)
166+
this.two.width = bb.width
167+
this.two.height = bb.height
168+
169+
// get svg data
170+
this.two.update()
171+
let svgText = this.getSvgData()
172+
173+
// restore original size
174+
this.two.width = this.width
175+
this.two.height = this.height
176+
this.two.scene.scale = scale
177+
this.two.scene.translation.copy(translation)
178+
this.two.update()
179+
180+
let fill = this.mindMap.theme.style.getAttribute("background-color")
181+
this.svgToPng(svgText, 0, fill).then((data) => this.downloadDataAsFile(data, filename))
182+
}
183+
184+
/**
185+
* This function is shamelessly copy from https://sites.google.com/a/mcpher.com/share/Home/excelquirks/gassnips/svgtopng
186+
* converts an svg string to base64 png using the domUrl
187+
* @param {string} svgText the svgtext
188+
* @param {number} [margin=0] the width of the border - the image size will be height+margin by width+margin
189+
* @param {string} [fill] optionally backgrund canvas fill
190+
* @return {Promise} a promise to the bas64 png image
191+
*/
192+
svgToPng(svgText, margin = 0, fill = null) {
193+
var self = this
194+
// convert an svg text to png using the browser
195+
return new Promise(function (resolve, reject) {
196+
try {
197+
// can use the domUrl function from the browser
198+
var domUrl = window.URL || window.webkitURL || window;
199+
if (!domUrl) {
200+
throw new Error("(browser doesnt support this)")
201+
}
202+
203+
// get original size
204+
var match = svgText.match(/height=\"(\d+)/m);
205+
var height = match && match[1] ? parseInt(match[1], 10) : 200;
206+
var match = svgText.match(/width=\"(\d+)/m);
207+
var width = match && match[1] ? parseInt(match[1], 10) : 200;
208+
209+
210+
// it needs a namespace
211+
if (!svgText.match(/xmlns=\"/mi)) {
212+
svgText = svgText.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" ');
213+
}
214+
215+
216+
// create a canvas element to pass through
217+
var canvas = document.createElement("canvas");
218+
canvas.width = width + margin * 2;
219+
canvas.height = height + margin * 2;
220+
var ctx = canvas.getContext("2d");
221+
222+
223+
// make a blob from the svg
224+
var svg = new Blob([svgText], {
225+
type: "image/svg+xml;charset=utf-8"
226+
});
227+
228+
// create a dom object for that image
229+
var url = domUrl.createObjectURL(svg);
230+
231+
// create a new image to hold it the converted type
232+
var img = new Image;
233+
234+
// when the image is loaded we can get it as base64 url
235+
img.onload = function () {
236+
// draw it to the canvas
237+
ctx.drawImage(this, margin, margin);
238+
239+
// if it needs some styling, we need a new canvas
240+
console.log(fill)
241+
if (fill) {
242+
var styled = document.createElement("canvas");
243+
styled.width = canvas.width;
244+
styled.height = canvas.height;
245+
var styledCtx = styled.getContext("2d");
246+
styledCtx.save();
247+
styledCtx.fillStyle = fill;
248+
styledCtx.fillRect(0, 0, canvas.width, canvas.height);
249+
styledCtx.strokeRect(0, 0, canvas.width, canvas.height);
250+
styledCtx.restore();
251+
styledCtx.drawImage(canvas, 0, 0);
252+
canvas = styled;
253+
}
254+
// we don't need the original any more
255+
domUrl.revokeObjectURL(url);
256+
// now we can resolve the promise, passing the base64 url
257+
resolve(canvas.toDataURL());
258+
};
259+
260+
// load the image
261+
img.src = url;
262+
263+
} catch (err) {
264+
reject('failed to convert svg to png ' + err);
265+
}
266+
});
267+
}
136268
}
137269
export default MindmapViewer

0 commit comments

Comments
 (0)