d3js 折线图+柱图-LMLPHP

<!DOCTYPE html>
<html>
<body>
<div id="vis"><svg></svg></div>
<div id="text"></div> <style>
div.CCMixed-tooltip {
border-radius: 5px;
visibility:hidden;
background: rgba(255,255,255,0.9);
position: absolute;
padding: 8px;
box-shadow: 0px 0px 5px #888888;
font-family: Arial, serif;
font-size: 12px;
color: #777;
} .CCMixed-axis, .legend {
font-family: Arial, serif;
font-size: 12px;
fill: #777;
} .CCMixed-axis path,
.CCMixed-axis line {
fill: none;
stroke: #DDD;
stroke-width: 2;
}
</style>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script> var CCMixedChart = {}; CCMixedChart.draw = function(elem, config){
var colorFunction = config.colorFunction; var canvas = d3.select("#" + elem);
canvas.select("svg").selectAll("*").remove();
var margin = {top: 20, right: 40, bottom: 40, left: 50};
var width = +config.width - margin.left - margin.right;
var height = +config.height - margin.top - margin.bottom - config.addtionalXAxisSpace; var svg = canvas.select("svg")
.attr("width", config.width)
.attr("height", config.height + config.addtionalXAxisSpace)
.append("g").attr("class", "canvas")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // draw legends
if(config.showLegend){
height = height - drawLegends() * 20 - 10;
} var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.5).domain(config.xAxis);
var yLeftMax = 0;
var yRightMax = 0;
for(var i=0; i<config.series.length; i++){
config.series[i].visible = true;
for(var j=0; j<config.series[i].data.length; j++){
if(config.series[i].yAxis == "left" && yLeftMax < config.series[i].data[j]){
yLeftMax = config.series[i].data[j];
}
if(config.series[i].yAxis == "right" && yRightMax < config.series[i].data[j]){
yRightMax = config.series[i].data[j];
}
}
} var yLeft = d3.scale.linear().range([height, 0]).domain([0, yLeftMax]);
var yRight = d3.scale.linear().range([height, 0]).domain([0, yRightMax]); // draw xAxis
drawXAxis(x); // draw left yAxis
drawYAxis(yLeft, "left", config.yAxisLeft);
drawYAxis(yRight, "right", config.yAxisRight); // draw charts
for(var i=0; i<config.series.length; i++){
var chart = config.series[i];
if(chart.type == 'bar'){
drawBarChart(chart);
} else if(chart.type == 'line'){
drawLineChart(chart)
}
} // draw tooltip
var tooltip = canvas.append("div").attr("class", "CCMixed-tooltip"); // draw invisible bars for hover/click events
svg.append("g").attr("class", "bars bars-action").selectAll(".bar-hover")
.data(config.xAxis)
.enter().append("rect")
.attr("class", "bar-hover")
.attr("x", function(d) { return x(d)-x.rangeBand()/2; })
.attr("y", 0)
.attr("width", x.rangeBand()*2)
.attr("height", height)
.style("opacity", "0")
.style("fill", "gold")
.style("cursor", "pointer")
.on("mouseover", function(d, i){mouseOver(this, d.replace(/ /g, '_'), i)})
.on("mouseout", function(d, i){mouseOut(this, d.replace(/ /g, '_'), i)})
.on("mousemove", function(){updateTooltipPos(d3.event)})
.on("click", function(d, i){
if(config.clickFunction){
config.clickFunction(config, d, i);
}
// stop the propagation of event
d3.event.stopPropagation();
}); function drawLegends(){
var legend = svg.append("g").attr("class", "legends CCMixed-legend"); var lx = 0;
var ly = 0;
var rows = 1; for(var i=0; i<config.series.length; i++){
var chart = config.series[i];
if(chart.type == 'bar'){
drawBarLegend(chart);
} else if(chart.type == 'line'){
drawLineLegend(chart)
}
} var tx = 0;
if(rows == 1){
tx = (width - lx) / 2;
}
legend.attr("transform", "translate(" + tx + ", "+ (config.height+config.addtionalXAxisSpace-(rows+1)*20) + ")"); function drawBarLegend(chart){
var barLegend = legend.append("g")
.attr("class", "legend legend-"+chart.label.replace(/ /g, '_'))
.style("cursor", "pointer")
.attr("transform", getLegendTransfrom(chart))
.style("fill", colorFunction(chart.colorKey));
barLegend.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", 12)
.attr("height", 12);
barLegend.append("text")
.attr("x", 10)
.attr("y", 0)
.attr("dy", "0.85em")
.attr("dx", "0.4em")
.style("text-anchor", "begin")
.text(chart.label);
barLegend.on("mouseover", function(){
svg.select(".bars-"+chart.label.replace(/ /g, '_')).selectAll(".bar")
.style("stroke","gold").style("stroke-width", 3);
}).on("mouseout", function(){
svg.select(".bars-"+chart.label.replace(/ /g, '_')).selectAll(".bar")
.style("stroke-width", 0);
}).on("click", function(){
toggleVisibility(chart);
});
} function drawLineLegend(chart){
var lineLegend = legend.append("g")
.attr("class", "legend legend-"+chart.label.replace(/ /g, '_'))
.style("cursor", "pointer")
.style("fill", colorFunction(chart.colorKey))
.attr("transform", getLegendTransfrom(chart));
lineLegend.append("circle")
.attr("r", 4)
.attr("cx", 6)
.attr("cy", 6)
lineLegend.append("line")
.attr("x1", 0)
.attr("y1", 6)
.attr("x2", 12)
.attr("y2", 6)
.style("stroke", colorFunction(chart.colorKey))
.style("stroke-width", 2)
lineLegend.append("text")
.attr("x", 10)
.attr("y", 0)
.attr("dy", "0.85em")
.attr("dx", "0.4em")
.style("text-anchor", "begin")
.text(chart.label)
lineLegend.on("mouseover", function(d){
svg.select(".line-"+chart.label.replace(/ /g, '_'))
.style("stroke-width", 4);
svg.select(".circles-"+chart.label.replace(/ /g, '_')).selectAll(".circle")
.attr("r", 6).style("stroke","gold").style("stroke-width", 2);
}).on("mouseout", function(d){
svg.select(".line-"+chart.label.replace(/ /g, '_'))
.style("stroke-width", 2);
svg.select(".circles-"+chart.label.replace(/ /g, '_')).selectAll(".circle")
.attr("r", 4).style("stroke-width", 0);
}).on("click", function(){
toggleVisibility(chart);
});
} function toggleVisibility (chart){
if(chart.type == "bar"){
var elem = svg.select(".bars-"+chart.label.replace(/ /g, '_'));
toggle(elem, chart);
} else if(chart.type == "line"){
var elem = svg.select(".line-"+chart.label.replace(/ /g, '_'));
toggle(elem, chart);
elem = svg.select(".circles-"+chart.label.replace(/ /g, '_'));
toggle(elem, chart);
} var showLeft = false;
var showRight = false;
for(var i=0; i<config.series.length; i++){
if(config.series[i].yAxis == "left" && config.series[i].visible){
showLeft = true;
}
if(config.series[i].yAxis == "right" && config.series[i].visible){
showRight = true;
}
} if(showLeft){
svg.select(".axis-left").selectAll(".tick").select("text").style("visibility", "visible");
svg.select(".axis-left").select(".title").style("visibility", "visible");
} else {
svg.select(".axis-left").selectAll(".tick").select("text").style("visibility", "hidden");
svg.select(".axis-left").select(".title").style("visibility", "hidden");
} if(showRight){
svg.select(".axis-right").selectAll(".tick").select("text").style("visibility", "visible");
svg.select(".axis-right").select(".title").style("visibility", "visible");
} else {
svg.select(".axis-right").selectAll(".tick").select("text").style("visibility", "hidden");
svg.select(".axis-right").select(".title").style("visibility", "hidden");
} function toggle(elem, chart){
if(elem.style("visibility") == "visible"){
elem.style("visibility", "hidden");
chart.visible = false;
var legend = svg.select(".legends").select(".legend-"+chart.label.replace(/ /g, '_')).style("fill", "#777");
if(chart.type == "line"){
legend.select("line").style("stroke", "#777");
}
} else {
elem.style("visibility", "visible");
chart.visible = true;
var legend = svg.select(".legends").select(".legend-"+chart.label.replace(/ /g, '_')).style("fill", colorFunction(chart.colorKey));
if(chart.type == "line"){
legend.select("line").style("stroke", colorFunction(chart.colorKey));
}
}
}
}; function getLegendTransfrom(chart){
var translate = "translate(" + lx + "," + ly + ")";
lx = lx + chart.label.length * 5 + 30;
if(chart.type == "line"){
lx = lx + 15;
} else if(chart.type == "bar"){
lx = lx - 10;
}
if(lx + chart.label.length * 5 >= width){
lx = 0;
ly = ly + 20;
rows = rows + 1;
}
return translate;
} return rows;
} function drawBarChart(chart){
var yScale = yLeft;
if(chart.yAxis == "right"){
yScale = yRight;
} svg.append("g")
.attr("class", function(d, i){return "bars bars-"+chart.label.replace(/ /g, '_');})
.selectAll(".bar")
.data(chart.data)
.enter().append("rect")
.attr("class", function(d, i){return "bar bar-"+config.xAxis[i].replace(/ /g, '_');})
.attr("x", function(d, i) { return x(config.xAxis[i]); })
.attr("y", function(d) { return yScale(d); })
.attr("width", x.rangeBand())
.attr("height", function(d) { return height - yScale(d); })
.style("fill", colorFunction(chart.colorKey));
} function drawLineChart(chart){
var yScale = yLeft;
if(chart.yAxis == "right"){
yScale = yRight;
} var line = d3.svg.line()
.x(function(d, i) {
return x(config.xAxis[i]) + x.rangeBand()/2;
})
.y(function(d) {
return yScale(d);
}); svg.append("g").append("path")
.style("stroke", colorFunction(chart.colorKey))
.attr("class", function(d, i){return "line-"+chart.label.replace(/ /g, '_');})
.style("stroke-width", 2)
.style("fill", "none")
.attr("d", line(chart.data)); svg.append("g")
.attr("class", function(d, i){return "circles circles-"+chart.label.replace(/ /g, '_');})
.selectAll(".circle")
.data(chart.data)
.enter().append("circle")
.attr("class", function(d, i){return "circle circle-"+config.xAxis[i].replace(/ /g, '_');})
.attr("r", 4)
.style("fill", colorFunction(chart.colorKey))
.attr("cx", function(d, i){return x(config.xAxis[i]) + x.rangeBand()/2;})
.attr("cy", function(d){return yScale(d);}); } function mouseOver(elem, d, i){
d3.select(elem).style("opacity", "0.3");
svg.select(".axis-x").select(".axis-"+d).style("font-weight","bold").style("font-size","14px");
svg.selectAll(".circles").select(".circle-"+d).attr("r", 7).style("stroke","gold").style("stroke-width",2);
svg.selectAll(".bars").select(".bar-"+d).style("stroke","gold").style("stroke-width", 2); var html;
if(config.tooltipFunction){
html = config.tooltipFunction(config, d, i);
} else {
html = getTooltips(d, i);
} tooltip.html(html).style("visibility", "visible"); function getTooltips(d, i){
var html = d + "<br/>";
for(var j=0; j<config.series.length; j++){
html = html + "<b>" + config.series[j].label + "</b>: " + config.series[j].data[i] + "<br/>";
}
return html;
}
}; function mouseOut(elem, d, i){
d3.select(elem).style("opacity", "0");
svg.select(".axis-x").select(".axis-"+d).style("font-weight","normal").style("font-size","12px");
svg.selectAll(".circles").select(".circle-"+d).attr("r", 4).style("stroke-width",0);
svg.selectAll(".bars").select(".bar-"+d).style("stroke-width",0);
tooltip.style("visibility", "hidden");
} function updateTooltipPos (e){
tooltip.style("top", (e.pageY + 15) + "px")
.style("left", (e.pageX + 15) + "px");
}; function drawXAxis(x){
// draw x
svg.append("g")
.attr("class", "CCMixed-axis axis-x")
.attr("transform", "translate(0," + height + ")")
.call(customXAxis)
.selectAll("text")
.data(config.xAxis)
.attr("class", function(d, i){return "axis-"+config.xAxis[i].replace(/ /g, '_');})
.style("text-anchor", "end")
.attr("dy", "0.3em")
.attr("dx", "-0.7em")
.attr("transform", "rotate(-45)")
.text(function(d, i){
// skip one if too many ticks
//alert(config.xAxis.length);
if(config.xAxis.length >= 10 && i % 2 == 1){
return "";
} else {
return d
}
});
function customXAxis(g) {
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
g.call(xAxis);
g.select(".domain").remove();
}
} function drawYAxis(y, orient, setting){
var axisY = svg.append("g")
.attr("class", "CCMixed-axis axis-"+orient)
.style("fill", colorFunction(setting.colorKey))
.call(customYAxis)
if(orient == 'right'){
axisY.attr("transform", "translate(" + width + ", 0)")
}
if(setting.title){
var axisYLabel = axisY.append("text")
.text(setting.title)
.attr("class", "title")
if(orient == "left"){
axisYLabel.attr("transform", "rotate(-90)")
.attr("y", -30)
.attr("x", -1*height/2)
.attr("dx", setting.title.length * 0.25 + "em")
.style("text-anchor", "end")
} else {
axisYLabel.attr("transform", "rotate(90)")
.attr("y", -30)
.attr("x", height/2)
.attr("dx", -1 * setting.title.length * 0.25 + "em")
.style("text-anchor", "begin")
}
} function customYAxis(g) {
var yAxis = d3.svg.axis()
.scale(y)
.orient(orient)
.tickSize(orient=="left"? -1*width : 0)
.ticks(config.yAxisTicks);
g.call(yAxis);
g.select(".domain").remove();
g.selectAll(".tick:not(:first-of-type) line").attr("stroke", "#030303");
}
}
}; var colorFunction1 = function(key){
return key;
} var clickFunction1 = function(config, d, i){
var html = d + "<br/>";
for(var j=0; j<config.series.length; j++){
html = html + "<b>" + config.series[j].label + "</b>: " + config.series[j].data[i] + "<br/>";
}
$("#text").html(html);
} $(document).ready(function(){
var vis = $("#vis"); var config = {
"addtionalXAxisSpace": 0, // OPTIONAL. if the labels for xAxis is too long can add more space between xAxis and legend
"width": 600, // width of canvas
"height": 400, // height of canvas
"showLegend": true, // OPTIONAL.
"colorFunction": colorFunction1, // color function, accept a key and return a color, can use d3.scale.category20()
"clickFunction": clickFunction1, // OPTIONAL. handles click event when clicking a day
//"tooltipFunction": tooltipFunction // OPTIONAL. if not provided default function will be used
"xAxis": ["1-Jul", "2-Jul", "3-Jul", "4-Jul", "5-Jul"],
"xAxisData": [1, 2, 3, 4, 5], // OPTIONAL. can store additional data to be used by the click event. e.g. xAxis is the date string but xAxisData is the timestamp
"yAxisTicks": 5, // number of horizontal lines
"yAxisLeft": { // config the left y axis, MUST if any series need to use this axis
"title": "Number of Non-responding Nodes",
"colorKey": "cornflowerblue"
},
"yAxisRight": { // config the right y axis, MUST if any series need to use this axis
"title": "Percentage (%)",
"colorKey": "yellowgreen"
},
"series":[ // array of chart config. one element correspond to one chart. bar charts are NOT stacked and will OVERLAP
{
"colorKey": " cornflowerblue",
"type": "bar", // chart type - bar or line
"data": [10, 20, 50, 100, 30],
"yAxis": "left", // associate the chart to left or right axis
"label": "label 1" // used in legends and tooltips
},
{
"colorKey": "yellowgreen",
"type": "line",
"data": [10, 20, 50, 100, 30],
"yAxis": "right",
"label": "label 2"
},
{
"colorKey": "yellow",
"type": "line",
"data": [30, 24, 64, 40, 39],
"yAxis": "right",
"label": "label 3"
}
]
}; CCMixedChart.draw("vis", config);
}); </script>
</body>
</html>
05-25 19:36