animation.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /**
  2. * Explore plugin.
  3. */
  4. Draw.loadPlugin(function(editorUi)
  5. {
  6. // Adds resource for action
  7. mxResources.parse('animation=Animation...');
  8. // Adds action
  9. editorUi.actions.addAction('animation', function()
  10. {
  11. if (this.animationWindow == null)
  12. {
  13. // LATER: Check outline window for initial placement
  14. this.animationWindow = new AnimationWindow(editorUi, (document.body.offsetWidth - 480) / 2,
  15. 120, 640, 480);
  16. this.animationWindow.window.setVisible(true);
  17. }
  18. else
  19. {
  20. this.animationWindow.window.setVisible(!this.animationWindow.window.isVisible());
  21. }
  22. });
  23. var menu = editorUi.menus.get('extras');
  24. var oldFunct = menu.funct;
  25. menu.funct = function(menu, parent)
  26. {
  27. oldFunct.apply(this, arguments);
  28. editorUi.menus.addMenuItems(menu, ['-', 'animation'], parent);
  29. };
  30. function animateCells(graph, cells, steps, delay)
  31. {
  32. graph.executeAnimations(graph.createWipeAnimations(cells, true), null, steps, delay);
  33. };
  34. function mapCell(cell, clone, mapping)
  35. {
  36. mapping = (mapping != null) ? mapping : new Object();
  37. mapping[cell.id] = clone;
  38. var childCount = cell.getChildCount();
  39. for (var i = 0; i < childCount; i++)
  40. {
  41. mapCell(cell.getChildAt(i), clone.getChildAt(i), mapping);
  42. }
  43. return mapping;
  44. };
  45. var allowedToRun = false;
  46. var running = false;
  47. function stop()
  48. {
  49. allowedToRun = false;
  50. };
  51. function run(graph, steps, loop)
  52. {
  53. if (!running)
  54. {
  55. allowedToRun = true;
  56. running = true;
  57. graph.getModel().beginUpdate();
  58. try
  59. {
  60. for (var id in graph.getModel().cells)
  61. {
  62. var cell = graph.getModel().cells[id];
  63. if (graph.getModel().isVertex(cell) || graph.getModel().isEdge(cell))
  64. {
  65. graph.setCellStyles('opacity', '0', [cell]);
  66. graph.setCellStyles('noLabel', '1', [cell]);
  67. }
  68. }
  69. }
  70. finally
  71. {
  72. graph.getModel().endUpdate();
  73. }
  74. var mapping = mapCell(editorUi.editor.graph.getModel().getRoot(), graph.getModel().getRoot());
  75. var step = 0;
  76. function next()
  77. {
  78. if (allowedToRun && step < steps.length)
  79. {
  80. var tokens = steps[step].split(' ');
  81. if (tokens.length > 0)
  82. {
  83. if (tokens[0] == 'wait' && tokens.length > 1)
  84. {
  85. window.setTimeout(function()
  86. {
  87. step++;
  88. next();
  89. }, parseFloat(tokens[1]));
  90. }
  91. else
  92. {
  93. if (tokens.length > 1)
  94. {
  95. var cell = mapping[tokens[1]];
  96. if (cell != null)
  97. {
  98. if (tokens[0] == 'show')
  99. {
  100. graph.setCellStyles('opacity', '100', [cell]);
  101. graph.setCellStyles('noLabel', null, [cell]);
  102. if (tokens.length > 2 && tokens[2] == 'fade')
  103. {
  104. Graph.fadeNodes(graph.getNodesForCells([cell]), 0, 1);
  105. }
  106. else
  107. {
  108. animateCells(graph, [cell]);
  109. }
  110. }
  111. else if (tokens[0] == 'flow')
  112. {
  113. if (graph.model.isEdge(cell))
  114. {
  115. toggleFlowAnim(graph, [cell], tokens[2]);
  116. }
  117. }
  118. else if (tokens[0] == 'hide')
  119. {
  120. Graph.fadeNodes(graph.getNodesForCells([cell]), 1, 0);
  121. }
  122. }
  123. else
  124. {
  125. console.log('cell not found', id, steps[step]);
  126. }
  127. }
  128. step++;
  129. next();
  130. }
  131. }
  132. }
  133. else
  134. {
  135. running = false;
  136. if (loop)
  137. {
  138. // Workaround for edge animation
  139. graph.refresh();
  140. run(graph, steps, loop);
  141. }
  142. }
  143. };
  144. next();
  145. }
  146. };
  147. /**
  148. *
  149. */
  150. var AnimationWindow = function(editorUi, x, y, w, h)
  151. {
  152. var table = document.createElement('table');
  153. table.style.width = '100%';
  154. table.style.height = '100%';
  155. var tbody = document.createElement('tbody');
  156. var tr1 = document.createElement('tr');
  157. var td11 = document.createElement('td');
  158. td11.style.width = '140px';
  159. var td12 = document.createElement('td');
  160. var tr2 = document.createElement('tr');
  161. tr2.style.height = '40px';
  162. var td21 = document.createElement('td');
  163. td21.setAttribute('colspan', '2');
  164. var list = document.createElement('textarea');
  165. list.style.overflow = 'auto';
  166. list.style.width = '100%';
  167. list.style.height = '100%';
  168. td11.appendChild(list);
  169. var root = editorUi.editor.graph.getModel().getRoot();
  170. if (root.value != null && typeof(root.value) == 'object')
  171. {
  172. list.value = root.value.getAttribute('animation');
  173. }
  174. var container = document.createElement('div');
  175. container.style.border = '1px solid lightGray';
  176. container.style.background = '#ffffff';
  177. container.style.width = '100%';
  178. container.style.height = '100%';
  179. container.style.overflow = 'auto';
  180. mxEvent.disableContextMenu(container);
  181. td12.appendChild(container);
  182. var graph = new Graph(container);
  183. graph.setEnabled(false);
  184. graph.setPanning(true);
  185. graph.foldingEnabled = false;
  186. graph.panningHandler.ignoreCell = true;
  187. graph.panningHandler.useLeftButtonForPanning = true;
  188. graph.minFitScale = null;
  189. graph.maxFitScale = null;
  190. graph.centerZoom = true;
  191. var buttons = {
  192. 'Fade In': 'show CELL fade',
  193. 'Wipe In': 'show CELL',
  194. 'Fade Out': 'hide CELL',
  195. 'Flow On': 'flow CELL start',
  196. 'Flow Off': 'flow CELL stop',
  197. 'Flow Toggle': 'flow CELL',
  198. 'Wait': '', // added by default
  199. }
  200. var bkeys = Object.keys(buttons);
  201. for (var i = 0; i < bkeys.length; i++)
  202. {
  203. var wait = 'wait 1000\n';
  204. (function(key)
  205. {
  206. var btn = mxUtils.button(key, function()
  207. {
  208. // we have a cell object
  209. var val = buttons[key]
  210. if (val.indexOf('CELL') > -1)
  211. {
  212. var cells = editorUi.editor.graph.getSelectionCells();
  213. if (cells.length > 0)
  214. {
  215. for (var i = 0; i < cells.length; i++)
  216. {
  217. var tmp = val.replace('CELL', cells[i].id)
  218. list.value += tmp + '\n'
  219. }
  220. list.value += wait
  221. }
  222. }
  223. else
  224. {
  225. if (val)
  226. {
  227. list.value += val + '\n'
  228. }
  229. list.value += wait
  230. }
  231. });
  232. td21.appendChild(btn);
  233. })(bkeys[i]);
  234. }
  235. var runBtn = mxUtils.button('Preview', function()
  236. {
  237. graph.getModel().clear();
  238. graph.getModel().setRoot(graph.cloneCells([editorUi.editor.graph.getModel().getRoot()])[0]);
  239. graph.maxFitScale = 1;
  240. graph.fit(8);
  241. graph.center();
  242. run(graph, list.value.split('\n'));
  243. });
  244. td21.appendChild(runBtn);
  245. var stopBtn = mxUtils.button('Stop', function()
  246. {
  247. graph.getModel().clear();
  248. stop();
  249. });
  250. td21.appendChild(stopBtn);
  251. var applyBtn = mxUtils.button('Apply', function()
  252. {
  253. editorUi.editor.graph.setAttributeForCell(root, 'animation', list.value);
  254. });
  255. td21.appendChild(applyBtn);
  256. tr1.appendChild(td11);
  257. tr1.appendChild(td12);
  258. tbody.appendChild(tr1);
  259. tr2.appendChild(td21);
  260. tbody.appendChild(tr2);
  261. table.appendChild(tbody);
  262. this.window = new mxWindow('Animation', table, x, y, w, h, true, true);
  263. this.window.destroyOnClose = false;
  264. this.window.setMaximizable(false);
  265. this.window.setResizable(true);
  266. this.window.setClosable(true);
  267. this.window.setVisible(true);
  268. };
  269. // Autostart in chromeless mode
  270. if (editorUi.editor.isChromelessView())
  271. {
  272. function startAnimation()
  273. {
  274. var root = editorUi.editor.graph.getModel().getRoot();
  275. var result = false;
  276. if (root.value != null && typeof(root.value) == 'object')
  277. {
  278. var desc = root.value.getAttribute('animation');
  279. if (desc != null)
  280. {
  281. run(editorUi.editor.graph, desc.split('\n'), true);
  282. result = true;
  283. }
  284. }
  285. return result;
  286. };
  287. // Wait for file to be loaded if no animation data is present
  288. if (!startAnimation())
  289. {
  290. editorUi.editor.addListener('fileLoaded', startAnimation);
  291. }
  292. }
  293. // Add flow capability
  294. function toggleFlowAnim(graph, cells, status)
  295. {
  296. if (!status)
  297. {
  298. status = 'toggle'
  299. }
  300. for (var i = 0; i < cells.length; i++)
  301. {
  302. if (editorUi.editor.graph.model.isEdge(cells[i]))
  303. {
  304. var state = graph.view.getState(cells[i]);
  305. if (state && state.shape != null)
  306. {
  307. var paths = state.shape.node.getElementsByTagName('path');
  308. if (paths.length > 1)
  309. {
  310. if ((status == 'toggle' && paths[1].getAttribute('class') == 'mxEdgeFlow') || status == 'stop')
  311. {
  312. paths[1].removeAttribute('class');
  313. if (mxUtils.getValue(state.style, mxConstants.STYLE_DASHED, '0') != '1')
  314. {
  315. paths[1].removeAttribute('stroke-dasharray');
  316. }
  317. }
  318. else if ((status == 'toggle' && paths[1].getAttribute('class') != 'mxEdgeFlow') || status == 'start')
  319. {
  320. paths[1].setAttribute('class', 'mxEdgeFlow');
  321. if (mxUtils.getValue(state.style, mxConstants.STYLE_DASHED, '0') != '1')
  322. {
  323. paths[1].setAttribute('stroke-dasharray', '8');
  324. }
  325. }
  326. }
  327. }
  328. }
  329. }
  330. };
  331. function showCell(graph, cell)
  332. {
  333. graph.setCellStyles('opacity', '100', cell);
  334. graph.setCellStyles('noLabel', null, [cell]);
  335. nodes = graph.getNodesForCells([cell]);
  336. if (nodes != null)
  337. {
  338. for (var i = 0; i < nodes.length; i++)
  339. {
  340. mxUtils.setPrefixedStyle(nodes[i].style, 'transition', null);
  341. nodes[i].style.opacity = '0';
  342. }
  343. }
  344. }
  345. try
  346. {
  347. var style = document.createElement('style')
  348. style.type = 'text/css';
  349. style.innerHTML = ['.mxEdgeFlow {',
  350. 'animation: mxEdgeFlow 0.5s linear;',
  351. 'animation-iteration-count: infinite;',
  352. '}',
  353. '@keyframes mxEdgeFlow {',
  354. 'to {',
  355. 'stroke-dashoffset: -16;',
  356. '}',
  357. '}'].join('\n');
  358. document.getElementsByTagName('head')[0].appendChild(style);
  359. }
  360. catch (e)
  361. {
  362. // ignore
  363. }
  364. });