46332b0332
Former-commit-id: 20a8eb8d74
545 lines
12 KiB
JavaScript
545 lines
12 KiB
JavaScript
/**
|
|
* Explore plugin.
|
|
*/
|
|
Draw.loadPlugin(function(editorUi)
|
|
{
|
|
// Adds resource for action
|
|
mxResources.parse('animation=Animation...');
|
|
|
|
// Adds action
|
|
editorUi.actions.addAction('animation', function()
|
|
{
|
|
if (this.animationWindow == null)
|
|
{
|
|
// LATER: Check outline window for initial placement
|
|
this.animationWindow = new AnimationWindow(editorUi, (document.body.offsetWidth - 480) / 2,
|
|
120, 640, 480);
|
|
this.animationWindow.window.setVisible(true);
|
|
}
|
|
else
|
|
{
|
|
this.animationWindow.window.setVisible(!this.animationWindow.window.isVisible());
|
|
}
|
|
});
|
|
|
|
var menu = editorUi.menus.get('extras');
|
|
var oldFunct = menu.funct;
|
|
|
|
menu.funct = function(menu, parent)
|
|
{
|
|
oldFunct.apply(this, arguments);
|
|
|
|
editorUi.menus.addMenuItems(menu, ['-', 'animation'], parent);
|
|
};
|
|
|
|
// For animation and fading
|
|
function getNodesForCells(graph, cells)
|
|
{
|
|
var nodes = [];
|
|
|
|
for (var i = 0; i < cells.length; i++)
|
|
{
|
|
var state = graph.view.getState(cells[i]);
|
|
|
|
if (state != null)
|
|
{
|
|
var shapes = graph.cellRenderer.getShapesForState(state);
|
|
|
|
for (var j = 0; j < shapes.length; j++)
|
|
{
|
|
if (shapes[j] != null && shapes[j].node != null)
|
|
{
|
|
nodes.push(shapes[j].node);
|
|
}
|
|
}
|
|
|
|
// Adds folding icon
|
|
if (state.control != null && state.control.node != null)
|
|
{
|
|
nodes.push(state.control.node);
|
|
}
|
|
}
|
|
}
|
|
|
|
return nodes;
|
|
};
|
|
|
|
function fadeIn(nodes)
|
|
{
|
|
if (nodes != null)
|
|
{
|
|
for (var i = 0; i < nodes.length; i++)
|
|
{
|
|
mxUtils.setPrefixedStyle(nodes[i].style, 'transition', null);
|
|
nodes[i].style.opacity = '0';
|
|
}
|
|
|
|
window.setTimeout(function()
|
|
{
|
|
for (var i = 0; i < nodes.length; i++)
|
|
{
|
|
mxUtils.setPrefixedStyle(nodes[i].style, 'transition', 'all 1s ease-in-out');
|
|
nodes[i].style.opacity = '1';
|
|
}
|
|
}, 0);
|
|
}
|
|
};
|
|
|
|
function fadeOut(nodes)
|
|
{
|
|
if (nodes != null)
|
|
{
|
|
for (var i = 0; i < nodes.length; i++)
|
|
{
|
|
mxUtils.setPrefixedStyle(nodes[i].style, 'transition', null);
|
|
nodes[i].style.opacity = '1';
|
|
}
|
|
|
|
window.setTimeout(function()
|
|
{
|
|
for (var i = 0; i < nodes.length; i++)
|
|
{
|
|
mxUtils.setPrefixedStyle(nodes[i].style, 'transition', 'all 1s ease-in-out');
|
|
nodes[i].style.opacity = '0';
|
|
}
|
|
}, 0);
|
|
}
|
|
};
|
|
|
|
function createEdgeAnimation(state)
|
|
{
|
|
var pts = state.absolutePoints.slice();
|
|
var segs = state.segments;
|
|
var total = state.length;
|
|
var n = pts.length;
|
|
|
|
return {
|
|
execute: function(step, steps)
|
|
{
|
|
if (state.shape != null)
|
|
{
|
|
var pts2 = [pts[0]];
|
|
var dist = total * step / steps;
|
|
|
|
for (var i = 1; i < n; i++)
|
|
{
|
|
if (dist <= segs[i - 1])
|
|
{
|
|
pts2.push(new mxPoint(pts[i - 1].x + (pts[i].x - pts[i - 1].x) * dist / segs[i - 1],
|
|
pts[i - 1].y + (pts[i].y - pts[i - 1].y) * dist / segs[i - 1]));
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
dist -= segs[i - 1];
|
|
pts2.push(pts[i]);
|
|
}
|
|
}
|
|
|
|
state.shape.points = pts2;
|
|
state.shape.redraw();
|
|
}
|
|
},
|
|
stop: function()
|
|
{
|
|
if (state.shape != null)
|
|
{
|
|
state.shape.points = pts;
|
|
state.shape.redraw();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
function createVertexAnimation(state)
|
|
{
|
|
var bds = new mxRectangle.fromRectangle(state.shape.bounds);
|
|
var ttr = null;
|
|
|
|
if (state.text != null && state.text.node != null && state.text.node.firstChild != null)
|
|
{
|
|
ttr = state.text.node.firstChild.getAttribute('transform');
|
|
}
|
|
|
|
return {
|
|
execute: function(step, steps)
|
|
{
|
|
if (state.shape != null)
|
|
{
|
|
var f = step / steps;
|
|
state.shape.bounds = new mxRectangle(bds.x, bds.y, bds.width * f, bds.height);
|
|
state.shape.redraw();
|
|
|
|
// Text is animated using CSS3 transitions
|
|
if (ttr != null)
|
|
{
|
|
state.text.node.firstChild.setAttribute('transform', ttr + ' scale(' + f + ',1)');
|
|
}
|
|
}
|
|
},
|
|
stop: function()
|
|
{
|
|
if (state.shape != null)
|
|
{
|
|
state.shape.bounds = bds;
|
|
state.shape.redraw();
|
|
|
|
if (ttr != null)
|
|
{
|
|
state.text.node.firstChild.setAttribute('transform', ttr);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
function animateCells(graph, cells, steps, delay)
|
|
{
|
|
steps = (steps != null) ? steps : 30;
|
|
delay = (delay != null) ? delay : 30;
|
|
|
|
var animations = [];
|
|
|
|
for (var i = 0; i < cells.length; i++)
|
|
{
|
|
var state = graph.view.getState(cells[i]);
|
|
|
|
if (state != null && state.shape != null && graph.model.isEdge(state.cell) &&
|
|
state.absolutePoints != null && state.absolutePoints.length > 1)
|
|
{
|
|
animations.push(createEdgeAnimation(state));
|
|
}
|
|
else if (state != null && graph.model.isVertex(state.cell) &&
|
|
state.shape != null && state.shape.bounds != null)
|
|
{
|
|
animations.push(createVertexAnimation(state));
|
|
// TODO: include descendants
|
|
}
|
|
}
|
|
|
|
var step = 0;
|
|
|
|
function animate()
|
|
{
|
|
if (step == steps)
|
|
{
|
|
window.clearInterval(thread);
|
|
|
|
for (var i = 0; i < animations.length; i++)
|
|
{
|
|
animations[i].stop();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (var i = 0; i < animations.length; i++)
|
|
{
|
|
animations[i].execute(step, steps);
|
|
}
|
|
|
|
step++;
|
|
}
|
|
}
|
|
|
|
var thread = window.setInterval(animate, delay);
|
|
animate();
|
|
};
|
|
|
|
function mapCell(cell, clone, mapping)
|
|
{
|
|
mapping = (mapping != null) ? mapping : new Object();
|
|
mapping[cell.id] = clone;
|
|
|
|
var childCount = cell.getChildCount();
|
|
|
|
for (var i = 0; i < childCount; i++)
|
|
{
|
|
mapCell(cell.getChildAt(i), clone.getChildAt(i), mapping);
|
|
}
|
|
|
|
return mapping;
|
|
};
|
|
|
|
var allowedToRun = false;
|
|
var running = false;
|
|
|
|
function stop()
|
|
{
|
|
allowedToRun = false;
|
|
};
|
|
|
|
function run(graph, steps, loop)
|
|
{
|
|
if (!running)
|
|
{
|
|
allowedToRun = true;
|
|
running = true;
|
|
|
|
graph.getModel().beginUpdate();
|
|
try
|
|
{
|
|
for (var id in graph.getModel().cells)
|
|
{
|
|
var cell = graph.getModel().cells[id];
|
|
|
|
if (graph.getModel().isVertex(cell) || graph.getModel().isEdge(cell))
|
|
{
|
|
graph.setCellStyles('opacity', '0', [cell]);
|
|
graph.setCellStyles('noLabel', '1', [cell]);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
graph.getModel().endUpdate();
|
|
}
|
|
|
|
var mapping = mapCell(editorUi.editor.graph.getModel().getRoot(), graph.getModel().getRoot());
|
|
var step = 0;
|
|
|
|
function next()
|
|
{
|
|
if (allowedToRun && step < steps.length)
|
|
{
|
|
var tokens = steps[step].split(' ');
|
|
|
|
if (tokens.length > 0)
|
|
{
|
|
if (tokens[0] == 'wait' && tokens.length > 1)
|
|
{
|
|
window.setTimeout(function()
|
|
{
|
|
step++;
|
|
next();
|
|
}, parseFloat(tokens[1]));
|
|
}
|
|
else
|
|
{
|
|
if (tokens.length > 1)
|
|
{
|
|
var cell = mapping[tokens[1]];
|
|
|
|
if (cell != null)
|
|
{
|
|
if (tokens[0] == 'show')
|
|
{
|
|
graph.setCellStyles('opacity', '100', [cell]);
|
|
graph.setCellStyles('noLabel', null, [cell]);
|
|
|
|
if (tokens.length > 2 && tokens[2] == 'fade')
|
|
{
|
|
fadeIn(getNodesForCells(graph, [cell]));
|
|
}
|
|
else
|
|
{
|
|
animateCells(graph, [cell]);
|
|
}
|
|
}
|
|
else if (tokens[0] == 'hide')
|
|
{
|
|
fadeOut(getNodesForCells(graph, [cell]));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
console.log('cell not found', id, steps[step]);
|
|
}
|
|
}
|
|
|
|
step++;
|
|
next();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
running = false;
|
|
|
|
if (loop)
|
|
{
|
|
// Workaround for edge animation
|
|
graph.refresh();
|
|
run(graph, steps, loop);
|
|
}
|
|
}
|
|
};
|
|
|
|
next();
|
|
}
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
var AnimationWindow = function(editorUi, x, y, w, h)
|
|
{
|
|
var table = document.createElement('table');
|
|
table.style.width = '100%';
|
|
table.style.height = '100%';
|
|
var tbody = document.createElement('tbody');
|
|
var tr1 = document.createElement('tr');
|
|
var td11 = document.createElement('td');
|
|
td11.style.width = '140px';
|
|
var td12 = document.createElement('td');
|
|
var tr2 = document.createElement('tr');
|
|
tr2.style.height = '40px';
|
|
var td21 = document.createElement('td');
|
|
td21.setAttribute('colspan', '2');
|
|
|
|
var list = document.createElement('textarea');
|
|
list.style.overflow = 'auto';
|
|
list.style.width = '100%';
|
|
list.style.height = '100%';
|
|
td11.appendChild(list);
|
|
|
|
var root = editorUi.editor.graph.getModel().getRoot();
|
|
|
|
if (root.value != null && typeof(root.value) == 'object')
|
|
{
|
|
list.value = root.value.getAttribute('animation');
|
|
}
|
|
|
|
var container = document.createElement('div');
|
|
container.style.border = '1px solid lightGray';
|
|
container.style.background = '#ffffff';
|
|
container.style.width = '100%';
|
|
container.style.height = '100%';
|
|
container.style.overflow = 'auto';
|
|
|
|
mxEvent.disableContextMenu(container);
|
|
td12.appendChild(container);
|
|
|
|
var graph = new Graph(container);
|
|
graph.setEnabled(false);
|
|
graph.setPanning(true);
|
|
graph.foldingEnabled = false;
|
|
graph.panningHandler.ignoreCell = true;
|
|
graph.panningHandler.useLeftButtonForPanning = true;
|
|
graph.minFitScale = null;
|
|
graph.maxFitScale = null;
|
|
graph.centerZoom = true;
|
|
|
|
var fadeInBtn = mxUtils.button('Fade In', function()
|
|
{
|
|
var cells = editorUi.editor.graph.getSelectionCells();
|
|
|
|
if (cells.length > 0)
|
|
{
|
|
for (var i = 0; i < cells.length; i++)
|
|
{
|
|
list.value = list.value + 'show ' + cells[i].id + ' fade\n';
|
|
}
|
|
|
|
list.value = list.value + 'wait 1000\n';
|
|
}
|
|
});
|
|
td21.appendChild(fadeInBtn);
|
|
|
|
var animateBtn = mxUtils.button('Wipe In', function()
|
|
{
|
|
var cells = editorUi.editor.graph.getSelectionCells();
|
|
|
|
if (cells.length > 0)
|
|
{
|
|
for (var i = 0; i < cells.length; i++)
|
|
{
|
|
list.value = list.value + 'show ' + cells[i].id + '\n';
|
|
}
|
|
|
|
list.value = list.value + 'wait 1000\n';
|
|
}
|
|
});
|
|
td21.appendChild(animateBtn);
|
|
|
|
var addBtn = mxUtils.button('Fade Out', function()
|
|
{
|
|
var cells = editorUi.editor.graph.getSelectionCells();
|
|
|
|
if (cells.length > 0)
|
|
{
|
|
for (var i = 0; i < cells.length; i++)
|
|
{
|
|
list.value = list.value + 'hide ' + cells[i].id + '\n';
|
|
}
|
|
|
|
list.value = list.value + 'wait 1000\n';
|
|
}
|
|
});
|
|
td21.appendChild(addBtn);
|
|
|
|
var waitBtn = mxUtils.button('Wait', function()
|
|
{
|
|
list.value = list.value + 'wait 1000\n';
|
|
});
|
|
td21.appendChild(waitBtn);
|
|
|
|
var runBtn = mxUtils.button('Preview', function()
|
|
{
|
|
graph.getModel().clear();
|
|
graph.getModel().setRoot(graph.cloneCells([editorUi.editor.graph.getModel().getRoot()])[0]);
|
|
graph.maxFitScale = 1;
|
|
graph.fit(8);
|
|
graph.center();
|
|
|
|
run(graph, list.value.split('\n'));
|
|
});
|
|
td21.appendChild(runBtn);
|
|
|
|
var stopBtn = mxUtils.button('Stop', function()
|
|
{
|
|
graph.getModel().clear();
|
|
stop();
|
|
});
|
|
td21.appendChild(stopBtn);
|
|
|
|
var applyBtn = mxUtils.button('Apply', function()
|
|
{
|
|
editorUi.editor.graph.setAttributeForCell(root, 'animation', list.value);
|
|
});
|
|
td21.appendChild(applyBtn);
|
|
|
|
tr1.appendChild(td11);
|
|
tr1.appendChild(td12);
|
|
tbody.appendChild(tr1);
|
|
tr2.appendChild(td21);
|
|
tbody.appendChild(tr2);
|
|
table.appendChild(tbody);
|
|
|
|
this.window = new mxWindow('Animation', table, x, y, w, h, true, true);
|
|
this.window.destroyOnClose = false;
|
|
this.window.setMaximizable(false);
|
|
this.window.setResizable(true);
|
|
this.window.setClosable(true);
|
|
this.window.setVisible(true);
|
|
};
|
|
|
|
// Autostart in chromeless mode
|
|
if (editorUi.editor.chromeless)
|
|
{
|
|
function startAnimation()
|
|
{
|
|
var root = editorUi.editor.graph.getModel().getRoot();
|
|
var result = false;
|
|
|
|
if (root.value != null && typeof(root.value) == 'object')
|
|
{
|
|
var desc = root.value.getAttribute('animation');
|
|
|
|
if (desc != null)
|
|
{
|
|
run(editorUi.editor.graph, desc.split('\n'), true);
|
|
result = true;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
// Wait for file to be loaded if no animation data is present
|
|
if (!startAnimation())
|
|
{
|
|
editorUi.editor.addListener('fileLoaded', startAnimation);
|
|
}
|
|
}
|
|
});
|