changing the data with the d3 streamgraph transition

I am utilizing d3 to create a stream graph that closely resembles the official example found here: http://bl.ocks.org/mbostock/4060954:

The only distinction is in how I updated it with fresh data. My aim is not just a vertical (y-value) transition, but also an addition of new data points on the right side. The entire graph should appear compressed horizontally.

Achieving the desired outcome wasn't problematic, however, the issue arises in the transition between the two states not appearing as expected.

You can observe a minimum example showcasing this odd transition effect on JSfiddle: http://jsfiddle.net/jaYJ9/4/

Click the update button to witness the effect

test_data0 = [{"0": 0.0, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.6, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.6}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.3}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.0}]
test_data1 = [{"0": 0.0, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.6, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.6}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.3}, {"0": 0.3, "1": 0.0, "-1...
$('#update').click(function(){
    streamed_history(test_data1)
});
var width = 300,
    height = 200,
    colors = {'0': '#6ff500', '1': '#ffad0a', '-1': '#f90035'},
    feedbacks = [-1, 0, 1],
    stack = d3.layout.stack();
var svg = d3.select("#timeline").append("svg")
    .attr("width", width)
    .attr("height", height);
var y = d3.scale.linear()
    .domain([0, 1])
    .range([height, 0]);

streamed_history(test_data0)

function streamed_history(data) {
    data_array = feedbacks.map(function (f) {
        return data.map(function(element, i) { return {x: i, y: element[f]}; })
    }),
    layers = stack(data_array)
    layers = feedbacks.map(function (f, i) {
        return {layer: layers[i], feedback: f, color: colors[f]}
    })

    var x = d3.scale.linear()
        .domain([0, data.length - 1])
        .range([0, width]);

    var area = d3.svg.area().interpolate("basis")
        .x(function(d) { return x(d.x); })
        .y0(function(d) { return y(d.y0); })
        .y1(function(d) { return y(d.y0 + d.y); });

    //enter
    svg.selectAll("path")
        .data(layers)
      .enter().append("path")
        .attr("d", function (d) {return area(d.layer);})
        .style("fill", function(d) { return d.color; });

    //update
    d3.selectAll("path")
      .data(layers)
    .transition()
      .duration(2000)
      .attr("d", function (d) {return area(d.layer);});
}

Answer №1

The issue at hand revolves around the limitation of SVG animations, where you can only append new points to the end of a path.

Here is a proposed solution that addresses this problem (note: this solution assumes the graphics are vertically dense and have a consistent order for determining the highest graph):

...
var area = d3.svg.area().interpolate("basis")
    ...
    .y0(function(d) { return y(null); }) // The null here plays a crucial role!
    ...
...
// Introducing a new function
function fixPath (path) {
    var Lidx = path.indexOf('L');
    var Cidx =  path.slice(Lidx).indexOf('C');
    var PCidx = path.slice(0,Lidx).lastIndexOf('C');

    var lp = path.substr(PCidx, Lidx-PCidx);
    var ss = path.substr(Lidx, Cidx);

    return (path.slice(0,Lidx) + lp + ss + path.slice(Lidx));
}
...
svg.selectAll("path")
    .data(layers.reverse()) // Reversing the order is necessary!
    .attr("d", function (d) { return fixPath(area(d.layer)); }) // Doubling up the bottom right corner helps avoid artifacts
    ...
...
d3.selectAll("path")
    .data(layers)
    .attr("d", function (d) { return fixPath(area(d.layer)); }) // Remember to apply the fix when updating as well!
    ...

View a working example here: http://jsfiddle.net/f5JSR/2/

Now, let's delve into the explanation...

Each color band in your graph forms a closed path, with d3.js ensuring no overlap between these bands. However, the paths start from the bottom left corner and loop back to themselves. This causes an odd animation effect when new points are added, pushing the entire path counterclockwise.

Initially, I explored using SVG clipping and the fill-rule: evenodd property to address this issue. However, incorporating clipping necessitates compound paths, leading to new points being appended to the end of these compounds paths, perpetuating the problem.

Instead, the provided solution opts for a simpler approach by extending all color bands to the bottom of the graph (y(null);). Furthermore, arranging the paths in descending order ensures the highest ones are rendered first. Note that this method may falter if one graph's height dips below another's.

Lastly, to mitigate artifacting around the bottom right corner due to point displacement, the fixPath function duplicates points at this location.

In conclusion, this solution effectively handles the scenario presented. Hopefully, this elucidation proves beneficial.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Storing values from a content script into textboxes using a button press: a simple guide

I am new to creating chrome extensions and currently utilizing a content script to fetch values. However, I am facing difficulty in loading these values into the popup.html. Here is the code snippet: popup.html <head> <script src ...

Create a debounced and chunked asynchronous queue system that utilizes streams

As someone who is new to the concept of reactive programming, I find myself wondering if there exists a more elegant approach for creating a debounced chunked async queue. But what exactly is a debounced chunked async queue? While the name might need some ...

Insert a span element before an HTML input using JavaScript

On my webpage, there is an html input inside a div. Here is what it looks like: <input type="text" class="form-control" placeholder="Barcode" role="barcode"> Using JavaScript only, I am trying to add the following code into the DOM above it: <s ...

Printing the HTML Template of a widget with multiple loops results in a blank first page being displayed

I have encountered an issue while working with a table and ng-repeat loops in my report widget. The table displays fine on the screen, but when I try to print it, there is always a blank page at the beginning. Interestingly, if I remove the second and thir ...

Anyone have a sample showcasing the integration of VueJS with the latest webpack 4 configuration?

VueJs templates now come with numerous examples and starting project skeletons. However, most of them still utilize webpack v3. Has anyone experimented with webpack 4? I'd love to see how you integrate version 4 of webpack into a VueJs project. Thank ...

Why does Angular not reset form after ng-click event?

Something seems off with my form reset after a ng-click event, am I missing something? I can successfully submit the form, but it doesn't automatically reset. Here is the error message I receive: angular.js:12701 POST 500 (Internal Server Error ...

Broadcast signals to an overarching frame

I have successfully embedded a chatbot (Angular 14 app) in an iframe and now I need to determine whether the frame should be minimized so it can fit within the parent container. My goal is to send custom events to the receiving frame. let iframeCanvas = do ...

Encounter a Config validation error while trying to utilize Nest.js ConfigService within e2e tests

I'm encountering an error despite having the NODE_ENV=development variable in my .env file. The error message reads: ● Test suite failed to run Config validation error: "NODE_ENV" must be one of [development, production] 11 | imports ...

I am retrieving data from a service and passing it to a component using Angular and receiving '[object Object]'

Searching for assistance with the problem below regarding my model class. I've attempted various approaches using the .pipe.map() and importing {map} from rxjs/operators, but still encountering the error message [object Object] export class AppProfile ...

Is there a way for me to respond to an APNS push notification by executing a task specified in the payload?

As a newcomer to objective-c, xcode, and app development, I kindly ask for your patience. I have managed to send a push notification via APNS to my new app. I can view the JSON message and log it using NSSLog. Payload: { aps = { alert = { ...

Do we always need to use eval() when parsing JSON objects?

<!DOCTYPE html> <html> <body> <h2>Creating a JSON Object in JavaScript</h2> <p> Name: <span id="jname"></span><br /> Evaluated Name: <span id="evalname"></span><br /> <p> <s ...

Exploring and cycling through numerous links using Selenium WebDriver in a node.js environment

Decided to switch from Casper.js to Selenium for access to more tools. Currently trying to loop through multiple links and navigate them using node.js along with selenium-webdriver. Struggling to find any helpful documentation or examples, as I keep enco ...

How can one overcome CORS policies to retrieve the title of a webpage using JavaScript?

As I work on a plugin for Obsidian that expands shortened urls like bit.ly or t.co to their full-length versions in Markdown, I encounter a problem. I need to fetch the page title in order to properly create a Markdown link [title](web link). Unfortunatel ...

Issue with Electron and Node.js: Application fails to launch due to inability to locate the app within the modules

I am currently testing out an electron application that is supposed to link to our website. I decided to follow one of the tutorials available. Unfortunately, upon launching the app, I encountered the following error: Error log : akshay@akshay-mint-deskt ...

React component failing to display CSS transitions

The task at hand requires the component to meet the following criteria: Items in a list should be displayed one at a time. There should be two buttons, Prev and Next, to reveal the previous or next item in the list. Clicking the Back/Next button should tr ...

Javascript: Issue with loading image resource onto a specific div element

Seeking guidance on how to display the actual image resource on a div tag: Here is the script in full: var smileys = []; smileys[":)"] = "happy.png"; smileys[":D"] = "laugh.png"; smileys[":3"] = "meow.png"; smileys[":{"] = "must.png"; smileys[":V"] = ...

Uniform Image Sizes in Bootstrap Carousel (With One Exception)

I am encountering a JavaScript exception related to image size. I am trying to set a fixed size for the images in my carousel, allowing them to auto adjust or resize without concern for distortion or pixelation. I have experimented with max-width and widt ...

Tips for combining several JSON objects in a Node.js/Jade merge operation

Given the code below which sets up various configurations for a company: default.js (utilized by config.js to load base configurations) { "templateData": { "corp": { "corpName": "Company", "DepartmentOne": { "name": "Dep ...

Having trouble getting CSURF (CSRF middleware) to function properly with XSRF in AngularJS

Struggling to get the CSRF middleware working with Express and Angular? You're not alone. Despite various guides on the internet, the process remains unclear. Express 4.0 uses csurf as its CSRF middleware, while Angular requires setting X-XSRF-TOKEN. ...

Being able to automatically update my JSON file without needing to manually refresh the webpage is a feature I am interested in exploring

I have a server that automatically updates a JSON file. However, the JavaScript code I have implemented below reads the JSON file and displays it to the client, but it always refreshes the page. I am looking for a solution on how to read my JSON file ever ...