animation.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  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. // For animation and fading
  31. function getNodesForCells(graph, cells)
  32. {
  33. var nodes = [];
  34. for (var i = 0; i < cells.length; i++)
  35. {
  36. var state = graph.view.getState(cells[i]);
  37. if (state != null)
  38. {
  39. var shapes = graph.cellRenderer.getShapesForState(state);
  40. for (var j = 0; j < shapes.length; j++)
  41. {
  42. if (shapes[j] != null && shapes[j].node != null)
  43. {
  44. nodes.push(shapes[j].node);
  45. }
  46. }
  47. // Adds folding icon
  48. if (state.control != null && state.control.node != null)
  49. {
  50. nodes.push(state.control.node);
  51. }
  52. }
  53. }
  54. return nodes;
  55. };
  56. function fadeIn(nodes)
  57. {
  58. if (nodes != null)
  59. {
  60. for (var i = 0; i < nodes.length; i++)
  61. {
  62. mxUtils.setPrefixedStyle(nodes[i].style, 'transition', null);
  63. nodes[i].style.opacity = '0';
  64. }
  65. window.setTimeout(function()
  66. {
  67. for (var i = 0; i < nodes.length; i++)
  68. {
  69. mxUtils.setPrefixedStyle(nodes[i].style, 'transition', 'all 1s ease-in-out');
  70. nodes[i].style.opacity = '1';
  71. }
  72. }, 0);
  73. }
  74. };
  75. function fadeOut(nodes)
  76. {
  77. if (nodes != null)
  78. {
  79. for (var i = 0; i < nodes.length; i++)
  80. {
  81. mxUtils.setPrefixedStyle(nodes[i].style, 'transition', null);
  82. nodes[i].style.opacity = '1';
  83. }
  84. window.setTimeout(function()
  85. {
  86. for (var i = 0; i < nodes.length; i++)
  87. {
  88. mxUtils.setPrefixedStyle(nodes[i].style, 'transition', 'all 1s ease-in-out');
  89. nodes[i].style.opacity = '0';
  90. }
  91. }, 0);
  92. }
  93. };
  94. function createEdgeAnimation(state)
  95. {
  96. var pts = state.absolutePoints.slice();
  97. var segs = state.segments;
  98. var total = state.length;
  99. var n = pts.length;
  100. return {
  101. execute: function(step, steps)
  102. {
  103. if (state.shape != null)
  104. {
  105. var pts2 = [pts[0]];
  106. var dist = total * step / steps;
  107. for (var i = 1; i < n; i++)
  108. {
  109. if (dist <= segs[i - 1])
  110. {
  111. pts2.push(new mxPoint(pts[i - 1].x + (pts[i].x - pts[i - 1].x) * dist / segs[i - 1],
  112. pts[i - 1].y + (pts[i].y - pts[i - 1].y) * dist / segs[i - 1]));
  113. break;
  114. }
  115. else
  116. {
  117. dist -= segs[i - 1];
  118. pts2.push(pts[i]);
  119. }
  120. }
  121. state.shape.points = pts2;
  122. state.shape.redraw();
  123. }
  124. },
  125. stop: function()
  126. {
  127. if (state.shape != null)
  128. {
  129. state.shape.points = pts;
  130. state.shape.redraw();
  131. }
  132. }
  133. };
  134. };
  135. function createVertexAnimation(state)
  136. {
  137. var bds = new mxRectangle.fromRectangle(state.shape.bounds);
  138. var ttr = null;
  139. if (state.text != null && state.text.node != null && state.text.node.firstChild != null)
  140. {
  141. ttr = state.text.node.firstChild.getAttribute('transform');
  142. }
  143. return {
  144. execute: function(step, steps)
  145. {
  146. if (state.shape != null)
  147. {
  148. var f = step / steps;
  149. state.shape.bounds = new mxRectangle(bds.x, bds.y, bds.width * f, bds.height);
  150. state.shape.redraw();
  151. // Text is animated using CSS3 transitions
  152. if (ttr != null)
  153. {
  154. state.text.node.firstChild.setAttribute('transform', ttr + ' scale(' + f + ',1)');
  155. }
  156. }
  157. },
  158. stop: function()
  159. {
  160. if (state.shape != null)
  161. {
  162. state.shape.bounds = bds;
  163. state.shape.redraw();
  164. if (ttr != null)
  165. {
  166. state.text.node.firstChild.setAttribute('transform', ttr);
  167. }
  168. }
  169. }
  170. };
  171. };
  172. function animateCells(graph, cells, steps, delay)
  173. {
  174. steps = (steps != null) ? steps : 30;
  175. delay = (delay != null) ? delay : 30;
  176. var animations = [];
  177. for (var i = 0; i < cells.length; i++)
  178. {
  179. var state = graph.view.getState(cells[i]);
  180. if (state != null && state.shape != null && graph.model.isEdge(state.cell) &&
  181. state.absolutePoints != null && state.absolutePoints.length > 1)
  182. {
  183. animations.push(createEdgeAnimation(state));
  184. }
  185. else if (state != null && graph.model.isVertex(state.cell) &&
  186. state.shape != null && state.shape.bounds != null)
  187. {
  188. animations.push(createVertexAnimation(state));
  189. // TODO: include descendants
  190. }
  191. }
  192. var step = 0;
  193. function animate()
  194. {
  195. if (step == steps)
  196. {
  197. window.clearInterval(thread);
  198. for (var i = 0; i < animations.length; i++)
  199. {
  200. animations[i].stop();
  201. }
  202. }
  203. else
  204. {
  205. for (var i = 0; i < animations.length; i++)
  206. {
  207. animations[i].execute(step, steps);
  208. }
  209. step++;
  210. }
  211. }
  212. var thread = window.setInterval(animate, delay);
  213. animate();
  214. };
  215. function mapCell(cell, clone, mapping)
  216. {
  217. mapping = (mapping != null) ? mapping : new Object();
  218. mapping[cell.id] = clone;
  219. var childCount = cell.getChildCount();
  220. for (var i = 0; i < childCount; i++)
  221. {
  222. mapCell(cell.getChildAt(i), clone.getChildAt(i), mapping);
  223. }
  224. return mapping;
  225. };
  226. var allowedToRun = false;
  227. var running = false;
  228. function stop()
  229. {
  230. allowedToRun = false;
  231. };
  232. function run(graph, steps, loop)
  233. {
  234. if (!running)
  235. {
  236. allowedToRun = true;
  237. running = true;
  238. graph.getModel().beginUpdate();
  239. try
  240. {
  241. for (var id in graph.getModel().cells)
  242. {
  243. var cell = graph.getModel().cells[id];
  244. if (graph.getModel().isVertex(cell) || graph.getModel().isEdge(cell))
  245. {
  246. graph.setCellStyles('opacity', '0', [cell]);
  247. graph.setCellStyles('noLabel', '1', [cell]);
  248. }
  249. }
  250. }
  251. finally
  252. {
  253. graph.getModel().endUpdate();
  254. }
  255. var mapping = mapCell(editorUi.editor.graph.getModel().getRoot(), graph.getModel().getRoot());
  256. var step = 0;
  257. function next()
  258. {
  259. if (allowedToRun && step < steps.length)
  260. {
  261. var tokens = steps[step].split(' ');
  262. if (tokens.length > 0)
  263. {
  264. if (tokens[0] == 'wait' && tokens.length > 1)
  265. {
  266. window.setTimeout(function()
  267. {
  268. step++;
  269. next();
  270. }, parseFloat(tokens[1]));
  271. }
  272. else
  273. {
  274. if (tokens.length > 1)
  275. {
  276. var cell = mapping[tokens[1]];
  277. if (cell != null)
  278. {
  279. if (tokens[0] == 'show')
  280. {
  281. graph.setCellStyles('opacity', '100', [cell]);
  282. graph.setCellStyles('noLabel', null, [cell]);
  283. if (tokens.length > 2 && tokens[2] == 'fade')
  284. {
  285. fadeIn(getNodesForCells(graph, [cell]));
  286. }
  287. else
  288. {
  289. animateCells(graph, [cell]);
  290. }
  291. }
  292. else if (tokens[0] == 'hide')
  293. {
  294. fadeOut(getNodesForCells(graph, [cell]));
  295. }
  296. }
  297. else
  298. {
  299. console.log('cell not found', id, steps[step]);
  300. }
  301. }
  302. step++;
  303. next();
  304. }
  305. }
  306. }
  307. else
  308. {
  309. running = false;
  310. if (loop)
  311. {
  312. // Workaround for edge animation
  313. graph.refresh();
  314. run(graph, steps, loop);
  315. }
  316. }
  317. };
  318. next();
  319. }
  320. };
  321. /**
  322. *
  323. */
  324. var AnimationWindow = function(editorUi, x, y, w, h)
  325. {
  326. var table = document.createElement('table');
  327. table.style.width = '100%';
  328. table.style.height = '100%';
  329. var tbody = document.createElement('tbody');
  330. var tr1 = document.createElement('tr');
  331. var td11 = document.createElement('td');
  332. td11.style.width = '140px';
  333. var td12 = document.createElement('td');
  334. var tr2 = document.createElement('tr');
  335. tr2.style.height = '40px';
  336. var td21 = document.createElement('td');
  337. td21.setAttribute('colspan', '2');
  338. var list = document.createElement('textarea');
  339. list.style.overflow = 'auto';
  340. list.style.width = '100%';
  341. list.style.height = '100%';
  342. td11.appendChild(list);
  343. var root = editorUi.editor.graph.getModel().getRoot();
  344. if (root.value != null && typeof(root.value) == 'object')
  345. {
  346. list.value = root.value.getAttribute('animation');
  347. }
  348. var container = document.createElement('div');
  349. container.style.border = '1px solid lightGray';
  350. container.style.background = '#ffffff';
  351. container.style.width = '100%';
  352. container.style.height = '100%';
  353. container.style.overflow = 'auto';
  354. mxEvent.disableContextMenu(container);
  355. td12.appendChild(container);
  356. var graph = new Graph(container);
  357. graph.setEnabled(false);
  358. graph.setPanning(true);
  359. graph.foldingEnabled = false;
  360. graph.panningHandler.ignoreCell = true;
  361. graph.panningHandler.useLeftButtonForPanning = true;
  362. graph.minFitScale = null;
  363. graph.maxFitScale = null;
  364. graph.centerZoom = true;
  365. var fadeInBtn = mxUtils.button('Fade In', function()
  366. {
  367. var cells = editorUi.editor.graph.getSelectionCells();
  368. if (cells.length > 0)
  369. {
  370. for (var i = 0; i < cells.length; i++)
  371. {
  372. list.value = list.value + 'show ' + cells[i].id + ' fade\n';
  373. }
  374. list.value = list.value + 'wait 1000\n';
  375. }
  376. });
  377. td21.appendChild(fadeInBtn);
  378. var animateBtn = mxUtils.button('Wipe In', function()
  379. {
  380. var cells = editorUi.editor.graph.getSelectionCells();
  381. if (cells.length > 0)
  382. {
  383. for (var i = 0; i < cells.length; i++)
  384. {
  385. list.value = list.value + 'show ' + cells[i].id + '\n';
  386. }
  387. list.value = list.value + 'wait 1000\n';
  388. }
  389. });
  390. td21.appendChild(animateBtn);
  391. var addBtn = mxUtils.button('Fade Out', function()
  392. {
  393. var cells = editorUi.editor.graph.getSelectionCells();
  394. if (cells.length > 0)
  395. {
  396. for (var i = 0; i < cells.length; i++)
  397. {
  398. list.value = list.value + 'hide ' + cells[i].id + '\n';
  399. }
  400. list.value = list.value + 'wait 1000\n';
  401. }
  402. });
  403. td21.appendChild(addBtn);
  404. var waitBtn = mxUtils.button('Wait', function()
  405. {
  406. list.value = list.value + 'wait 1000\n';
  407. });
  408. td21.appendChild(waitBtn);
  409. var runBtn = mxUtils.button('Preview', function()
  410. {
  411. graph.getModel().clear();
  412. graph.getModel().setRoot(graph.cloneCells([editorUi.editor.graph.getModel().getRoot()])[0]);
  413. graph.maxFitScale = 1;
  414. graph.fit(8);
  415. graph.center();
  416. run(graph, list.value.split('\n'));
  417. });
  418. td21.appendChild(runBtn);
  419. var stopBtn = mxUtils.button('Stop', function()
  420. {
  421. graph.getModel().clear();
  422. stop();
  423. });
  424. td21.appendChild(stopBtn);
  425. var applyBtn = mxUtils.button('Apply', function()
  426. {
  427. editorUi.editor.graph.setAttributeForCell(root, 'animation', list.value);
  428. });
  429. td21.appendChild(applyBtn);
  430. tr1.appendChild(td11);
  431. tr1.appendChild(td12);
  432. tbody.appendChild(tr1);
  433. tr2.appendChild(td21);
  434. tbody.appendChild(tr2);
  435. table.appendChild(tbody);
  436. this.window = new mxWindow('Animation', table, x, y, w, h, true, true);
  437. this.window.destroyOnClose = false;
  438. this.window.setMaximizable(false);
  439. this.window.setResizable(true);
  440. this.window.setClosable(true);
  441. this.window.setVisible(true);
  442. };
  443. // Autostart in chromeless mode
  444. if (editorUi.editor.chromeless)
  445. {
  446. function startAnimation()
  447. {
  448. var root = editorUi.editor.graph.getModel().getRoot();
  449. var result = false;
  450. if (root.value != null && typeof(root.value) == 'object')
  451. {
  452. var desc = root.value.getAttribute('animation');
  453. if (desc != null)
  454. {
  455. run(editorUi.editor.graph, desc.split('\n'), true);
  456. result = true;
  457. }
  458. }
  459. return result;
  460. };
  461. // Wait for file to be loaded if no animation data is present
  462. if (!startAnimation())
  463. {
  464. editorUi.editor.addListener('fileLoaded', startAnimation);
  465. }
  466. }
  467. });