score:1
[UPDATE: According to the comments the code was updated to change with a new line starting from the bottom when the set of keys in the new data are different]
here is a contribution for a better understanding of the problem, and a possible answer.
There is some misuse of the key element. When you define the key of the line, it's for d3 to know that one line is binded to that key. In this case, your key is binded to the path. When you add
this.line = this.line
.data([data], d => d.key)
d3 binds the selection to [data] and will generate exactly one element ([data].length = 1) for this elements, d = data, hence d.key = null. This is the reason why you are not adding multiple lines, because your paths always got the key = null.
So, on the first time everything works as planned, you started a path as zero and then moves it to the final position with the transition.
This path has d attribute generate by the d3.line with a format like M x1 y1 L x2 y2 L x3 y3 ... L x12 y 12
. Exactly 12 points for the first time.
When you swap the data, d3 will check the key (null again) and will consider this as an update.
So, it will interpolate the current path to a new one with the new data. The issue here is that there are no keys to bind the values. As you have now 31 points, it will interpolate the first 12 points (which is the part that you see moving) and add the remaining points (13 to 31). Of course, these last points don't have transition, because they didn't exist.
A possible solution for your case is to use a custom interpolator (that you can build) and use an attrTween to do the interpolation.
Fortunately, someone built one already: https://unpkg.com/d3-interpolate-path/build/d3-interpolate-path.min.js
SO here is a working solution
new Vue({
el: "#app",
data() {
return {
index: 0,
data: [
[{
key: "Jan",
value: 5787
},
{
key: "Feb",
value: 6387
},
{
key: "Mrt",
value: 7375
},
{
key: "Apr",
value: 6220
},
{
key: "Mei",
value: 6214
},
{
key: "Jun",
value: 5205
},
{
key: "Jul",
value: 5025
},
{
key: "Aug",
value: 4267
},
{
key: "Sep",
value: 6901
},
{
key: "Okt",
value: 5800
},
{
key: "Nov",
value: 7414
},
{
key: "Dec",
value: 6547
}
],
[{
"key": 1,
"value": 4431
},
{
"key": 2,
"value": 5027
},
{
"key": 3,
"value": 4586
},
{
"key": 4,
"value": 7342
},
{
"key": 5,
"value": 6724
},
{
"key": 6,
"value": 6070
},
{
"key": 7,
"value": 5137
},
{
"key": 8,
"value": 5871
},
{
"key": 9,
"value": 6997
},
{
"key": 10,
"value": 6481
},
{
"key": 11,
"value": 5194
},
{
"key": 12,
"value": 4428
},
{
"key": 13,
"value": 4790
},
{
"key": 14,
"value": 5825
},
{
"key": 15,
"value": 4709
},
{
"key": 16,
"value": 6867
},
{
"key": 17,
"value": 5555
},
{
"key": 18,
"value": 4451
},
{
"key": 19,
"value": 7137
},
{
"key": 20,
"value": 5353
},
{
"key": 21,
"value": 5048
},
{
"key": 22,
"value": 5169
},
{
"key": 23,
"value": 6650
},
{
"key": 24,
"value": 5918
},
{
"key": 25,
"value": 5679
},
{
"key": 26,
"value": 5546
},
{
"key": 27,
"value": 6899
},
{
"key": 28,
"value": 5541
},
{
"key": 29,
"value": 7193
},
{
"key": 30,
"value": 5006
},
{
"key": 31,
"value": 6580
}
]
]
}
},
mounted() {
// set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 30
},
width = 500 - margin.left - margin.right;
this.height = 200 - margin.top - margin.bottom;
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
this.svg = d3
.select("#my_dataviz")
.append("svg")
.attr(
"viewBox",
`0 0 ${width + margin.left + margin.right} ${this.height +
margin.top +
margin.bottom}`
)
.attr("preserveAspectRatio", "xMinYMin")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// set the ranges
this.xScale = d3
.scalePoint()
.range([0, width])
.domain(
this.data.map(function(d) {
return d.key;
})
)
.padding(0.5);
this.yScale = d3.scaleLinear().rangeRound([this.height, 0]);
this.yScale.domain([0, 7000]);
// Draw Axis
this.xAxis = d3.axisBottom(this.xScale);
this.xAxisDraw = this.svg
.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${this.height})`);
this.yAxis = d3
.axisLeft(this.yScale)
.tickValues([0, 7000])
.tickFormat(d => {
if (d > 1000) {
d = Math.round(d / 1000);
d = d + "K";
}
return d;
});
this.yAxisDraw = this.svg.append("g").attr("class", "y axis");
this.update(this.data[this.index]);
},
methods: {
swapData() {
if (this.index === 0) this.index = 1;
else this.index = 0;
this.update(this.data[this.index]);
},
update(data) {
// Update scales.
this.xScale.domain(data.map(d => d.key));
this.yScale.domain([0, 7000]);
// Set up transition.
const dur = 1000;
const t = d3.transition().duration(dur);
const line = d3
.line()
.x(d => {
return this.xScale(d.key);
})
.y((d) => {
return this.yScale(d.value);
});
// Update line.
this.line = this.svg.selectAll(".line")
this.line = this.line
.data([data], d => d.reduce((key, elem) => key + '_' + elem.key, ''))
.join(
enter => {
enter
.append("path")
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#206BF3")
.attr("stroke-width", 4)
.attr(
"d",
d3
.line()
.x(d => {
return this.xScale(d.key);
})
.y(() => {
return this.yScale(0);
})
)
.transition(t)
.attr(
"d", (d) => line(d)
);
},
update => {
update
.transition(t)
.attrTween('d', function(d) {
var previous = d3.select(this).attr('d');
var current = line(d);
return d3.interpolatePath(previous, current);
});
},
exit => exit.remove()
);
// Update Axes.
this.yAxis.tickValues([0, 7000]);
if (data.length > 12) {
this.xAxis.tickValues(
data.map((d, i) => {
if (i % 3 === 0) return d.key;
else return 0;
})
);
} else {
this.xAxis.tickValues(
data.map(d => {
return d.key;
})
);
}
this.yAxis.tickValues([0, 7000]);
this.xAxisDraw.transition(t).call(this.xAxis.scale(this.xScale));
this.yAxisDraw.transition(t).call(this.yAxis.scale(this.yScale));
}
}
})
<div id="app">
<button @click="swapData">Swap</button>
<div id="my_dataviz" class="flex justify-center"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://d3js.org/d3.v6.js"></script>
<script src="https://unpkg.com/d3-interpolate-path/build/d3-interpolate-path.min.js"></script>
score:1
I'm not directly answering your question yet (sorry!) because this might be a better solution. It's possible to interpolate between lines with a different number of points, which may provide a better experience?
There's a d3-interpolate-path plugin that can handle a different number of points being present in the path, but still create a reasonably smooth animation by inserting placeholder points into the line.
There's a really good explanation of how this works, as well as some examples of it working https://bocoup.com/blog/improving-d3-path-animation .
Answer
If you really do want to animate from zero each time, then you need to check the keys match the last set of keys.
Create a d3 local store
const keyStore = d3.local();
Get the keys from last render (element wants to be your line)
const oldKeys = keyStore.get(element);
Determine if the keys match:
const newKeys = data.map(d => d.key); // arraysEqual - https://stackoverflow.com/a/16436975/21061 const keysMatch = arraysEqual(oldKeys, newKeys);
Change your interpolation on
keysMatch
(see previous ternary):update.transition(t) .attrTween('d', function(d) { var previous = keysMatch ? d3.select(this).attr('d') : 0; var current = line(d); return d3.interpolatePath(previous, current); });
Source: stackoverflow.com
Related Query
- D3.js line chart using the wrong keys when using general update pattern
- D3.js Line chart not updating properly when applying the general update pattern
- Using General update pattern in line graph
- Using D3, I am trying to add and remove lines from a multi line chart when the legend is clicked
- D3.js - How to add the general update pattern to a stacked bar chart
- Line not visible in line chart when having same value in the domain() - using D3
- D3 V4 Transition on entering elements using General Update Pattern with merge
- How to plot animated line chart using d3 while the data table is required to be updated every 1 second?
- Update donut chart using the c3 library (JQuery)
- D3 V4 - Using merge with general update pattern
- Adapting diverging stacked bar chart to use general update pattern
- NVD3 Line Chart doesn't display when data passed using ajax - data.map is not a function
- Why the line in this D3 chart isn't properly displayed when updated?
- d3js multi-line chart is being drawn off the plot area - is update pattern to the issue?
- Maintain color mapping for Stacked/Grouped Multi-Bar Chart using NVD3 when the series count fluxuates
- d3 chart update automatically when data change by using vue
- Are D3 value keys matched in the same way that default index numbers are when using enter()?
- Drawing a line using d3 is not visible when all data items are the same
- D3 js - when using brush line grows beyond chart x-axis
- Update the colored area of a line chart
- Aw snap in Google Chrome when interactive line chart using nvd3 + angular library
- How to simultaneously update the color of elements in a SVG when I mouseover elements of a second SVG using D3
- using d3.js to store a svg line chart as an image on the client side
- update line when dragging point in grouped multiline chart
- D3 reusable chart tootip follows wrong chart when update
- d3.js can not update the chart - using json data
- How do I update the y axis of the chart using D3 js
- D3 General update pattern transition not working on pie chart
- Wrong item "clicked" when using the "click" event on a SVG circle using D3
- Stacked Bar Chart with general update pattern d3.js
More Query from same tag
- R netWorkD3 Sankey - add percentage by js doesn't work
- D3: Transition Treemap and retain original group sizing
- d3js column bar chart height error
- Creat table with d3.js and json data containing data and links
- D3 tree: lines instead of diagonal projection
- D3 Format 's' in version 4
- Nesting data in D3.js
- How to switch from d3.svg.line to a choice that allows different coloured segments?
- bar height issue in bar chart of d3 js
- Ragged list or data frame to JSON
- Large Force Directed Graph Dataset in D3/Angular
- How to zoom around an svg
- How to set date range when using d3.time.scale()
- Difference between d3.js and d3.v3.js
- Circles on top of bar chart d3.js
- D3 v4: spacing between and binding of elements of arbitrarily complex array with nested g tags
- d3.js v5 : Unable to get data points to show on map of US
- TypeError: Cannot read property 'select' of undefined in d3
- d3 zoom and pan upgrade to version 4
- Unable to get node datum on mouseover in D3 v6
- How to reach a URL based on a node click?
- Invalid value for <circle> attribute cx=“NaN” using D3.js
- Making different size of `radius` to create `arc` not working
- D3 js legend for a dynamic dataset
- Adding axis to the dynamic line chart in d3
- How to place a label in an area outside of an SVG path?
- D3js single bar graph the updates via a slider
- Modifying data in non-DOM elements in D3?
- ForeignObject text not appearing
- Click event causing drag event as well in force layout graph