/** * This plugin provides a method to animate a sigma instance by interpolating * some node properties. Check the sigma.plugins.animate function doc or the * examples/animate.html code sample to know more. */ (function() { 'use strict'; if (typeof sigma === 'undefined') throw 'sigma is not declared'; sigma.utils.pkg('sigma.plugins'); var _id = 0, _cache = {}; // TOOLING FUNCTIONS: // ****************** function parseColor(val) { if (_cache[val]) return _cache[val]; var result = [0, 0, 0]; if (val.match(/^#/)) { val = (val || '').replace(/^#/, ''); result = (val.length === 3) ? [ parseInt(val.charAt(0) + val.charAt(0), 16), parseInt(val.charAt(1) + val.charAt(1), 16), parseInt(val.charAt(2) + val.charAt(2), 16) ] : [ parseInt(val.charAt(0) + val.charAt(1), 16), parseInt(val.charAt(2) + val.charAt(3), 16), parseInt(val.charAt(4) + val.charAt(5), 16) ]; } else if (val.match(/^ *rgba? *\(/)) { val = val.match( /^ *rgba? *\( *([0-9]*) *, *([0-9]*) *, *([0-9]*) *(,.*)?\) *$/ ); result = [ +val[1], +val[2], +val[3] ]; } _cache[val] = { r: result[0], g: result[1], b: result[2] }; return _cache[val]; } function interpolateColors(c1, c2, p) { c1 = parseColor(c1); c2 = parseColor(c2); var c = { r: c1.r * (1 - p) + c2.r * p, g: c1.g * (1 - p) + c2.g * p, b: c1.b * (1 - p) + c2.b * p }; return 'rgb(' + [c.r | 0, c.g | 0, c.b | 0].join(',') + ')'; } /** * This function will animate some specified node properties. It will * basically call requestAnimationFrame, interpolate the values and call the * refresh method during a specified duration. * * Recognized parameters: * ********************** * Here is the exhaustive list of every accepted parameters in the settings * object: * * {?array} nodes An array of node objects or node ids. If * not specified, all nodes of the graph * will be animated. * {?(function|string)} easing Either the name of an easing in the * sigma.utils.easings package or a * function. If not specified, the * quadraticInOut easing from this package * will be used instead. * {?number} duration The duration of the animation. If not * specified, the "animationsTime" setting * value of the sigma instance will be used * instead. * {?function} onComplete Eventually a function to call when the * animation is ended. * * @param {sigma} s The related sigma instance. * @param {object} animate An hash with the keys being the node properties * to interpolate, and the values being the related * target values. * @param {?object} options Eventually an object with options. */ sigma.plugins.animate = function(s, animate, options) { var o = options || {}, id = ++_id, duration = o.duration || s.settings('animationsTime'), easing = typeof o.easing === 'string' ? sigma.utils.easings[o.easing] : typeof o.easing === 'function' ? o.easing : sigma.utils.easings.quadraticInOut, start = sigma.utils.dateNow(), nodes, startPositions; if (o.nodes && o.nodes.length) { if (typeof o.nodes[0] === 'object') nodes = o.nodes; else nodes = s.graph.nodes(o.nodes); // argument is an array of IDs } else nodes = s.graph.nodes(); // Store initial positions: startPositions = nodes.reduce(function(res, node) { var k; res[node.id] = {}; for (k in animate) if (k in node) res[node.id][k] = node[k]; return res; }, {}); s.animations = s.animations || Object.create({}); sigma.plugins.kill(s); // Do not refresh edgequadtree during drag: var k, c; for (k in s.cameras) { c = s.cameras[k]; c.edgequadtree._enabled = false; } function step() { var p = (sigma.utils.dateNow() - start) / duration; if (p >= 1) { nodes.forEach(function(node) { for (var k in animate) if (k in animate) node[k] = node[animate[k]]; }); // Allow to refresh edgequadtree: var k, c; for (k in s.cameras) { c = s.cameras[k]; c.edgequadtree._enabled = true; } s.refresh(); if (typeof o.onComplete === 'function') o.onComplete(); } else { p = easing(p); nodes.forEach(function(node) { for (var k in animate) if (k in animate) { if (k.match(/color$/)) node[k] = interpolateColors( startPositions[node.id][k], node[animate[k]], p ); else node[k] = node[animate[k]] * p + startPositions[node.id][k] * (1 - p); } }); s.refresh(); s.animations[id] = requestAnimationFrame(step); } } step(); }; sigma.plugins.kill = function(s) { for (var k in (s.animations || {})) cancelAnimationFrame(s.animations[k]); // Allow to refresh edgequadtree: var k, c; for (k in s.cameras) { c = s.cameras[k]; c.edgequadtree._enabled = true; } }; }).call(window);