基于Vue+D3.js的仪表图组件
实现的效果如下图所示
1.定义data数据,实现组件的可定制化
-
panel组件能定制的参数包括以下几项
config: { size: 200, //panel的大小 label: '采油量', //panel的名称 min: 0, //量程的最小值 max: 100, //量程的最大值 majorTicks: 6, //主刻度的个数 minorTicks: 4, //次刻度的个数 greenColor: '#109618', //区域1颜色 yellowColor:'#FF9900', //区域2颜色 redColor:'#DC3912', //区域3颜色 duration: 500, //转动一次动画需要的时间 value: 10} //panel显示的值
2.处理数据函数setConfig()
-
处理config数据
return { value: config.value, size: config.size, cx: config.size / 2, //圆心的x坐标 cy: config.size / 2, //圆心的y坐标 label: config.label, max: config.max, min: config.min, range: config.max - config.min, //量程 radius: config.size * 0.97 / 2, //半径(稍许留白) minorTicks: config.minorTicks, majorTicks: config.majorTicks, duration: config.duration, greenColor: config.greenColor, yellowColor : config.yellowColor, greenZones: {from: config.min, to: config.min + (config.max - config.min) * 0.75}, //区域1范围 yellowZones: { from: config.min + (config.max - config.min)*0.75, to: config.min +(config.max - config.min)*0.9 } , redColor:config.redColor, //区域2范围 redZones: { from: config.min +(config.max - config.min)*0.9, to: config.max } //区域3范围 }
3.render函数
-
3.1获取数据
let {size, cx, cy, radius, range, min, max, label, minorTicks, majorTicks, duration, value,greenZones,greenColor,yellowZones,yellowColor,redZones,redColor} = this.setConfig(config);
-
3.2添加svg
let self = this; //添加上下文 let panel = d3.select("#panel").append("svg:svg") .attr("class", "gauge") //添加类名gauge .attr("width", size) //设置svg宽度 .attr("height", size); //设置svg高度
-
3.3画外圆
panel.append("svg:circle") //添加circle,圆 .attr("cx", cx) //设置circle的坐标 .attr("cy", cy) .attr("r", radius) //设置circle的半径 .style("fill", "#ccc") //设置circle的填充颜色 .style("stroke", "#000") //设置circle的轮廓颜色 .style("stroke-width", "0.5px");//设置circle的轮廓宽度
-
3.4画内圆
panel.append("svg:circle") .attr("cx", cx) .attr("cy", cy) .attr("r", 0.9 * radius) //半径不一样 .style("fill", "#fff") .style("stroke", "#e0e0e0") .style("stroke-width", "2px");
-
3.5画颜色区域
黄色和绿色同下 panel.append("svg:path") //添加路径 .style("fill", greenColor) .attr("d", d3.svg.arc() //添加圆弧 .startAngle(this.valueToRadians(greenZones.from, config)) //开始弧度 .endAngle(this.valueToRadians(greenZones.to, config)) //结束弧度 .innerRadius(0.65 * radius) //内圈半径 .outerRadius(0.85 * radius)) //外圈半径 .attr("transform", function () {//移动+旋转 return "translate(" + cx + ", " + cy + ") rotate(270)" });
-
3.6设置label
let fontSize = Math.round(size / 9); //字体大小 panel.append("svg:text") //添加文本 .attr("x", cx) //文本位置 .attr("y", cy / 2 + fontSize / 2) .attr("dy", fontSize / 2) .attr("text-anchor", "middle")//文字角度 .text(label) //文字内容 .style("font-size", fontSize + "px")//字体大小 .style("fill", "#333") //颜色 .style("stroke-width", "0px");
-
3.7设置大小刻度线
let deltaSize = Math.round(size / 16); // let majorDelta = range / (majorTicks - 1);//大刻度之间的距离 { for (let major = min; major <= max; major += majorDelta) { //循环每个大刻度 let minorDelta = majorDelta / minorTicks;//小刻度之间的距离 for (let minor = major + minorDelta; minor < Math.min(major + majorDelta, max); minor += minorDelta) { //循环每个小刻度 let point1 = this.valueToPoint(minor, config, 0.75); let point2 = this.valueToPoint(minor, config, 0.85); //获取小刻度线的(x1,y1)(x2,y2)位置 //添加小刻度线 panel.append("svg:line") .attr("x1", point1.x) //小刻度线的两点坐标 .attr("y1", point1.y) .attr("x2", point2.x) .attr("y2", point2.y) .style("stroke", "#666")//小刻度线颜色 .style("stroke-width", "1px");//小刻度的宽度 } //获取大刻度线的(x1,y1)(x2,y2)位置 let point3 = this.valueToPoint(major, config, 0.7); let point4 = this.valueToPoint(major, config, 0.85); //添加大刻度线 panel.append("svg:line") .attr("x1", point3.x) .attr("y1", point3.y) .attr("x2", point4.x) .attr("y2", point4.y) .style("stroke", "#333") .style("stroke-width", "2px"); //标记最大值和最小值 if (major == min || major == max) { let point = this.valueToPoint(major, config, 0.63); panel.append("svg:text") .attr("x", point.x) .attr("y", point.y) .attr("dy", fontSize / 3) .attr("text-anchor", major == min ? "state" : "end") .text(major) .style("font-size", deltaSize + "px") .style("fill", "#333") .style("stroke-width", "0px") } } }
3.8设置指针
-
pointerContainer = panel.append("svg:g").attr("class", "pointerContainer");
//中间值 let midValue = (min + max) / 2; //指针的点轨迹 let pointerPath = this.buildPointerPath(midValue, config); //曲线生成器 let pointerLine = d3.svg.line() .x(function (d) { return d.x }) .y(function (d) { return d.y }) .interpolate("basis"); //贝斯曲线 //画指针 pointerContainer.selectAll("path") .data([pointerPath]) .enter() .append("svg:path") .attr("d", pointerLine) .style("fill", "#dc3912") //填充的颜色 .style("stroke", "#c63310") //轮廓的颜色 .style("fill-opacity", 0.7); //填充的透明度
-
3.9画指针中心圆
pointerContainer.append("svg:circle") .attr("cx", cx) .attr("cy", cy) .attr("r", 0.12 * radius) .style("fill", "#4684EE") .style("stroke", "#666") .style("opacity", 1);
-
3.10设置value
let valueSize = Math.round(size / 10); pointerContainer.append("svg:text") .attr("x", cx) .attr("y", size - (cy / 4) - valueSize) .text(midValue) .attr("dy", valueSize / 2) .attr("text-anchor", "middle") .style("font-size", valueSize + "px") .style("fill", "#000") .style("stroke-width", "0px"); this.redraw(value,duration,min,max,range,config,cy,cx)//指针渲染函数
4.设置指针渲染函数
-
渲染指针
redraw(value,duration,min,max,range,config,cy,cx){ let self =this; let panel = d3.select("#panel").select(".gauge"); //指针旋转 let pointContainer = panel.select(".pointerContainer"); //设置value值 pointContainer.selectAll('text').text(value); let pointer = pointContainer.selectAll("path"); //指针移动 pointer.transition() .duration(duration)//持续时间 .attrTween("transform", function () { let pointerValue = value; //判断超出量程的问题 if (value > max) pointerValue = max + 0.02 * range; else if (value < min) pointerValue = min - 0.02 * range; //目标旋转角度:将数值转化为角度,并减去一个直角, let targetRotation = (self.valueToDegrees(pointerValue, config) - 90); //现在的角度 let currentRotation = self._currentRotation || targetRotation; self._currentRotation = targetRotation; return function (step) { let rotation = currentRotation + (targetRotation - currentRotation) * step; return " rotate(" + rotation + ", " + cx + ", " + cy + ")"; //定义旋转 } }) }
5.公共函数
-
转化为弧度
valueToRadians(value, config) { return this.valueToDegrees(value, config) * Math.PI / 180 }
-
转化为角度
valueToDegrees(value, config) { let {range, min} = this.setConfig(config); return value / range * 270 - (min / range * 270 + 45) }
-
生成指针点数据
valueToPoint(value, config, factor){ let {cx, cy, radius} = this.setConfig(config); return { x: cx - radius * factor * Math.cos(this.valueToRadians(value, config)), y: cy - radius * factor * Math.sin(this.valueToRadians(value, config)) } }
-
生成指针
buildPointerPath(value, config){ let {range} = this.setConfig(config); let delta = range / 13, head = this.valueToPoint(value, config, 0.85), head1 = this.valueToPoint(value - delta, config, 0.12), head2 = this.valueToPoint(value + delta, config, 0.12), tailValue = value - (range * (1 / (270 / 360)) / 2), tail = this.valueToPoint(tailValue, config, 0.28), tail1 = this.valueToPoint(tailValue - delta, config, 0.12), tail2 = this.valueToPoint(tailValue + delta, config, 0.12); return [head, head1, tail2, tail, tail1, head2, head]; }