Javascript Vue D3JS创建交互式甜甜圈图表
我有一个项目,我需要重新创建一个特定的甜甜圈图表。此图表由蓝色部分组成,在悬停时需要弹出并更改颜色。我在网上找到了一些关于如何重新创建此动画的文档: 问题是,我曾尝试在我的Vue组件中实现此解决方案,但当我在图表中徘徊时,它会不断抛出错误 当我将鼠标悬停在图表中时,我得到以下错误:Javascript Vue D3JS创建交互式甜甜圈图表,javascript,vue.js,animation,d3.js,charts,Javascript,Vue.js,Animation,D3.js,Charts,我有一个项目,我需要重新创建一个特定的甜甜圈图表。此图表由蓝色部分组成,在悬停时需要弹出并更改颜色。我在网上找到了一些关于如何重新创建此动画的文档: 问题是,我曾尝试在我的Vue组件中实现此解决方案,但当我在图表中徘徊时,它会不断抛出错误 当我将鼠标悬停在图表中时,我得到以下错误:无法读取下一行未定义的属性'startAngle':path.transition().attr(“d”,d3.arc().innerRadius(半径*0.7)。outerRadius(半径*1.08))。我似乎无法
无法读取下一行未定义的属性'startAngle'
:path.transition().attr(“d”,d3.arc().innerRadius(半径*0.7)。outerRadius(半径*1.08))代码>。我似乎无法理解为什么会发生此错误,因为我没有使用startAngle
函数
当我将鼠标从图表中移出时,如果(!thisPath.classed(“clicked”){
,则在行中,我还得到以下错误node.getAttribute不是函数
这是我第一次使用D3.JS,我仍在努力了解这个库的很多内容。我喜欢你能用它创建多少不同的图形
这是我的组件的完整代码:
<template>
<div class="p-3 flex flex-col items-center h-full">
<div class="w-full flex-1 h-full">
<div ref="chart" class="flex justify-center h-full"></div>
</div>
</div>
</template>
<script>
import * as d3 from "d3";
export default {
name: "DoughnutChartItem",
props: {
data: {
type: Array,
required: true
},
height: {
type: Number,
required: true
},
width: {
type: Number,
required: true
}
},
mounted() {
// // set the dimensions and margins of the graph
// var width = 450;
// var height = 450;
var margin = 50;
// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = Math.min(this.width, this.height) / 2 - margin;
// append the svg object to the div called 'chart'
var svg = d3
.select(this.$refs["chart"])
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("viewBox", `0 0 ${this.width} ${this.height}`)
.append("g")
.attr(
"transform",
"translate(" + this.width / 2 + "," + this.height / 2 + ")"
);
// // Create dummy data
// var data = { a: 9, b: 20, c: 30, d: 8, e: 12 };
// set the color scale
var color = d3
.scaleOrdinal()
.domain(Object.keys(this.data))
.range(["#206BF3"]);
// Compute the position of each group on the pie:
var pie = d3.pie().value(function(d) {
return d[1];
});
var data_ready = pie(Object.entries(this.data));
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll("whatever")
.data(data_ready)
.enter()
.append("path")
.attr(
"d",
d3
.arc()
.innerRadius(100) // This is the size of the donut hole
.outerRadius(radius)
)
.attr("fill", function(d) {
return color(d.data[0]);
})
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("opacity", 0.7);
svg.on("mouseover", () => {
this.pathAnim(radius, d3.select(this), 1);
});
svg.on("mouseout", () => {
var thisPath = d3.select(this);
if (!thisPath.classed("clicked")) {
this.pathAnim(radius, thisPath, 0);
}
});
},
methods: {
pathAnim(radius, path, dir) {
switch (dir) {
case 0:
path
.transition()
.duration(500)
.ease("bounce")
.attr(
"d",
d3
.arc()
.innerRadius(radius * 0.7)
.outerRadius(radius)
);
break;
case 1:
console.log(
path.transition().attr(
"d",
d3
.arc()
.innerRadius(radius * 0.7)
.outerRadius(radius * 1.08)
)
);
// path.transition().attr(
// "d",
// d3
// .arc()
// .innerRadius(radius * 0.7)
// .outerRadius(radius * 1.08)
// );
break;
}
}
}
};
</script>
</script>
从“d3”导入*作为d3;
导出默认值{
名称:“DoughnutChartItem”,
道具:{
数据:{
类型:数组,
必填项:true
},
高度:{
类型:数字,
必填项:true
},
宽度:{
类型:数字,
必填项:true
}
},
安装的(){
////设置图形的尺寸和边距
//var宽度=450;
//var高度=450;
var保证金=50;
//pieplot的半径是宽度的一半或高度的一半(最小的一个)。我减去一点边距。
var radius=数学最小值(this.width,this.height)/2-边距;
//将svg对象附加到名为“chart”的div中
var svg=d3
.选择(此.$refs[“图表”])
.append(“svg”)
.attr(“宽度”、“100%”)
.attr(“高度”、“100%”)
.attr(“viewBox”`0${this.width}${this.height}`)
.附加(“g”)
艾特先生(
“转化”,
翻译(“+this.width/2+”,“+this.height/2+”)
);
////创建虚拟数据
//变量数据={a:9,b:20,c:30,d:8,e:12};
//设置颜色比例
var color=d3
.scaleOrdinal()
.domain(Object.keys(this.data))
.范围([“#206BF3]”);
//计算每个组在饼图上的位置:
var pie=d3.pie().value(函数(d){
返回d[1];
});
var data_ready=pie(Object.entries(this.data));
//构建饼图:基本上,饼图的每个部分都是我们使用arc函数构建的路径。
svg
.selectAll(“任意”)
.数据(数据准备就绪)
.输入()
.append(“路径”)
艾特先生(
“d”,
d3
.arc()
.innerRadius(100)//这是甜甜圈孔的大小
.外部(半径)
)
.attr(“填充”,功能(d){
返回颜色(d.data[0]);
})
.attr(“笔划”、“黑色”)
.style(“笔划宽度”、“2px”)
.样式(“不透明度”,0.7);
svg.on(“鼠标悬停”,()=>{
this.pathAnim(半径,d3.select(this),1);
});
svg.on(“mouseout”,()=>{
var thisPath=d3。选择(此);
如果(!thisPath.classed(“单击”)){
this.pathAnim(半径,thisPath,0);
}
});
},
方法:{
路径动画(半径、路径、方向){
交换机(dir){
案例0:
路径
.transition()
.持续时间(500)
.轻松(“反弹”)
艾特先生(
“d”,
d3
.arc()
.内半径(半径*0.7)
.外部(半径)
);
打破
案例1:
console.log(
path.transition().attr(
“d”,
d3
.arc()
.内半径(半径*0.7)
.外表面(半径*1.08)
)
);
//path.transition().attr(
//“d”,
//d3
//.arc()
//.内半径(半径*0.7)
//.外表面(半径*1.08)
// );
打破
}
}
}
};
三个问题
首先,将“mouseover”(和“mouseout”)事件分配给整个SVG
。您希望将其分配给每个路径
其次,您在这方面遇到了问题
svg.on("mouseover", () => {
this.pathAnim(radius, d3.select(this), 1);
});
这里的this
变量是VUE组件(因此this.pathAnim是可调用的)。但是当您执行d3.select(this)
时,您选择的是整个VUE组件,而不是要设置动画的path
元素(请参见第1点)
您可以将其改写为:
var paths = svg //<-- hold reference to selection of paths
.selectAll('whatever')
.data(data_ready)
.enter()
.append('path')
.attr(
'd',
d3
.arc()
.innerRadius(100)
.outerRadius(radius)
)
.attr('fill', function (d) {
return color(d.data[0]);
})
.attr('stroke', 'black')
.style('stroke-width', '2px')
.style('opacity', 0.7);
paths.on('mouseover', (d,i,j) => { //<-- operate on paths
this.pathAnim(radius, d3.select(j[i]), 1); //<-- use alternate way to select path without needing this variable
});
运行代码:
新Vue({
el:'一',
数据:{
类型:数组,
要求:正确,
},
安装的(){
//设置图形的尺寸和边距
var宽度=450;
var高度=450;
var保证金=40;
//pieplot的半径是宽度的一半或高度的一半(最小的一个)。我减去一点边距。
变量半径=数学最小值(宽度、高度)/2-裕度;
//将svg对象附加到名为“my_dataviz”的div中
var svg=d3
.选择(“#我的数据VIZ”)
.append('svg')
.attr('width',width)
.attr('height',height)
.append('g')
艾特先生(
“转换”,
'平移('+width/2+','+height/2+')'
);
//创建虚拟数据
变量数据={a:9,b:20,c:30,d:8,e:12};
//设置颜色比例
var color=d3
.scaleOrdinal()
.domain(对象.键(数据))
.范围(['98abc5'、'8a89a6'、'7b6888'、'6b486b'、'
path
.transition()
.duration(500)
.ease(d3.easeBounce) //<-- ease function
.attr(
'd',
d3
.arc()
.innerRadius(radius * 0.7)
.outerRadius(radius)
);