I'm trying to make a stacked bar chart / waterfall chart and I am having a lot of difficulties setting it up. I'm using echarts with vue, but the framework doesn't matter that much. Any help regarding setting this up in echarts would be extremely helpful!
It needs to look like this:
The following code is what I ended up with, which just creates randomly generated data as a mock.
import { defineComponent, ref, onMounted } from 'vue';
import { use } from 'echarts/core';
import VChart from 'vue-echarts';
import { BarChart } from 'echarts/charts';
import { GridComponent, TooltipComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
export default defineComponent({
components: {
VChart,
},
setup() {
const timeIntervals = ref([]);
const updateTimeIntervals = () => {
const intervals = [];
let startTime = new Date();
startTime.setMinutes(0, 0, 0);
for (let i = 0; i < 8; i++) { // 8 hours with 1-hour intervals
intervals.push(
startTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
);
startTime.setHours(startTime.getHours() + 1);
}
timeIntervals.value = intervals;
};
onMounted(updateTimeIntervals);
const chartOptions = ref({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
},
xAxis: {
type: 'category',
data: timeIntervals,
axisLabel: { rotate: 45 },
},
yAxis: {
type: 'value',
min: -100,
max: 100,
},
series: [
{
name: 'Category A',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#5470c6',
},
{
name: 'Category A (Duplicate)',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#91cc75',
},
{
name: 'Category B',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#fac858',
},
{
name: 'Category C',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#ee6666',
},
{
name: 'Category D',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#73c0de',
},
],
});
return { chartOptions, timeIntervals };
},
});
The code above generates something like this which has multiple where htings where items overlap, not centered in 0 (this is because of mock data) and it just generally isn't what I want. But this is the furthest I have come:
I'm trying to make a stacked bar chart / waterfall chart and I am having a lot of difficulties setting it up. I'm using echarts with vue, but the framework doesn't matter that much. Any help regarding setting this up in echarts would be extremely helpful!
It needs to look like this:
The following code is what I ended up with, which just creates randomly generated data as a mock.
import { defineComponent, ref, onMounted } from 'vue';
import { use } from 'echarts/core';
import VChart from 'vue-echarts';
import { BarChart } from 'echarts/charts';
import { GridComponent, TooltipComponent } from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
use([BarChart, GridComponent, TooltipComponent, CanvasRenderer]);
export default defineComponent({
components: {
VChart,
},
setup() {
const timeIntervals = ref([]);
const updateTimeIntervals = () => {
const intervals = [];
let startTime = new Date();
startTime.setMinutes(0, 0, 0);
for (let i = 0; i < 8; i++) { // 8 hours with 1-hour intervals
intervals.push(
startTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
);
startTime.setHours(startTime.getHours() + 1);
}
timeIntervals.value = intervals;
};
onMounted(updateTimeIntervals);
const chartOptions = ref({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
},
xAxis: {
type: 'category',
data: timeIntervals,
axisLabel: { rotate: 45 },
},
yAxis: {
type: 'value',
min: -100,
max: 100,
},
series: [
{
name: 'Category A',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#5470c6',
},
{
name: 'Category A (Duplicate)',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#91cc75',
},
{
name: 'Category B',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#fac858',
},
{
name: 'Category C',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#ee6666',
},
{
name: 'Category D',
type: 'bar',
stack: 'total',
data: Array.from({ length: 8 }, () => Math.floor(Math.random() * 200 - 100)),
color: '#73c0de',
},
],
});
return { chartOptions, timeIntervals };
},
});
The code above generates something like this which has multiple where htings where items overlap, not centered in 0 (this is because of mock data) and it just generally isn't what I want. But this is the furthest I have come:
Share Improve this question asked Mar 14 at 9:38 dunctdunct 811 silver badge8 bronze badges1 Answer
Reset to default 0You can achieve the given look by using two different stacks, one for the positive and one for the negative part. The resulting bars can be overlapped with barGap: "-100%"
. Note, that the blue bar is cut into two pieces, which might complicate things when using legend or tooltip.
Example:
const data = [{
time: 1,
blue: 48,
green_pos: 0,
green_neg: 0,
yellow_pos: 0,
yellow_neg: 0
}, ... ];
option = {
xAxis: {
type: 'category',
},
yAxis: { type: 'value' },
series: [
{
name: 'blue_pos',
type: 'bar',
stack: 'pos',
data: data.map(x => x.blue / 2),
color: '#5470c6'
},
{
name: 'blue_neg',
type: 'bar',
stack: 'neg',
data: data.map(x => -x.blue / 2),
color: '#5470c6'
},
{
name: 'green_pos',
type: 'bar',
stack: 'pos',
data: data.map(x => x.green_pos),
color: '#91cc75'
},
{
name: 'green_neg',
type: 'bar',
stack: 'neg',
data: data.map(x => -x.green_neg),
color: '#91cc75'
},
{
name: 'yellow_pos',
type: 'bar',
stack: 'pos',
data: data.map(x => x.yellow_pos),
color: '#fac858'
},
{
name: 'yellow_neg',
type: 'bar',
stack: 'neg',
data: data.map(x => -x.yellow_neg),
color: '#fac858',
barGap: "-100%"
},
]
};