SVG Dataflow JS

by balefrost

HTML

<svg id="without-dirty" width="200" height="300">

<defs>
    <marker id="arrow" viewBox="-5 -10 20 20" orient="auto">
        <polygon points="-5,-10 15,0 -5,10 0,0" />
    </marker>
    <clipPath id="node" clipPathUnits="objectBoundingBox">
        <circle cx="0.5" cy="0.5" r="0.5" />
    </clipPath>
</defs>
    
<line x1="100" y1="0" x2="100" y2="43.75" marker-end="url(#arrow)" stroke="black" stroke-width="5" />
<line x1="100" y1="75" x2="67.334" y2="123.998" marker-end="url(#arrow)" stroke="black" stroke-width="5" />
<line x1="100" y1="75" x2="132.666" y2="123.998" marker-end="url(#arrow)" stroke="black" stroke-width="5" />
<line x1="50" y1="150" x2="82.666" y2="198.998" marker-end="url(#arrow)" stroke="black" stroke-width="5" />
<line x1="150" y1="150" x2="117.334" y2="198.998" marker-end="url(#arrow)" stroke="black" stroke-width="5" />
<line x1="100" y1="225" x2="100" y2="288.75" marker-end="url(#arrow)" stroke="black" stroke-width="5" />

<g clip-path="url(#node)" transform="translate(100,-15)">
    <rect x="-10" y="-10" width="20" height="20" fill="red" />
    <rect x="0" y="-10" width="10" height="20" fill="red"></rect>

    <animateTransform id="withoutDirtyStart" begin="withoutDirty.click" dur="0.5s" attributeName="transform" type="translate" from="100,-15" to="100,75" fill="freeze" />
    <animateTransform begin="withoutDirtyStart.begin+0.5s" dur="0.5s" attributeName="transform" type="translate" from="100,75" to="150,150" fill="freeze" />
    <animateTransform begin="withoutDirtyStart.begin+1s" dur="0.5s" attributeName="transform" type="translate" from="150,150" to="100,225" fill="freeze" />
    <animateTransform begin="withoutDirtyStart.begin+1.5s" dur="0.5s" attributeName="transform" type="translate" from="100,225" to="100,315" fill="freeze" />
    <animateTransform begin="withoutDirtyStart.begin+2.5s" dur="0.5s" attributeName="transform" type="translate" from="100,75" to="50,150" fill="freeze" />
    <animateTransform...

JavaScript

(function() {
    var svg = document.querySelector('#without-dirty');
    var svgns = "http://www.w3.org/2000/svg";
    
    function createTransitionable() {
        var rect = document.createElementNS(svgns, 'rect');
        rect.setAttribute('fill', 'green');
        var toRed = document.createElementNS(svgns, 'animate');
        toRed.setAttribute('begin', 'indeterminite');
        toRed.setAttribute('attributeName', 'fill');
        toRed.setAttribute('dur', '0.2s');
        toRed.setAttribute('from', 'green');
        toRed.setAttribute('to', 'red');
        toRed.setAttribute('fill', 'freeze');
        rect.appendChild(toRed);
        var toGreen = document.createElementNS(svgns, 'animate');
        toGreen.setAttribute('begin', 'indeterminite');
        toGreen.setAttribute('attributeName', 'fill');
        toGreen.setAttribute('dur', '0.2s');
        toGreen.setAttribute('from', 'red');
        toGreen.setAttribute('to', 'green');
        toGreen.setAttribute('fill', 'freeze');
        rect.appendChild(toGreen);
        return {
            element: rect,
            goRed: function() { toRed.beginElement(); },
            goGreen: function() { toGreen.beginElement(); }
        };
    }
    
    function createNode(x, y) {
        var g = document.createElementNS(svgns, 'g');
        g.setAttribute('clip-path', 'url(#node)');
        g.setAttribute('transform', 'translate(' + x + ',' + y + ')');
        var rect = createTransitionable();
        rect.element.setAttribute('x', -20);
        rect.element.setAttribute('y', -20);
        rect.element.setAttribute('width', 40);
        rect.element.setAttribute('height', 40);
        g.appendChild(rect.element);
        return {
            element: g,
            goRed: function() { rect.goRed(); },
            goGreen: function() { rect.goGreen(); }
        };
    }

    function createSplitNode(x, y, r) {
        var g = document.createElementNS(svgns, 'g');
        g.setAttribute('clip-path',...