实现如下效果:
一、json数据:
{
"data": [[100.00,92.24,84.47,76.71,68.95,61.18,53.42,46.81,40.10,29.00,13.33]],
"categories": [0,10,20,30,40,50,60, 70, 80, 90, 100],
"area_data":[
{"value":[100.000,88.760,77.520,66.290,55.050,43.810,34.440,28.430,21.330,10.000,3.333], "other_value":[100.00,95.08,90.17,85.25,80.33,75.42,70.50,65.71,59.24,47.52,30.00], "color": "#388E3C", "opacity": 0.7}
],
"marker":{
"cross":[
{"value":[ 81.36 ,40] ,"h_step_start":10.17, "h_step_end":10.17, "v_step_start":16.67, "v_step_end":16.67,"desc":"12926.5 ( 81.36 ,40 )"}
]
},
"params":{
"title":"临近坐标图标题测试",
"x_label":"x轴测试",
"y_label":"y轴测试",
"drag_label":"AUC:56.7%",
"colors":["#000"],
"show_grid":true,
"show_cross":true,
"show_area":true
},
"size":{
"width":800,
"height":600
}
}
参数说明:
- *data:线条,二维数组
- *categories:x轴刻度,需要数值型
- area_data:智信区间的内容, cross[value:中心点, h_step_start:水平右面步长, h_step_end:水平左面步长, v_step_start:垂直下面步长, v_step_end:垂直上面步长,desc:鼠标放十字架上的提示框]
- marker:包含十字架的内容
- title:标题
- x_label:x轴标签
- y_label:y轴标签
- drag_label:可以拖拽的文字
- colors:线条颜色
- show_grid:false| true, 是否显示网格线
- show_cross:false| true, 是否显示十字架
- show_area:false| true, 是否显示智信区间
二、用到的js插件
#jquery插件
<script src="jquery-1.8.3.min.js"></script>
#d3 v3版本插件
<script src="d3-3.min.js"></script>
#图的实现文件
<script src="area_line.js"></script>
三、图的调用
var content = $.parseJSON($('textarea').val()); //从textarea里面取出图的json数据并转化为object
graph.areaLine("container", content); //把生成图放在#container里面
四、以下为图的代码实现(D3.js实现)
var graph = {
/**
* 临近坐标图(区域+line+mark)
*
* @return void
**/
areaLine(container, content)
{
d3.select('#'+container).select('svg').remove();
d3.select('body').select('.arealine_tooltip').remove();
var colors = ["#388E3C", "#F44336", "#0288D1", "#FF9800", "#727272", "#E91E63", "#673AB7", "#8BC34A", "#2196F3", "#D32F2F", "#FFC107", "#BDBDBD", "#F8BBD0", "#3F51B5", "#CDDC39", "#009688", "#C2185B", "#FFEB3B", "#212121", "#FFCCBC", "#BBDEFB", "#0099CC", "#FFcc99"];
var width = content.size.width;
var height = content.size.height;
var margin = {
top : 50,
right : 50,
bottom : 50,
left : 50,
};
var grid_width = width - margin.left - margin.right;
var grid_height = height - margin.top - margin.bottom;
// 计算最大值、最小值
var max_ys = new Array();
var min_ys = new Array();
for (var i in content.data) {
max_ys.push(d3.max(content.data[i]));
min_ys.push(d3.min(content.data[i]));
}
if (typeof(content.area_data) != 'undefined') {
for (var i in content.area_data) {
min_ys.push(d3.min(content.area_data[i].value));
min_ys.push(d3.min(content.area_data[i].other_value));
max_ys.push(d3.max(content.area_data[i].other_value));
max_ys.push(d3.max(content.area_data[i].other_value));
}
}
var max_y = d3.max(max_ys);
var min_y = d3.min(min_ys);
console.log(min_ys, max_ys);
if (min_y >= 0) {
min_y -= min_y/2;
} else {
min_y += min_y/2;
}
if (max_y >= 0) {
//max_y += max_y/5;
} else {
max_y += max_y/2;
}
// 计算x的轴刻度值
var categories = content.categories;
/*for (var i in content.data[0]){
categories.push(parseInt(i));
}*/
// 定义x,y比例尺
var x_scale = d3.scale.linear()
.domain([d3.max(categories),d3.min(categories)])
.range([0, grid_width]);
var y_scale = d3.scale.linear()
.domain([max_y,min_y])
.range([0, grid_height]);
// 定义x,y坐标轴
var x_axis = d3.svg.axis()
.scale(x_scale)
.orient('bottom')
.outerTickSize(0);
var y_axis = d3.svg.axis()
.scale(y_scale)
.orient('left')
.outerTickSize(0);
var line = d3.svg.line()
.x(function(d,i) {
return x_scale(categories[i]);
})
.y(function(d) {
return y_scale(d);
});
//开始绘图
var svg = d3.select('#'+container)
.append('svg')
.attr('version', '1.1')
.attr('style', 'font-family:arial')
.attr('xmlns', 'http://www.w3.org/2000/svg')
.attr('width', width)
.attr('height', height);
var main = svg.append('g')
.attr('class', 'main')
.attr('transform','translate('+margin.left+', '+margin.top+')');
var xaxis_obj = main.append('g')
.attr('class', 'axis xaxis')
.call(x_axis)
.attr('transform', 'translate(0, '+grid_height+')');
var yaxis_obj = main.append('g')
.attr('class', 'axis yaxis')
.call(y_axis);
var xtick_values = x_scale.ticks();
var ytick_values = y_scale.ticks();
d3.select('.xaxis').selectAll('.tick').each(function() {
xtick_values.push(d3.select);
});
// 是否显示网格线
if (typeof(content.params.show_grid) != 'undefined' && content.params.show_grid == true) {
var helplines = main.append('g')
.attr('class','helplines');
var y_helpers = helplines.append('g').attr('class', 'y_helps').selectAll('.helpline')
.data(ytick_values)
.enter()
.append('line')
.attr('class', 'helpline')
.attr('x1', function() {
return x_scale(d3.min(xtick_values));
})
.attr('y1', function(d) {
return y_scale(d);
})
.attr('x2', function() {
return x_scale(d3.max(categories));
})
.attr('y2', function(d) {
console.log(graph.getAngle(x_scale(d3.min(xtick_values)),y_scale(d),x_scale(d3.max(categories)),y_scale(d)));
return y_scale(d);
});
d3.selectAll('.helplines').selectAll('.helpline').attr('stroke', '#ccc').attr('stroke-dasharray', '5,5');
}
//绘制背景
var bg_obj = main.append('g').attr('class','bg');
bg_obj.append('rect')
.attr('width',grid_width)
.attr('height', grid_height)
.attr('stroke', '#000')
.attr('fill', 'none');
bg_obj.append('line')
.attr('x1', function() {
return x_scale(d3.max(categories));
})
.attr('y1', function() {
return y_scale(min_y);
})
.attr('x2', function() {
return x_scale(0);
})
.attr('y2', function() {
return y_scale(max_y);
})
.attr('stroke', '#ccc');
if (typeof(content.params.show_area) != 'undefined' && content.params.show_area === true && typeof(content.area_data) != 'undefined'){
// 画区域
var areas_obj = main.append('g')
.attr('class', 'area_group')
.selectAll('.areas')
.data(content.area_data)
.enter()
.append('path')
.attr('class', 'areas')
.attr('d', function(d) {
return graph.drawArea(d,x_scale, y_scale,categories);
})
.attr('stroke', function(d) {
return d.color;
})
.attr('fill', function(d) {
return d.color;
})
.attr('fill-opacity', function(d) {
return d.opacity;
});
}
var line_obj = main.append('g')
.attr('class', 'line_group')
.selectAll('.lines')
.data(content.data)
.enter()
.append('path')
.attr('class', 'lines')
.attr('d', function(d) {
return line(d);
})
.attr('stroke', function(d, i) {
return typeof(content.params.colors) != 'undefined' ? content.params.colors[i%content.params.colors.length] : colors[i%colors.length];
})
.attr('stroke-width',2)
.attr('fill', 'none');
// 创建提示框
var tooltip = this.createTip();
var circle_group = main.append('g').attr('class', 'circle_group');
var circle_obj = circle_group.selectAll('.circles')
.data(content.data)
.enter()
.append('g')
.attr('class', 'circles')
.each(function(d,i) {
var self = d3.select(this);
self.selectAll('.circle_'+i)
.data(d)
.enter()
.append('circle')
.attr('class', 'circle_'+i)
.attr('cx', function(dd,ii) {
return x_scale(categories[ii]);
})
.attr('cy', function(dd, ii) {
return y_scale(dd);
})
.attr('r', 5)
.attr('fill', '#000')
.attr('fill-opacity', 0.0)
.on('mouseover', function(d,i) {
var circle_obj = d3.select(this);
var page_x = d3.event.pageX;
var page_y = d3.event.pageY+20;
circle_obj.attr('fill-opacity', 0.7);
tooltip.html('<b>position:</b>'+categories[i]+"</br> <b>value:</b>"+d)
.style("left",page_x+"px")
.style("top",page_y+"px")
.style("opacity",0.9)
.style('padding', '5px');
})
.on('mouseout', function(){
var circle_obj = d3.select(this);
circle_obj.attr('fill-opacity', 0.0);
tooltip.style('opacity', 0.0);
});
});
// 画十字架
if (typeof(content.marker) != 'undefined' && typeof(content.marker.cross) != '' && typeof(content.params.show_cross) != 'undefined' && content.params.show_cross === true) {
var cross_group = main.append('g').attr('class', 'marker').append('g').attr('class', 'cross_group');
cross_group.selectAll('.cross')
.data(content.marker.cross)
.enter()
.append('g')
.attr('class', 'cross')
.attr('transform', function(d, i) {
return 'rotate(0, '+x_scale(d.value[0])+','+y_scale(d.value[1])+')';
})
.each(function(d) {
var self = d3.select(this);
self.append('line')
.attr('class', 'cross_v')
.attr('x1', function(d, i) {
return x_scale(d.value[0]);
})
.attr('y1', function(d){
return y_scale(d.value[1]+d.v_step_start);
})
.attr('x2', function(d) {
return x_scale(d.value[0]);
})
.attr('y2', function(d) {
return y_scale(d.value[1] - d.v_step_end);
})
.attr('stroke', '#000');
self.append('line')
.attr('class', 'cross_v_start')
.attr('x1', function(d, i) {
return x_scale(d.value[0])-4;
})
.attr('y1', function(d){
return y_scale(d.value[1]+d.v_step_start);
})
.attr('x2', function(d) {
return x_scale(d.value[0])+4;
})
.attr('y2', function(d) {
return y_scale(d.value[1]+d.v_step_start);
})
.attr('stroke', '#000');
self.append('line')
.attr('class', 'cross_v_end')
.attr('x1', function(d, i) {
return x_scale(d.value[0])-4;
})
.attr('y1', function(d){
return y_scale(d.value[1]-d.v_step_end);
})
.attr('x2', function(d) {
return x_scale(d.value[0])+4;
})
.attr('y2', function(d) {
return y_scale(d.value[1]-d.v_step_end);
})
.attr('stroke', '#000');
self.append('line')
.attr('class', 'cross_h')
.attr('x1', function(d, i) {
return x_scale(d.value[0]-d.h_step_start);
})
.attr('y1', function(d){
return y_scale(d.value[1]);
})
.attr('x2', function(d) {
return x_scale(d.value[0]+d.h_step_end);
})
.attr('y2', function(d) {
return y_scale(d.value[1]);
})
.attr('stroke', '#000');
self.append('line')
.attr('class', 'cross_h_start')
.attr('x1', function(d, i) {
return x_scale(d.value[0]-d.h_step_start);
})
.attr('y1', function(d){
return y_scale(d.value[1])-4;
})
.attr('x2', function(d) {
return x_scale(d.value[0]-d.h_step_start);
})
.attr('y2', function(d) {
return y_scale(d.value[1])+4;
})
.attr('stroke', '#000');
self.append('line')
.attr('class', 'cross_h_end')
.attr('x1', function(d, i) {
return x_scale(d.value[0]+d.h_step_end);
})
.attr('y1', function(d){
return y_scale(d.value[1])-4;
})
.attr('x2', function(d) {
return x_scale(d.value[0]+d.h_step_end);
})
.attr('y2', function(d) {
return y_scale(d.value[1])+4;
})
.attr('stroke', '#000');
})
.on('mouseover', function(d) {
var self = d3.select(this);
var page_x = d3.event.pageX;
var page_y = d3.event.pageY+20;
circle_obj.attr('fill-opacity', 0.7);
tooltip.html('<b>Description:</b>'+d.desc)
.style("left",page_x+"px")
.style("top",page_y+"px")
.style("opacity",0.9)
.style('padding', '5px');
})
.on('mouseout', function() {
tooltip.style('opacity', 0.0);
});
//去掉定时旋转,
//setInterval(function() {
d3.selectAll('.cross').each(function() {
var self = d3.select(this);
var rotate = self.attr('transform');
rotate = rotate.replace(/rotate\(/, '');
rotate = rotate.replace(/\)/, '');
var rotates = rotate.split(',');
var angle = parseInt(rotates[0]) + 10;
if (angle == 360) {
angle = 0;
}
//去掉倾斜角度
//self.attr('transform', 'rotate('+angle+', '+rotates[1]+', '+rotates[2]+')');
});
//}, 10000);
}
// 赋x,y刻度样式
d3.selectAll('.axis').selectAll('line').attr('stroke', '#000').attr('style', 'font-size:11px');
d3.selectAll('.axis').selectAll('path').attr('stroke', '#000').attr('style', 'font-size:11px').attr('fill', 'none');
var move_label = typeof(content.params.drag_label) != 'undefined' ? content.params.drag_label: '';
var move_x = width/2;
var move_y = grid_height + margin.top - 20;
if (move_label != '') {
var drag = this.createMove();
console.log(drag);
svg.append('text').text(move_label).attr('x', move_x).attr('y',move_y).attr('style', 'font-weight:bold;font-size:12px;cursor:pointer').call(drag);
}
var title = typeof(content.params.title) != 'undefined' ? content.params.title : '';
var title_per_width = 1;
if (title != '') {
var title_x = (width - title.length * title_per_width) /2;
svg.append('text').text(title).attr('text-anchor', 'middle').attr('x', title_x).attr('y', margin.top/2);
}
var x_label = typeof(content.params.x_label) != 'undefined' ? content.params.x_label : '';
if (x_label != '') {
var x_label_x = width/2 - x_label.length;
svg.append('text').text(x_label).attr('text-anchor', 'middle').attr('x', x_label_x).attr('y', height - margin.bottom/4).attr('style', 'font-size:11px');
}
var y_label = typeof(content.params.y_label) != 'undefined' ? content.params.y_label : '';
if (y_label != '') {
var y_label_x = height/2 - x_label.length;
svg.append('text').text(y_label).attr('text-anchor', 'middle').attr('x', -y_label_x).attr('y', margin.left/4).attr('transform', 'rotate(-90)').attr('style', 'font-size:11px');
}
},
/**
*
* 画区域
**/
drawArea:function(data, x_scale, y_scale, categories)
{
var y0_data = data['value'];
var y1_data = data['other_value'];
var area = d3.svg.area()
.x(function(d,i) {
return x_scale(categories[i])
})
.y0(function(d, i) {
return y_scale(d);
})
.y1(function(d, i) {
return y_scale(y1_data[i]);
});
return area(y0_data);
},
/**
*
* 创建移动事件
**/
createMove:function()
{
// 定义拖拽事件
var drag = d3.behavior.drag()
.on("drag",dragmove)
function dragmove(d){
d3.select(this)
.attr("x", d3.event.x)
.attr("y", d3.event.y);
}
return drag;
},
/**
*
* 创建提示框
**/
createTip:function()
{
var tooltip = d3.select("body").append("div")
.attr("class","arealine_tooltip")
.attr("opacity",0.0);
return tooltip;
},
getAngle:function(x1,y1,x2,y2)
{
var x = Math.abs(x1 - x2);
var y = Math.abs(y1 - y2);
// 斜边长
var z = Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
// 余弦
var cos = y/z;
//弧度
var radina = Math.acos(cos);
//角度
var angle = 180/ (Math.PI/ radina);
return angle;
}
};
以上就是临近坐标图的全部实现,如果有不清楚的地方可以留言,欢迎交流