d3.js 化工管柱状图 [英] d3.js chemical tube bar chart
问题描述
我对创建各种液体条形图很感兴趣.如图所示,指针/虚线标记拉到一边.
4 月 22 日 - 处理动态数据的最新代码
最新的jsfiddlehttp://jsfiddle.net/NYEaX/1843/
var $this = $("#checmicalbars");变量数据 = [{"label": "渲染",价值":90,"startcolor": "#c3da54",结束颜色":#c1e500"},{"label": "角色设计",价值":95,"startcolor": "#e94adc","endcolor": "#aae3dd"},{"label": "草图",价值":80,"startcolor": "#c3da54",结束颜色":#fa5283"},{"label": "故事板",价值":90,"startcolor": "#e94adc",结束颜色":#f83b03"},{"label": "绘图",价值":82,"startcolor": "#c3da54",结束颜色":#f88504"},{"label": "绘画",价值":90,"startcolor": "#e94adc",结束颜色":#f7d200"}];无功h = 450;无功 w = 400;var barHeight = 150;var barWidth = 180;变量选项 = {最小限制:0,最大限制:100}//设置比例var x = d3.scale.ordinal().rangeRoundBands([0, barWidth], .1);var y = d3.scale.linear().range([barHeight, 0]);var xAxis = d3.svg.axis().scale(this.x).orient("底部");var yAxis = d3.svg.axis().scale(this.y).orient("左");//设置比例//图表容器var progresschart = d3.select($this[0]).append("svg").attr("宽度", w).attr("高度", h).append("g").attr("transform", "translate(0,5)");var barrectsholder = progresschart.append("g").attr("class", "barrectsholder").attr("transform", "translate(15,0)");var labelholder = progresschart.append("g").attr("class", "labelsholder").attr("transform", "translate(10,"+(barHeight+ 20)+")");var lineholder = progresschart.append("g").attr("class", "lineholder").attr("transform", "translate(25,"+(barHeight+ 15)+")");//图表容器y.domain([options["minlimit"], options["maxlimit"]]);/*无功量规 = barrectsholder.append("g").attr("宽度", config.w).attr("高度", config.h).append("g");液体条(仪表,配置);*/var bar = barrectsholder.selectAll("svg").数据(数据);栏输入().append("svg").attr("class", function(d, i) {返回 "bar"+i;}).attr("宽度", 20).attr("x", function(d, i) {返回 30 * i;}).attr(y",函数(d){返回 y(d.value);}).attr(高度",函数(d){返回 barHeight - y(d.value);})$.each(data, function( index, value ) {//警报(索引+:"+值);变量配置 = {w: 20,h:条形高度,价值:价值.价值,振幅: 0.02,//波高占波圈半径的百分比.countPerWidth: 1,//波圈每宽度的全波数.riseTime: 1000,//波浪从 0 上升到最终高度的时间量(以毫秒为单位).animateTime: 1000,//全波进入波圈的时间量,以毫秒为单位.rise: true,//控制波浪是从 0 升到它的全高,还是从它的全高开始.颜色过渡:1000,colorBefore: value.startcolor,//填充波之前的颜色.colorAfter: value.endcolor,//填充波形之后的颜色.offset: 0//初始偏移波浪的量.0 = 无偏移.1 = 一个全波的偏移.};var Gauge = barrectsholder.selectAll("svg.bar"+index)液体条(仪表,配置);});/*//__ 条var bar = barrectsholder.selectAll("rect").数据(数据);//__ 进入栏输入().append("rect").attr("class", "bar").attr("y", barHeight);//__ 更新酒吧.attr("y", barHeight).attr("高度", 0).style("填充", 函数(d){返回 d.startcolor;}).过渡().duration(2500).style("填充", 函数(d){返回 d.endcolor;}).attr("宽度", 20).attr("x", function(d, i) {返回 30 * i;}).attr(y",函数(d){返回 y(d.value);}).attr(高度",函数(d){返回 barHeight - y(d.value);})//__ 出口bar.exit().过渡().duration(250).attr("y", 0).attr("高度", 0).消除();//__ 条*///__ 标签var labels = labelsholder.selectAll("text").数据(数据);标签.输入().append("文本").attr("class", "barlabels").attr("x", 200).attr("y", function(d, i) {返回 20 * i;}).text(function(d) {返回 d.label;})var lines = lineholder.selectAll("text").数据(数据);行.输入().append("line")//附加一行.style("stroke-dasharray", ("3, 3")).style("stroke", "black")//给线条上色.attr("x1", function(d, i) {返回 barWidth-(30 * (i+1));})//行首尾的x pos.attr("y1", function(d, i) {返回 20 * i;})//行首尾的y位置.attr("x2", function(d, i) {返回条宽;})//第二行的x pos.attr("y2", function(d, i) {返回 20 * i;});//第二行的y posvar lineHeights = 100;行.输入().append("line")//附加一行.style("stroke-dasharray", ("3, 3")).style("stroke", "black")//给线条上色.attr("x1", function(d, i) {返回 30 * i;})//行首尾的x pos.attr("y1", function(d, i) {返回 lineHeights - (20 * i);})//行首尾的y位置.attr("x2", function(d, i) {返回 30 * i;})//第二行的x pos.attr("y2", function(d, i) {返回-15;});//第二行的y pos功能liquidBar(仪表,配置){var fillPercent = Math.max(0, Math.min(100, config.value))/100;var waveHeightScale = d3.scale.linear().range([0, config.amplitude, 0]).domain([0, 50, 100]);var waveHeight = (config.h/2) * waveHeightScale(fillPercent * 100);var waveLength = config.w/config.countPerWidth;var waveClipCount = 1 + config.countPerWidth;var waveClipWidth = waveLength * waveClipCount;//用于构建剪切波区域的数据.var 数据 = [];for (var i = 0; i <= 40 * waveClipCount; i++) {数据推送({x: i/(40 * waveClipCount),y: (i/(40))});}//缩放以控制剪切路径的大小.var waveScaleX = d3.scale.linear().range([0, waveClipWidth]).domain([0, 1]);var waveScaleY = d3.scale.linear().range([0, waveHeight]).domain([0, 1]);//缩放以控制剪切路径的位置.var waveRiseScale = d3.scale.linear()//裁剪区域大小是填充圆的高度+波浪高度,所以我们定位裁剪波浪//这样它会在 0% 时与填充圆完全重叠,并将完全覆盖填充//在 100% 处循环..range([(config.h + waveHeight), (waveHeight)]).domain([0, 1]);var waveAnimateScale = d3.scale.linear().range([0, waveClipWidth - config.w])//将剪辑区域推一整波然后弹回..domain([0, 1]);//将仪表置于父 SVG 中.var GaugeGroup = Gauge.append("g").attr("class", "gaugeGroup").attr("transform", "translate(0,0)");var randomId = Math.floor(Math.random() * 26) + Date.now();//剪切波区域.var clipArea = d3.svg.area().x(函数(d){返回 waveScaleX(d.x);}).y0(函数(d){return waveScaleY(Math.sin(Math.PI * 2 * config.offset * -1 + Math.PI * 2 * (1 - config.countPerWidth) + d.y * 2 * Math.PI));}).y1(函数(d){返回(config.h + waveHeight);});var waveGroup = GaugeGroup.append("defs").append("clipPath").attr("id", "clipWave" + randomId);var wave = waveGroup.append("path").数据(数据).attr("d", 剪辑区域).attr("T", 0);//带有剪切波的内圆.var fillGroup = GaugeGroup.append("g").attr("clip-path", "url(#clipWave" + randomId + ")");fillGroup.append("rect").attr("x", 0).attr("y", 0).attr("宽度", config.w).attr("高度", config.h).style("填充", config.colorBefore).过渡().duration(config.colorTransition).style("fill", config.colorAfter);//使波浪上升.wave 和 waveGroup 是分开的,因此可以独立控制水平和垂直移动.var waveGroupXPosition = config.w - waveClipWidth;如果(配置.上升){waveGroup.attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(0) + ')').过渡().duration(config.riseTime).attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(fillPercent) + ')').each("开始", function() {wave.attr('transform', 'translate(1,0)');});//当waveRise=true 和waveAnimate=false 时,为了使剪辑波形正确定位,此变换是必要的.没有这个,波浪就不会正确定位,但不清楚为什么这实际上是必要的.} 别的 {waveGroup.attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(fillPercent) + ')');}动画波();函数 animateWave() {wave.attr('transform', 'translate(' + waveAnimateScale(wave.attr('T')) + ',0)');wave.transition().duration(config.animateTime * (1 - wave.attr('T'))).ease('线性').attr('transform', 'translate(' + waveAnimateScale(1) + ',0)').attr('T', 1).each('end', function() {wave.attr('T', 0);animateWave(config.animateTime);});}}
I am interested in creating this liquid bar chart of sorts. With the pointers/dotted markers pulling off to the side like shown.
22 April - latest code to work with dynamic data http://jsfiddle.net/NYEaX/1855/
latest bar chart code http://jsfiddle.net/NYEaX/1827/
latest cleaned up water wave code http://jsfiddle.net/Qh9X5/10331/
//I want to strip down this waterwave code http://jsfiddle.net/Qh9X5/10091/
//progress chart http://jsfiddle.net/NYEaX/1740/
latest base code for this bar chart. http://jsfiddle.net/NYEaX/1822/
var $this = $("#checmicalbars");
var data = [{
"label": "Rendering",
"value": 90,
"startcolor": "#c3da54",
"endcolor": "#c1e500"
},
{
"label": "Character Design",
"value": 95,
"startcolor": "#e94adc",
"endcolor": "#aae3dd"
},
{
"label": "Sketching",
"value": 80,
"startcolor": "#c3da54",
"endcolor": "#fa5283"
},
{
"label": "Story Boarding",
"value": 90,
"startcolor": "#e94adc",
"endcolor": "#f83b03"
},
{
"label": "Drawing",
"value": 82,
"startcolor": "#c3da54",
"endcolor": "#f88504"
},
{
"label": "Painting",
"value": 90,
"startcolor": "#e94adc",
"endcolor": "#f7d200"
}];
var h = 150;
var w = 300;
var options = {
minlimit: 0,
maxlimit: 100
}
// setup scales
var x = d3.scale.ordinal()
.rangeRoundBands([0, w], .1);
var y = d3.scale.linear()
.range([h, 0]);
var xAxis = d3.svg.axis()
.scale(this.x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(this.y)
.orient("left");
// setup scales
// chart container
var progresschart = d3.select($this[0]).append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(0,5)");
var barrectsholder = progresschart.append("g")
.attr("class", "chart")
.attr("transform", "translate(15,0)");
// chart container
y.domain([options["minlimit"], options["maxlimit"]]);
//__ bars
var bar = barrectsholder.selectAll("rect")
.data(data);
//__ enter
bar.enter()
.append("rect")
.attr("class", "bar")
.attr("y", h);
//__ update
bar
.attr("y", h)
.attr("height", 0)
.style("fill", function(d){
return d.startcolor;
})
.transition()
.duration(2500)
.style("fill", function(d){
return d.endcolor;
})
.attr("width", 20)
.attr("x", function(d, i) {
return 30 * i;
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return h - y(d.value);
})
//__ exit
bar.exit()
.transition()
.duration(250)
.attr("y", 0)
.attr("height", 0)
.remove();
//__ bars
I've merged the two charts together - but the watercode is translated correctly if its a separate svg - be good to get this code cleaned up/reviewed. Also ensuring the pointers/labels adjust/adapt with more/less data sets.
latest jsfiddle http://jsfiddle.net/NYEaX/1843/
var $this = $("#checmicalbars");
var data = [{
"label": "Rendering",
"value": 90,
"startcolor": "#c3da54",
"endcolor": "#c1e500"
},
{
"label": "Character Design",
"value": 95,
"startcolor": "#e94adc",
"endcolor": "#aae3dd"
},
{
"label": "Sketching",
"value": 80,
"startcolor": "#c3da54",
"endcolor": "#fa5283"
},
{
"label": "Story Boarding",
"value": 90,
"startcolor": "#e94adc",
"endcolor": "#f83b03"
},
{
"label": "Drawing",
"value": 82,
"startcolor": "#c3da54",
"endcolor": "#f88504"
},
{
"label": "Painting",
"value": 90,
"startcolor": "#e94adc",
"endcolor": "#f7d200"
}];
var h = 450;
var w = 400;
var barHeight = 150;
var barWidth = 180;
var options = {
minlimit: 0,
maxlimit: 100
}
// setup scales
var x = d3.scale.ordinal()
.rangeRoundBands([0, barWidth], .1);
var y = d3.scale.linear()
.range([barHeight, 0]);
var xAxis = d3.svg.axis()
.scale(this.x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(this.y)
.orient("left");
// setup scales
// chart container
var progresschart = d3.select($this[0]).append("svg")
.attr("width", w)
.attr("height", h)
.append("g")
.attr("transform", "translate(0,5)");
var barrectsholder = progresschart.append("g")
.attr("class", "barrectsholder")
.attr("transform", "translate(15,0)");
var labelsholder = progresschart.append("g")
.attr("class", "labelsholder")
.attr("transform", "translate(10,"+(barHeight+ 20)+")");
var lineholder = progresschart.append("g")
.attr("class", "lineholder")
.attr("transform", "translate(25,"+(barHeight+ 15)+")");
// chart container
y.domain([options["minlimit"], options["maxlimit"]]);
/*
var gauge = barrectsholder
.append("g")
.attr("width", config.w)
.attr("height", config.h)
.append("g");
liquidBar(gauge, config);
*/
var bar = barrectsholder.selectAll("svg")
.data(data);
bar.enter()
.append("svg")
.attr("class", function(d, i) {
return "bar"+i;
})
.attr("width", 20)
.attr("x", function(d, i) {
return 30 * i;
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return barHeight - y(d.value);
})
$.each(data, function( index, value ) {
//alert( index + ": " + value );
var config = {
w: 20,
h: barHeight,
value: value.value,
amplitude: 0.02, // The wave height as a percentage of the radius of the wave circle.
countPerWidth: 1, // The number of full waves per width of the wave circle.
riseTime: 1000, // The amount of time in milliseconds for the wave to rise from 0 to it's final height.
animateTime: 1000, // The amount of time in milliseconds for a full wave to enter the wave circle.
rise: true, // Control if the wave should rise from 0 to it's full height, or start at it's full height.
colorTransition: 1000,
colorBefore: value.startcolor, // The color before of the fill wave.
colorAfter: value.endcolor, // The color after of the fill wave.
offset: 0 // The amount to initially offset the wave. 0 = no offset. 1 = offset of one full wave.
};
var gauge = barrectsholder.selectAll("svg.bar"+index)
liquidBar(gauge, config);
});
/*
//__ bars
var bar = barrectsholder.selectAll("rect")
.data(data);
//__ enter
bar.enter()
.append("rect")
.attr("class", "bar")
.attr("y", barHeight);
//__ update
bar
.attr("y", barHeight)
.attr("height", 0)
.style("fill", function(d){
return d.startcolor;
})
.transition()
.duration(2500)
.style("fill", function(d){
return d.endcolor;
})
.attr("width", 20)
.attr("x", function(d, i) {
return 30 * i;
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return barHeight - y(d.value);
})
//__ exit
bar.exit()
.transition()
.duration(250)
.attr("y", 0)
.attr("height", 0)
.remove();
//__ bars
*/
//__ labels
var labels = labelsholder.selectAll("text")
.data(data);
labels.enter()
.append("text")
.attr("class", "barlabels")
.attr("x", 200)
.attr("y", function(d, i) {
return 20 * i;
})
.text(function(d) {
return d.label;
})
var lines = lineholder.selectAll("text")
.data(data);
lines.enter()
.append("line")// attach a line
.style("stroke-dasharray", ("3, 3"))
.style("stroke", "black")// colour the line
.attr("x1", function(d, i) {
return barWidth-(30 * (i+1));
})//x pos of the 1st end of the line
.attr("y1", function(d, i) {
return 20 * i;
})//y pos of the 1st end of the line
.attr("x2", function(d, i) {
return barWidth;
})//x pos of the 2nd end of the line
.attr("y2", function(d, i) {
return 20 * i;
});//y pos of the 2nd end of the line
var lineHeights = 100;
lines.enter()
.append("line")// attach a line
.style("stroke-dasharray", ("3, 3"))
.style("stroke", "black")// colour the line
.attr("x1", function(d, i) {
return 30 * i;
})//x pos of the 1st end of the line
.attr("y1", function(d, i) {
return lineHeights - (20 * i);
})//y pos of the 1st end of the line
.attr("x2", function(d, i) {
return 30 * i;
})//x pos of the 2nd end of the line
.attr("y2", function(d, i) {
return -15;
});//y pos of the 2nd end of the line
function liquidBar(gauge, config) {
var fillPercent = Math.max(0, Math.min(100, config.value)) / 100;
var waveHeightScale = d3.scale.linear()
.range([0, config.amplitude, 0])
.domain([0, 50, 100]);
var waveHeight = (config.h / 2) * waveHeightScale(fillPercent * 100);
var waveLength = config.w / config.countPerWidth;
var waveClipCount = 1 + config.countPerWidth;
var waveClipWidth = waveLength * waveClipCount;
// Data for building the clip wave area.
var data = [];
for (var i = 0; i <= 40 * waveClipCount; i++) {
data.push({
x: i / (40 * waveClipCount),
y: (i / (40))
});
}
// Scales for controlling the size of the clipping path.
var waveScaleX = d3.scale.linear().range([0, waveClipWidth]).domain([0, 1]);
var waveScaleY = d3.scale.linear().range([0, waveHeight]).domain([0, 1]);
// Scales for controlling the position of the clipping path.
var waveRiseScale = d3.scale.linear()
// The clipping area size is the height of the fill circle + the wave height, so we position the clip wave
// such that the it will overlap the fill circle at all when at 0%, and will totally cover the fill
// circle at 100%.
.range([(config.h + waveHeight), (waveHeight)])
.domain([0, 1]);
var waveAnimateScale = d3.scale.linear()
.range([0, waveClipWidth - config.w]) // Push the clip area one full wave then snap back.
.domain([0, 1]);
// Center the gauge within the parent SVG.
var gaugeGroup = gauge.append("g")
.attr("class", "gaugeGroup")
.attr("transform", "translate(0,0)");
var randomId = Math.floor(Math.random() * 26) + Date.now();
// The clipping wave area.
var clipArea = d3.svg.area()
.x(function(d) {
return waveScaleX(d.x);
})
.y0(function(d) {
return waveScaleY(Math.sin(Math.PI * 2 * config.offset * -1 + Math.PI * 2 * (1 - config.countPerWidth) + d.y * 2 * Math.PI));
})
.y1(function(d) {
return (config.h + waveHeight);
});
var waveGroup = gaugeGroup.append("defs")
.append("clipPath")
.attr("id", "clipWave" + randomId);
var wave = waveGroup.append("path")
.datum(data)
.attr("d", clipArea)
.attr("T", 0);
// The inner circle with the clipping wave attached.
var fillGroup = gaugeGroup.append("g")
.attr("clip-path", "url(#clipWave" + randomId + ")");
fillGroup.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", config.w)
.attr("height", config.h)
.style("fill", config.colorBefore)
.transition()
.duration(config.colorTransition)
.style("fill", config.colorAfter);
// Make the wave rise. wave and waveGroup are separate so that horizontal and vertical movement can be controlled independently.
var waveGroupXPosition = config.w - waveClipWidth;
if (config.rise) {
waveGroup.attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(0) + ')')
.transition()
.duration(config.riseTime)
.attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(fillPercent) + ')')
.each("start", function() {
wave.attr('transform', 'translate(1,0)');
}); // This transform is necessary to get the clip wave positioned correctly when waveRise=true and waveAnimate=false. The wave will not position correctly without this, but it's not clear why this is actually necessary.
} else {
waveGroup.attr('transform', 'translate(' + waveGroupXPosition + ',' + waveRiseScale(fillPercent) + ')');
}
animateWave();
function animateWave() {
wave.attr('transform', 'translate(' + waveAnimateScale(wave.attr('T')) + ',0)');
wave.transition()
.duration(config.animateTime * (1 - wave.attr('T')))
.ease('linear')
.attr('transform', 'translate(' + waveAnimateScale(1) + ',0)')
.attr('T', 1)
.each('end', function() {
wave.attr('T', 0);
animateWave(config.animateTime);
});
}
}
这篇关于d3.js 化工管柱状图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!