JSFiddle - React, Tailwind, and code Playground

by john_s

HTML

<div id="diagram">
    <img id="clientIcon" src="https://cdn3.iconfinder.com/data/icons/streamline-icon-set-free-pack/48/Streamline-04-48.png"/>
    <img id="networkIcon" src="https://cdn1.iconfinder.com/data/icons/ecqlipse2/NETWORK%20-%20LAN.png" />
    <img id="serverIcon" src="https://cdn3.iconfinder.com/data/icons/streamline-icon-set-free-pack/48/Streamline-04-48.png" />
    <div id="laneContainer">
    </div>
</div>

<div>
    Speed (ms):
    <input type="number" id="speed" value="500" /><br />
    Packets:
    <input type="number" id="packets" value="1" /><br />
    <button type="button" id="apply">Apply!</button>
</div>

CSS

#diagram {
    position: relative;
    margin: 10px;
    width: 600px;
}
#clientIcon {
    position: absolute;
    left: 0px;
    width: 50px;
    height: 50px;
}
#networkIcon {
    position: absolute;
    left: 275px;
    width: 50px;
    height: 50px;
}
#serverIcon {
    position: absolute;
    left: 550px;
    width: 50px;
    height: 50px;
}
#laneContainer {
    position: absolute;
    left: 55px;
    width: 490px;
}
.lane {
    position: absolute;
    width: 100%;
    height: 20px;
    border: 1px dashed gray;
    opacity: 0.5;
    z-index: -1;
}
.data {
    position: absolute;
    width: 20px;
    height: 20px;
    left: 5px;
    text-align: center;
    vertical-align: middle;
    background-color: lightgreen;
}

JavaScript

var $diagram = $('#diagram'),
    $laneContainer = $('#laneContainer'),
    $dataContainer = $('#dataContainer');

function udpateLanes(count) {
    $laneContainer.empty();
    for (var i = 0; i < count; i++) {
        var $lane = $('<div class="lane"></div>').css({ top: (20 * i) + 'px' });
        $laneContainer.append($lane);
    }
    $diagram.animate({ height: (count * 20 + 20) + 'px' }, 'fast');
}

$('#apply').click(function() {
    var $button = $(this),
        speed = parseInt($("#speed").val(), 10),
        dataCount = parseInt($("#packets").val(), 10),
        downDuration = (speed/4) * dataCount;

    $button.prop('disabled', true);
    
    udpateLanes(dataCount * 2);

    var $datas = $();

    for (var i = 0; i < dataCount; i++) {
        $datas = $datas.add($('<div class="data"><span>' + (i + 1) + '</span></div>').css({ top: (20 * i) + 'px' }));
    }
    
    $datas.hide().appendTo($laneContainer);
    $datas.fadeIn();
    $datas.animate({ left: '+=230px' }, speed);
    $datas.animate({ left: '+=230px' }, speed);
    $datas.queue(function(next) {
        $(this).children().fadeOut((downDuration / 2), function() {
            $(this).text(function(i, text) { return 'a' + text; });
        }).fadeIn((downDuration / 2));
        next();
    });
    $datas.animate({ top: '+=' + (20 * dataCount) + 'px', backgroundColor: '#f09090' }, downDuration);
    $datas.animate({ left: '-=230px' }, speed);
    $datas.animate({ left: '-=230px' }, speed);
    $datas.fadeOut();
    $datas.promise().done(function() {
        $("#packets").val(dataCount * 2);
        udpateLanes(dataCount * 4);
        $button.prop('disabled', false);
    });
});

udpateLanes(2);