11import Two from "two.js"
22import 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 ( / x m l n s = \" / 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 ( / h e i g h t = \" ( \d + ) / m) ;
205+ var height = match && match [ 1 ] ? parseInt ( match [ 1 ] , 10 ) : 200 ;
206+ var match = svgText . match ( / w i d t h = \" ( \d + ) / m) ;
207+ var width = match && match [ 1 ] ? parseInt ( match [ 1 ] , 10 ) : 200 ;
208+
209+
210+ // it needs a namespace
211+ if ( ! svgText . match ( / x m l n s = \" / 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}
137269export default MindmapViewer
0 commit comments