jsmind.screenshot.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*
  2. * Released under BSD License
  3. * Copyright (c) 2014-2021 hizzgdev@163.com
  4. *
  5. * Project Home:
  6. * https://github.com/hizzgdev/jsmind/
  7. */
  8. (function ($w) {
  9. 'use strict';
  10. var __name__ = 'jsMind';
  11. var jsMind = $w[__name__];
  12. if (!jsMind) { return; }
  13. if (typeof jsMind.screenshot != 'undefined') { return; }
  14. var $d = $w.document;
  15. var $c = function (tag) { return $d.createElement(tag); };
  16. var css = function (cstyle, property_name) {
  17. return cstyle.getPropertyValue(property_name);
  18. };
  19. var is_visible = function (cstyle) {
  20. var visibility = css(cstyle, 'visibility');
  21. var display = css(cstyle, 'display');
  22. return (visibility !== 'hidden' && display !== 'none');
  23. };
  24. var jcanvas = {};
  25. jcanvas.rect = function (ctx, x, y, w, h, r) {
  26. if (w < 2 * r) r = w / 2;
  27. if (h < 2 * r) r = h / 2;
  28. ctx.moveTo(x + r, y);
  29. ctx.arcTo(x + w, y, x + w, y + h, r);
  30. ctx.arcTo(x + w, y + h, x, y + h, r);
  31. ctx.arcTo(x, y + h, x, y, r);
  32. ctx.arcTo(x, y, x + w, y, r);
  33. };
  34. jcanvas.text_multiline = function (ctx, text, x, y, w, h, lineheight) {
  35. var line = '';
  36. var text_len = text.length;
  37. var chars = text.split('');
  38. var test_line = null;
  39. ctx.textAlign = 'left';
  40. ctx.textBaseline = 'top';
  41. for (var i = 0; i < text_len; i++) {
  42. test_line = line + chars[i];
  43. if (ctx.measureText(test_line).width > w && i > 0) {
  44. ctx.fillText(line, x, y);
  45. line = chars[i];
  46. y += lineheight;
  47. } else {
  48. line = test_line;
  49. }
  50. }
  51. ctx.fillText(line, x, y);
  52. };
  53. jcanvas.text_ellipsis = function (ctx, text, x, y, w, h) {
  54. var center_y = y + h / 2;
  55. var text = jcanvas.fittingString(ctx, text, w);
  56. ctx.textAlign = 'left';
  57. ctx.textBaseline = 'middle';
  58. ctx.fillText(text, x, center_y, w);
  59. };
  60. jcanvas.fittingString = function (ctx, text, max_width) {
  61. var width = ctx.measureText(text).width;
  62. var ellipsis = '…';
  63. var ellipsis_width = ctx.measureText(ellipsis).width;
  64. if (width <= max_width || width <= ellipsis_width) {
  65. return text;
  66. } else {
  67. var len = text.length;
  68. while (width >= max_width - ellipsis_width && len-- > 0) {
  69. text = text.substring(0, len);
  70. width = ctx.measureText(text).width;
  71. }
  72. return text + ellipsis;
  73. }
  74. };
  75. jcanvas.image = function (ctx, url, x, y, w, h, r, rotation, callback) {
  76. var img = new Image();
  77. img.onload = function () {
  78. ctx.save();
  79. ctx.translate(x, y);
  80. ctx.save();
  81. ctx.beginPath();
  82. jcanvas.rect(ctx, 0, 0, w, h, r);
  83. ctx.closePath();
  84. ctx.clip();
  85. ctx.translate(w / 2, h / 2);
  86. ctx.rotate(rotation * Math.PI / 180);
  87. ctx.drawImage(img, -w / 2, -h / 2);
  88. ctx.restore();
  89. ctx.restore();
  90. !!callback && callback();
  91. }
  92. img.src = url;
  93. };
  94. jsMind.screenshot = function (jm) {
  95. this.jm = jm;
  96. this.canvas_elem = null;
  97. this.canvas_ctx = null;
  98. this._inited = false;
  99. };
  100. jsMind.screenshot.prototype = {
  101. init: function () {
  102. if (this._inited) { return; }
  103. console.log('init');
  104. var c = $c('canvas');
  105. var ctx = c.getContext('2d');
  106. this.canvas_elem = c;
  107. this.canvas_ctx = ctx;
  108. this.jm.view.e_panel.appendChild(c);
  109. this._inited = true;
  110. this.resize();
  111. },
  112. shoot: function (callback) {
  113. this.init();
  114. this._draw(function () {
  115. !!callback && callback();
  116. this.clean();
  117. }.bind(this));
  118. this._watermark();
  119. },
  120. shootDownload: function () {
  121. this.shoot(function () {
  122. this._download();
  123. }.bind(this));
  124. },
  125. shootAsDataURL: function (callback) {
  126. this.shoot(function () {
  127. !!callback && callback(this.canvas_elem.toDataURL());
  128. }.bind(this));
  129. },
  130. resize: function () {
  131. if (this._inited) {
  132. this.canvas_elem.width = this.jm.view.size.w;
  133. this.canvas_elem.height = this.jm.view.size.h;
  134. }
  135. },
  136. clean: function () {
  137. var c = this.canvas_elem;
  138. this.canvas_ctx.clearRect(0, 0, c.width, c.height);
  139. },
  140. _draw: function (callback) {
  141. var ctx = this.canvas_ctx;
  142. ctx.textAlign = 'left';
  143. ctx.textBaseline = 'top';
  144. this._draw_lines(function () {
  145. this._draw_nodes(callback);
  146. }.bind(this));
  147. },
  148. _watermark: function () {
  149. var c = this.canvas_elem;
  150. var ctx = this.canvas_ctx;
  151. ctx.textAlign = 'right';
  152. ctx.textBaseline = 'bottom';
  153. ctx.fillStyle = '#000';
  154. ctx.font = '11px Verdana,Arial,Helvetica,sans-serif';
  155. ctx.fillText('hizzgdev.github.io/jsmind', c.width - 5.5, c.height - 2.5);
  156. ctx.textAlign = 'left';
  157. ctx.fillText($w.location, 5.5, c.height - 2.5);
  158. },
  159. _draw_lines: function (callback) {
  160. this.jm.view.graph.copy_to(this.canvas_ctx, callback);
  161. },
  162. _draw_nodes: function (callback) {
  163. var nodes = this.jm.mind.nodes;
  164. var node;
  165. for (var nodeid in nodes) {
  166. node = nodes[nodeid];
  167. this._draw_node(node);
  168. }
  169. function check_nodes_ready() {
  170. console.log('check_node_ready' + new Date());
  171. var allOk = true;
  172. for (var nodeid in nodes) {
  173. node = nodes[nodeid];
  174. allOk = allOk & node.ready;
  175. }
  176. if (!allOk) {
  177. $w.setTimeout(check_nodes_ready, 200);
  178. } else {
  179. $w.setTimeout(callback, 200);
  180. }
  181. }
  182. check_nodes_ready();
  183. },
  184. _draw_node: function (node) {
  185. var ctx = this.canvas_ctx;
  186. var view_data = node._data.view;
  187. var node_element = view_data.element;
  188. var ncs = getComputedStyle(node_element);
  189. if (!is_visible(ncs)) {
  190. node.ready = true;
  191. return;
  192. }
  193. var bgcolor = css(ncs, 'background-color');
  194. var round_radius = parseInt(css(ncs, 'border-top-left-radius'));
  195. var color = css(ncs, 'color');
  196. var padding_left = parseInt(css(ncs, 'padding-left'));
  197. var padding_right = parseInt(css(ncs, 'padding-right'));
  198. var padding_top = parseInt(css(ncs, 'padding-top'));
  199. var padding_bottom = parseInt(css(ncs, 'padding-bottom'));
  200. var text_overflow = css(ncs, 'text-overflow');
  201. var font = css(ncs, 'font-style') + ' ' +
  202. css(ncs, 'font-variant') + ' ' +
  203. css(ncs, 'font-weight') + ' ' +
  204. css(ncs, 'font-size') + '/' + css(ncs, 'line-height') + ' ' +
  205. css(ncs, 'font-family');
  206. var rb = {
  207. x: view_data.abs_x,
  208. y: view_data.abs_y,
  209. w: view_data.width + 1,
  210. h: view_data.height + 1
  211. };
  212. var tb = {
  213. x: rb.x + padding_left,
  214. y: rb.y + padding_top,
  215. w: rb.w - padding_left - padding_right,
  216. h: rb.h - padding_top - padding_bottom
  217. };
  218. ctx.font = font;
  219. ctx.fillStyle = bgcolor;
  220. ctx.beginPath();
  221. jcanvas.rect(ctx, rb.x, rb.y, rb.w, rb.h, round_radius);
  222. ctx.closePath();
  223. ctx.fill();
  224. ctx.fillStyle = color;
  225. if ('background-image' in node.data) {
  226. var backgroundUrl = css(ncs, 'background-image').slice(5, -2);
  227. node.ready = false;
  228. var rotation = 0;
  229. if ('background-rotation' in node.data) {
  230. rotation = node.data['background-rotation'];
  231. }
  232. jcanvas.image(ctx, backgroundUrl, rb.x, rb.y, rb.w, rb.h, round_radius, rotation,
  233. function () {
  234. node.ready = true;
  235. });
  236. }
  237. if (!!node.topic) {
  238. if (text_overflow === 'ellipsis') {
  239. jcanvas.text_ellipsis(ctx, node.topic, tb.x, tb.y, tb.w, tb.h);
  240. } else {
  241. var line_height = parseInt(css(ncs, 'line-height'));
  242. jcanvas.text_multiline(ctx, node.topic, tb.x, tb.y, tb.w, tb.h, line_height);
  243. }
  244. }
  245. if (!!view_data.expander) {
  246. this._draw_expander(view_data.expander);
  247. }
  248. if (!('background-image' in node.data)) {
  249. node.ready = true;
  250. }
  251. },
  252. _draw_expander: function (expander) {
  253. var ctx = this.canvas_ctx;
  254. var ncs = getComputedStyle(expander);
  255. if (!is_visible(ncs)) { return; }
  256. var style_left = css(ncs, 'left');
  257. var style_top = css(ncs, 'top');
  258. var font = css(ncs, 'font');
  259. var left = parseInt(style_left);
  260. var top = parseInt(style_top);
  261. var is_plus = expander.innerHTML === '+';
  262. ctx.lineWidth = 1;
  263. ctx.beginPath();
  264. ctx.arc(left + 7, top + 7, 5, 0, Math.PI * 2, true);
  265. ctx.moveTo(left + 10, top + 7);
  266. ctx.lineTo(left + 4, top + 7);
  267. if (is_plus) {
  268. ctx.moveTo(left + 7, top + 4);
  269. ctx.lineTo(left + 7, top + 10);
  270. }
  271. ctx.closePath();
  272. ctx.stroke();
  273. },
  274. _download: function () {
  275. var c = this.canvas_elem;
  276. var name = this.jm.mind.name + '.png';
  277. if (navigator.msSaveBlob && (!!c.msToBlob)) {
  278. var blob = c.msToBlob();
  279. navigator.msSaveBlob(blob, name);
  280. } else {
  281. var bloburl = this.canvas_elem.toDataURL();
  282. var anchor = $c('a');
  283. if ('download' in anchor) {
  284. anchor.style.visibility = 'hidden';
  285. anchor.href = bloburl;
  286. anchor.download = name;
  287. $d.body.appendChild(anchor);
  288. var evt = $d.createEvent('MouseEvents');
  289. evt.initEvent('click', true, true);
  290. anchor.dispatchEvent(evt);
  291. $d.body.removeChild(anchor);
  292. } else {
  293. location.href = bloburl;
  294. }
  295. }
  296. },
  297. jm_event_handle: function (type, data) {
  298. if (type === jsMind.event_type.resize) {
  299. this.resize();
  300. }
  301. }
  302. };
  303. var screenshot_plugin = new jsMind.plugin('screenshot', function (jm) {
  304. var jss = new jsMind.screenshot(jm);
  305. jm.screenshot = jss;
  306. jm.shoot = function () {
  307. jss.shoot();
  308. };
  309. jm.add_event_listener(function (type, data) {
  310. jss.jm_event_handle.call(jss, type, data);
  311. });
  312. });
  313. jsMind.register_plugin(screenshot_plugin);
  314. })(window);