D3更新条形图VUE.js
嘿,我对Vue中的d3图表有问题。除了更新数据外,其他一切都很好 我使用道具传递数据,当数据改变我的图表而不是更新时,它会在当前图表的正下方创建另一个图表 我想做的是保持当前图形,并在数据发生变化时仅更新条形图D3更新条形图VUE.js,vue.js,d3.js,methods,bar-chart,data-visualization,Vue.js,D3.js,Methods,Bar Chart,Data Visualization,嘿,我对Vue中的d3图表有问题。除了更新数据外,其他一切都很好 我使用道具传递数据,当数据改变我的图表而不是更新时,它会在当前图表的正下方创建另一个图表 我想做的是保持当前图形,并在数据发生变化时仅更新条形图 <script> export default { props: { arr: { type: Array }, colors: { type: Array } }, w
<script>
export default {
props: {
arr: {
type: Array
},
colors: {
type: Array
}
},
watch: {
arr: {
immediate: true,
handler(val) {
setTimeout(() => {
this.barCharts(val)
}, 100);
}
}
},
methods: {
barCharts(data) {
let margin = { top: 40, right: 20, bottom: 50, left: 60 };
let width = 650
let height = 240;
let color = d3.scaleOrdinal().range(this.colors);
let t = d3.transition().duration(750);
let g = d3
.select("#barChart")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", "100%")
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
let xAxisGroup = g
.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0, ${height})`);
let yAxisGroup = g.append("g").attr("class", "y axis");
// X Scale
let x = d3
.scaleBand()
.range([0, width])
.padding(0.2);
// Y Scale
let y = d3.scaleLinear().range([height, 0]);
// axis
x.domain(data.map(d => d.name));
y.domain([
0,
d3.max(data, function(d) {
return d.value;
})
]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.attr("class", "axis")
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-30)");
g.append("g")
.call(d3.axisLeft(y))
.attr("class", "axis");
// draw bars
let rects = g.selectAll("rect").data(data);
rects
.transition(t)
.attr("y", function(d) {
return y(d.value);
})
.attr("x", function(d) {
return x(d.name);
})
.attr("height", function(d) {
return height - y(d.value);
})
.attr("width", x.bandwidth())
.attr("fill", (d, i) => color(i));
rects
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.name);
})
.attr("width", x.bandwidth())
.attr("fill", (d, i) => color(i))
.attr("y", y(0))
.attr("height", 0)
.transition(t)
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
}
}
};
}
</script>
导出默认值{
道具:{
arr:{
类型:数组
},
颜色:{
类型:数组
}
},
观察:{
arr:{
立即:是的,
处理程序(val){
设置超时(()=>{
这是条形图(val)
}, 100);
}
}
},
方法:{
条形图(数据){
让边距={顶部:40,右侧:20,底部:50,左侧:60};
宽度=650
设高度=240;
设color=d3.scaleOrdinal().range(this.colors);
设t=d3.transition().duration(750);
设g=d3
.选择(“条形图”)
.append(“svg”)
.attr(“宽度”,宽度+边距。左侧+边距。右侧)
.attr(“高度”、“100%”)
.附加(“g”)
.attr(“transform”,`translate(${margin.left},${margin.top})`);
设xAxisGroup=g
.附加(“g”)
.attr(“类”、“x轴”)
.attr(“transform”,`translate(0,${height})`);
设yAxisGroup=g.append(“g”).attr(“class”,“y轴”);
//X刻度
设x=d3
.scaleBand()
.范围([0,宽度])
.填充(0.2);
//Y刻度
设y=d3.scaleLinear().range([height,0]);
//轴心
x、 域(data.map(d=>d.name));
y、 领域([
0,
d3.最大值(数据、函数(d){
返回d值;
})
]);
g、 附加(“g”)
.attr(“变换”、“平移(0)”、“高度+”)
.call(d3.axisBottom(x))
.attr(“类”、“轴”)
.selectAll(“文本”)
.style(“文本锚定”、“结束”)
.attr(“dx”,“-.8em”)
.attr(“dy”,“.15em”)
.attr(“变换”、“旋转(-30)”);
g、 附加(“g”)
.呼叫(d3.左(y))
.attr(“类”、“轴”);
//拉杆
设rects=g.selectAll(“rect”).data(数据);
直肠
.过渡(t)
.attr(“y”,函数(d){
返回y(d值);
})
.attr(“x”,函数(d){
返回x(d.name);
})
.attr(“高度”,功能(d){
返回高度-y(d值);
})
.attr(“宽度”,x.带宽())
.attr(“填充”(d,i)=>颜色(i));
直肠
.输入()
.append(“rect”)
.attr(“x”,函数(d){
返回x(d.name);
})
.attr(“宽度”,x.带宽())
.attr(“填充”(d,i)=>颜色(i))
.attr(“y”,y(0))
.attr(“高度”,0)
.过渡(t)
.attr(“y”,函数(d){
返回y(d值);
})
.attr(“高度”,功能(d){
返回高度-y(d值);
});
}
}
};
}
我是否可以实现另一个函数,例如update,在该函数内部调用,以仅更新bard
提前谢谢 生成新的条形图是因为在
barCharts
方法中将新的SVG元素添加到目标中。每次调用它时,它都会附加一个带有更新数据的新SVG,从而导致出现重复图表的问题
由于Vue有自己的呈现和更新DOM元素的方法,所以不应该使用d3来实现这一点,因为这会影响Vue的反应性和清理(这意味着可能会导致内存泄漏或元素未被删除)
我的建议是在vue
中定义
和其他元素,并使用v-bind指令将条形图值传递给vue呈现的SVG元素。例如,您可以使用d3 scale
中的方法在组件中创建x
、y
和barColor
方法/计算属性。然后可以将每个条的结果绑定到模板中的正确属性,例如
或类似属性
让我知道这是否足以让你上路,否则我今天晚些时候可以给你写一个完整的例子。我还推荐这些结合vue和d3的视频,它们对我很有洞察力:
- 生成一个新的条形图是因为在
条形图方法中将一个新的SVG元素附加到目标中。每次调用它时,它都会附加一个带有更新数据的新SVG,从而导致出现重复图表的问题
由于Vue有自己的呈现和更新DOM元素的方法,所以不应该使用d3来实现这一点,因为这会影响Vue的反应性和清理(这意味着可能会导致内存泄漏或元素未被删除)
我的建议是在vue
中定义
和其他元素,并使用v-bind指令将条形图值传递给vue呈现的SVG元素。例如,您可以使用d3 scale
中的方法在组件中创建x
、y
和barColor
方法/计算属性。然后可以将每个条的结果绑定到模板中的正确属性,例如
或类似属性
让我知道这是否足以让你上路,否则我今天晚些时候可以给你写一个完整的例子。我还推荐这些结合vue和d3的视频,它们对我很有洞察力:
根据你的建议,我已经改变了这一点,我可以承认这非常棒!!它工作得很好,只是有一个过渡问题。我不太确定我应该把它放在哪里。。。在输入时,我希望将y从0转换为条形图的高度,然后在数据更改时进行另一次转换
再次感谢您的帮助
<template>
<svg class="barChart" :width="width" height="340">
<g>
<!-- axis bottom -->
<g class="x-axis" fill="none" :transform="`translate(0, ${height})`">
<path stroke="currentColor" :d="`M0.5,6V0.5H${width}.5V6`"></path>
<g
class="tick"
opacity="1"
font-size="10"
font-family="sans-serif"
text-anchor="middle"
v-for="(bar, index) in bars"
:key="index"
:transform="`translate(${bar.x + bar.width / 2}, 0)`"
>
<line stroke="currentColor" y2="6"></line>
<text fill="currentColor" y="9" dy="0.71em">{{ bar.xLabel }}</text>
</g>
</g>
<!-- y-axis -->
<g class="y-axis" fill="none" :transform="`translate(0, 0)`">
<path
class="domain"
stroke="currentColor"
:d="`M0.5,${height}.5H0.5V0.5H-6`"
></path>
<g
class="tick"
opacity="1"
font-size="10"
font-family="sans-serif"
text-anchor="end"
v-for="(tick, index) in yTicks"
:key="index"
:transform="`translate(0, ${y(tick) + 0.5})`"
>
<line stroke="currentColor" x2="-6"></line>
<text fill="currentColor" x="-9" dy="0.32em">{{ tick }}</text>
</g>
</g>
<!-- bars -->
<g class="bars" fill="none">
<rect
v-for="(bar, index) in bars"
:fill="color(bar.xLabel)"
:key="index"
:height="bar.height"
:width="bar.width"
:x="bar.x"
:y="bar.y"
></rect>
</g>
</g>
</svg>
</template>
<script>
import * as d3 from "d3";
import { scaleBand, scaleLinear } from 'd3-scale'
export default {
props: {
dataset: {
type: Array
},
colors: {
type: Array
}
},
data() {
return {
height: 240,
width: 650
}
},
computed: {
color() {
return d3.scaleOrdinal().range(this.colors);
},
yTicks() {
return this.y.ticks(5);
},
x() {
return scaleBand()
.range([0, this.width])
.padding(0.3)
.domain(this.dataset.map(e => e[0]));
},
y() {
let values = this.dataset.map(e => e[1]);
return scaleLinear()
.range([this.height, 0])
.domain([0, Math.max(...values)]);
},
bars() {
let bars = this.dataset.map(d => {
return {
xLabel: d[0],
x: this.x(d[0]),
y: this.y(d[1]),
width: this.x.bandwidth(),
height: this.height - this.y(d[1])
};
});
return bars;
}
},
}
</script>
{