@maxgraph/core
Version:
maxGraph is a fully client side JavaScript diagramming library that uses SVG and HTML for rendering.
170 lines (166 loc) • 6.56 kB
JavaScript
/*
Copyright 2021-present The maxGraph project Contributors
Copyright (c) 2006-2015, JGraph Ltd
Copyright (c) 2006-2015, Gaudenz Alder
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { setOpacity } from '../../util/styleUtils.js';
import GeometryChange from '../undoable_changes/GeometryChange.js';
import TerminalChange from '../undoable_changes/TerminalChange.js';
import ValueChange from '../undoable_changes/ValueChange.js';
import ChildChange from '../undoable_changes/ChildChange.js';
import StyleChange from '../undoable_changes/StyleChange.js';
/**
* Provides animation effects.
*
* @category Animation
*/
class Effects {
/**
* Asynchronous animated move operation. See also: <Morphing>.
*
* @example
* ```javascript
* graph.model.addListener(mxEvent.CHANGE, function(sender, evt)
* {
* var changes = evt.getProperty('edit').changes;
*
* if (changes.length < 10)
* {
* Effects.animateChanges(graph, changes);
* }
* });
* ```
*
* @param graph - {@link AbstractGraph} that received the changes.
* @param changes - Array of changes to be animated.
* @param done - Optional function argument that is invoked after the
* last step of the animation.
*/
static animateChanges(graph, changes, done) {
const maxStep = 10;
let step = 0;
const animate = () => {
let isRequired = false;
for (let i = 0; i < changes.length; i += 1) {
const change = changes[i];
if (change instanceof GeometryChange ||
change instanceof TerminalChange ||
change instanceof ValueChange ||
change instanceof ChildChange ||
change instanceof StyleChange) {
// @ts-ignore
const state = graph.getView().getState(change.cell || change.child, false);
if (state != null) {
isRequired = true;
if (change.constructor !== GeometryChange || change.cell.isEdge()) {
setOpacity(state.shape.node, (100 * step) / maxStep);
}
else {
const { scale } = graph.getView();
const geometry = change.geometry;
const previous = change.previous;
const dx = (geometry.x - previous.x) * scale;
const dy = (geometry.y - previous.y) * scale;
const sx = (geometry.width - previous.width) * scale;
const sy = (geometry.height - previous.height) * scale;
if (step === 0) {
state.x -= dx;
state.y -= dy;
state.width -= sx;
state.height -= sy;
}
else {
state.x += dx / maxStep;
state.y += dy / maxStep;
state.width += sx / maxStep;
state.height += sy / maxStep;
}
graph.cellRenderer.redraw(state);
// Fades all connected edges and children
Effects.cascadeOpacity(graph, change.cell, (100 * step) / maxStep);
}
}
}
}
if (step < maxStep && isRequired) {
step++;
window.setTimeout(animate, delay);
}
else if (done != null) {
done();
}
};
const delay = 30;
animate();
}
/**
* Sets the opacity on the given cell and its descendants.
*
* @param graph - {@link AbstractGraph} that contains the cells.
* @param cell - {@link Cell} to set the opacity for.
* @param opacity - New value for the opacity in %.
*/
static cascadeOpacity(graph, cell, opacity) {
// Fades all children
const childCount = cell.getChildCount();
for (let i = 0; i < childCount; i += 1) {
const child = cell.getChildAt(i);
const childState = graph.getView().getState(child);
if (childState != null) {
setOpacity(childState.shape.node, opacity);
Effects.cascadeOpacity(graph, child, opacity);
}
}
// Fades all connected edges
const edges = cell.getEdges();
if (edges != null) {
for (let i = 0; i < edges.length; i += 1) {
const edgeState = graph.getView().getState(edges[i]);
if (edgeState != null) {
setOpacity(edgeState.shape.node, opacity);
}
}
}
}
/**
* Asynchronous fade-out operation.
*/
static fadeOut(node, from, remove, step, delay, isEnabled) {
step = step || 40;
delay = delay || 30;
let opacity = from || 100;
setOpacity(node, opacity);
if (isEnabled || isEnabled == null) {
const f = () => {
opacity = Math.max(opacity - step, 0);
setOpacity(node, opacity);
if (opacity > 0) {
window.setTimeout(f, delay);
}
else {
node.style.visibility = 'hidden';
if (remove && node.parentNode) {
node.parentNode.removeChild(node);
}
}
};
window.setTimeout(f, delay);
}
else {
node.style.visibility = 'hidden';
if (remove && node.parentNode) {
node.parentNode.removeChild(node);
}
}
}
}
export default Effects;