我正在使用java脚本和画布制作图表。下面是jsfiddle链接
http://jsfiddle.net/ghan_123/n0r796ae/
我想在悬停气泡(转折点)上显示值,如
1月8日的价格2100
java脚本
var CanvasChart = function () {
var ctx;
var margin = { top: 40, left: 75, right: 0, bottom: 75 };
var chartHeight, chartWidth, yMax, xMax, data;
var maxYValue = 0;
var ratio = 0;
var renderType = { lines: 'lines', points: 'points' };
var render = function(canvasId, dataObj) {
data = dataObj;
getMaxDataYValue();
var canvas = document.getElementById(canvasId);
chartHeight = canvas.getAttribute('height');
chartWidth = canvas.getAttribute('width');
xMax = chartWidth - (margin.left + margin.right);
yMax = chartHeight - (margin.top + margin.bottom);
ratio = yMax / maxYValue;
ctx = canvas.getContext("2d");
renderChart();
};
var renderChart = function () {
renderBackground();
renderText();
renderLinesAndLabels();
//render data based upon type of renderType(s) that client supplies
if (data.renderTypes == undefined || data.renderTypes == null) data.renderTypes = [renderType.lines];
for (var i = 0; i < data.renderTypes.length; i++) {
renderData(data.renderTypes[i]);
}
};
var getMaxDataYValue = function () {
for (var i = 0; i < data.dataPoints.length; i++) {
if (data.dataPoints[i].y > maxYValue) maxYValue = data.dataPoints[i].y;
}
};
var renderBackground = function() {
var lingrad = ctx.createLinearGradient(margin.left, margin.top, xMax - margin.right, yMax);
lingrad.addColorStop(0.0, '#ffffff');
lingrad.addColorStop(0.2, '#fff');
lingrad.addColorStop(0.8, '#fff');
lingrad.addColorStop(1, '#ffffff');
ctx.fillStyle = lingrad;
ctx.fillRect(margin.left, margin.top, xMax - margin.left, yMax - margin.top);
ctx.fillStyle = '#6A6A6A';
};
var renderText = function() {
var labelFont = (data.labelFont != null) ? data.labelFont : '20pt Arial';
ctx.font = labelFont;
ctx.textAlign = "center";
//Title
var txtSize = ctx.measureText(data.title);
ctx.fillText(data.title, (chartWidth / 2), (margin.top / 2));
//X-axis text
txtSize = ctx.measureText(data.xLabel);
ctx.fillText(data.xLabel, margin.left + (xMax / 2) - (txtSize.width / 2), yMax + (margin.bottom / 1.2));
//Y-axis text
ctx.save();
ctx.rotate(-Math.PI / 2);
ctx.font = labelFont;
ctx.fillText(data.yLabel, (yMax / 2) * -1, margin.left / 4);
ctx.restore();
};
var renderLinesAndLabels = function () {
//Vertical guide lines
var yInc = yMax / data.dataPoints.length;
var yPos = 0;
var yLabelInc = (maxYValue * ratio) / data.dataPoints.length;
var xInc = getXInc();
var xPos = margin.left;
for (var i = 0; i < data.dataPoints.length; i++) {
yPos += (i == 0) ? margin.top : yInc;
//Draw horizontal lines
drawLine(margin.left, yPos, xMax, yPos, '#E8E8E8');
//y axis labels
ctx.font = (data.dataPointFont != null) ? data.dataPointFont : '10pt Calibri';
var txt = Math.round(maxYValue - ((i == 0) ? 0 : yPos / ratio));
var txtSize = ctx.measureText(txt);
ctx.fillText(txt, margin.left - ((txtSize.width >= 14) ? txtSize.width : 10) - 7, yPos + 4);
//x axis labels
txt = data.dataPoints[i].x;
txtSize = ctx.measureText(txt);
ctx.fillText(txt, xPos, yMax + (margin.bottom / 3));
xPos += xInc;
}
//Vertical line
drawLine(margin.left, margin.top, margin.left, yMax, 'black');
//Horizontal Line
drawLine(margin.left, yMax, xMax, yMax, 'black');
};
var renderData = function(type) {
var xInc = getXInc();
var prevX = 0,
prevY = 0;
for (var i = 0; i < data.dataPoints.length; i++) {
var pt = data.dataPoints[i];
var ptY = (maxYValue - pt.y) * ratio;
if (ptY < margin.top) ptY = margin.top;
var ptX = (i * xInc) + margin.left;
if (i > 0 && type == renderType.lines) {
//Draw connecting lines
drawLine(ptX, ptY, prevX, prevY, '#309FB2', 2);
}
if (type == renderType.points) {
var radgrad = ctx.createRadialGradient(ptX, ptY, 8, ptX - 5, ptY - 5, 0);
radgrad.addColorStop(0, '#309FB2');
radgrad.addColorStop(0.9, 'White');
ctx.beginPath();
ctx.fillStyle = radgrad;
//Render circle
ctx.arc(ptX, ptY, 8, 0, 2 * Math.PI, false)
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
ctx.stroke();
ctx.closePath();
}
prevX = ptX;
prevY = ptY;
}
};
var getXInc = function() {
return Math.round(xMax / data.dataPoints.length) - 1;
};
var drawLine = function(startX, startY, endX, endY, strokeStyle, lineWidth) {
if (strokeStyle != null) ctx.strokeStyle = strokeStyle;
if (lineWidth != null) ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.stroke();
ctx.closePath();
};
return {
renderType: renderType,
render: render
};
} ();
$(document).ready(function () {
var dataDef = { title: "US Population Chart",
xLabel: 'Date',
yLabel: 'Price',
labelFont: '19pt Arial',
dataPointFont: '10pt Arial',
renderTypes: [CanvasChart.renderType.lines, CanvasChart.renderType.points],
dataPoints: [{ x: '1-aug', y: 2100 },
{ x: '2-aug', y: 2000 },
{ x: '3-aug', y: 2200 },
{ x: '4-aug', y: 1800 },
{ x: '5-aug', y: 1500 },
{ x: '6-aug', y: 3000 },
]
};
CanvasChart.render('canvas', dataDef);
});
HTML部分
<body style="margin-left:50px;margin-top:50px;">
<canvas id="canvas" width="500" height="300"></canvas>
答案 0 :(得分:2)
以下是使用普通旧DIV作为工具提示的简单方法。
如果鼠标位于数据点内,
这是带注释的代码和演示:
var CanvasChart = function () {
var ctx;
var margin = { top: 40, left: 75, right: 0, bottom: 75 };
var chartHeight, chartWidth, yMax, xMax, data;
var maxYValue = 0;
var ratio = 0;
var renderType = { lines: 'lines', points: 'points' };
// addition
var dataXY=[];
var render = function(canvasId, dataObj) {
data = dataObj;
getMaxDataYValue();
var canvas = document.getElementById(canvasId);
chartHeight = canvas.getAttribute('height');
chartWidth = canvas.getAttribute('width');
xMax = chartWidth - (margin.left + margin.right);
yMax = chartHeight - (margin.top + margin.bottom);
ratio = yMax / maxYValue;
ctx = canvas.getContext("2d");
renderChart();
};
var renderChart = function () {
renderBackground();
renderText();
renderLinesAndLabels();
//render data based upon type of renderType(s) that client supplies
if (data.renderTypes == undefined || data.renderTypes == null) data.renderTypes = [renderType.lines];
for (var i = 0; i < data.renderTypes.length; i++) {
renderData(data.renderTypes[i]);
}
};
var getMaxDataYValue = function () {
for (var i = 0; i < data.dataPoints.length; i++) {
if (data.dataPoints[i].y > maxYValue) maxYValue = data.dataPoints[i].y;
}
};
var renderBackground = function() {
var lingrad = ctx.createLinearGradient(margin.left, margin.top, xMax - margin.right, yMax);
lingrad.addColorStop(0.0, '#ffffff');
lingrad.addColorStop(0.2, '#fff');
lingrad.addColorStop(0.8, '#fff');
lingrad.addColorStop(1, '#ffffff');
ctx.fillStyle = lingrad;
ctx.fillRect(margin.left, margin.top, xMax - margin.left, yMax - margin.top);
ctx.fillStyle = '#6A6A6A';
};
var renderText = function() {
var labelFont = (data.labelFont != null) ? data.labelFont : '20pt Arial';
ctx.font = labelFont;
ctx.textAlign = "center";
//Title
var txtSize = ctx.measureText(data.title);
ctx.fillText(data.title, (chartWidth / 2), (margin.top / 2));
//X-axis text
txtSize = ctx.measureText(data.xLabel);
ctx.fillText(data.xLabel, margin.left + (xMax / 2) - (txtSize.width / 2), yMax + (margin.bottom / 1.2));
//Y-axis text
ctx.save();
ctx.rotate(-Math.PI / 2);
ctx.font = labelFont;
ctx.fillText(data.yLabel, (yMax / 2) * -1, margin.left / 4);
ctx.restore();
};
var renderLinesAndLabels = function () {
//Vertical guide lines
var yInc = yMax / data.dataPoints.length;
var yPos = 0;
var yLabelInc = (maxYValue * ratio) / data.dataPoints.length;
var xInc = getXInc();
var xPos = margin.left;
for (var i = 0; i < data.dataPoints.length; i++) {
yPos += (i == 0) ? margin.top : yInc;
//Draw horizontal lines
drawLine(margin.left, yPos, xMax, yPos, '#E8E8E8');
//y axis labels
ctx.font = (data.dataPointFont != null) ? data.dataPointFont : '10pt Calibri';
var txt = Math.round(maxYValue - ((i == 0) ? 0 : yPos / ratio));
var txtSize = ctx.measureText(txt);
ctx.fillText(txt, margin.left - ((txtSize.width >= 14) ? txtSize.width : 10) - 7, yPos + 4);
//x axis labels
txt = data.dataPoints[i].x;
txtSize = ctx.measureText(txt);
ctx.fillText(txt, xPos, yMax + (margin.bottom / 3));
xPos += xInc;
}
//Vertical line
drawLine(margin.left, margin.top, margin.left, yMax, 'black');
//Horizontal Line
drawLine(margin.left, yMax, xMax, yMax, 'black');
};
var renderData = function(type) {
var xInc = getXInc();
var prevX = 0,
prevY = 0;
// addition
dataXY.length=0;
for (var i = 0; i < data.dataPoints.length; i++) {
var pt = data.dataPoints[i];
var ptY = (maxYValue - pt.y) * ratio;
if (ptY < margin.top) ptY = margin.top;
var ptX = (i * xInc) + margin.left;
if (i > 0 && type == renderType.lines) {
//Draw connecting lines
drawLine(ptX, ptY, prevX, prevY, '#309FB2', 2);
}
if (type == renderType.points) {
var radgrad = ctx.createRadialGradient(ptX, ptY, 8, ptX - 5, ptY - 5, 0);
radgrad.addColorStop(0, '#309FB2');
radgrad.addColorStop(0.9, 'White');
ctx.beginPath();
ctx.fillStyle = radgrad;
//Render circle
ctx.arc(ptX, ptY, 8, 0, 2 * Math.PI, false)
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#000';
ctx.stroke();
ctx.closePath();
// addition
dataXY.push({cx:ptX,cy:ptY,r:8,tipText:'Price='+pt.y+' on '+pt.x});
}
prevX = ptX;
prevY = ptY;
}
};
var getXInc = function() {
return Math.round(xMax / data.dataPoints.length) - 1;
};
var drawLine = function(startX, startY, endX, endY, strokeStyle, lineWidth) {
if (strokeStyle != null) ctx.strokeStyle = strokeStyle;
if (lineWidth != null) ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.stroke();
ctx.closePath();
};
return {
renderType: renderType,
render: render, // added comma
// addition
dataXY: dataXY
};
} ();
//
// end CanvasChart
/////////////////////////////
// addition
var CanvasTooltip=(function(){
// ctor
function CanvasTooltip(canvasId,targets){
var that=this;
this.$canvas=$("#"+canvasId);
this.targets=targets;
this.openTooltipIndex=-1;
// add tip elements
this.init();
this.$tip=$('#canvasTip').hide();
// eventing
this.offsetX,this.offsetY;
this.reOffset();
this.$canvas.on('mousemove',function(e){ that.handleMouseMove(e); });
$(window).on('scroll',function(e){ that.reOffset(); });
}
CanvasTooltip.prototype.reOffset=function(){
var BB=this.$canvas[0].getBoundingClientRect();
this.offsetX=BB.left;
this.offsetY=BB.top;
};
CanvasTooltip.prototype.init=function(){
this.$canvas.wrap('<div id="canvasTipWrapper"></div>');
$('#canvasTipWrapper').css({'position':'relative'});
$('<div id="canvasTip"></div>')
.css({
'position':'absolute',
'border':'1px solid gray',
'border-left-width':'7px',
'border-left-color':'maroon',
'margin-bottom':'20px',
'background':'rgba(255,255,255,1.00)',
'padding-left':'5px',
'padding-right':'5px',
'max-width':'150px'
})
.appendTo('#canvasTipWrapper');
};
CanvasTooltip.prototype.handleMouseMove =function(e){
e.preventDefault();
e.stopPropagation();
var mouseX=parseInt(e.clientX-this.offsetX);
var mouseY=parseInt(e.clientY-this.offsetY);
// hit-test each target
var hit=-1;
for(var i=0;i<this.targets.length;i++){
var t=this.targets[i];
if(t.cx){
var dx=mouseX-t.cx;
var dy=mouseY-t.cy;
var r=t.r;
if(dx*dx+dy*dy<r*r){
hit=i;
if(hit!==this.openTooltipIndex){
// configure & open the tooltip
this.openTooltipIndex=hit;
this.$tip
.text(t.tipText)
.css({left:t.cx,top:t.cy-t.r-10-this.$tip.height()})
.show();
}
}
}
}
// hide the tip if no target was hit
if(hit==-1 && this.openTooltipIndex!==-1){
this.$tip.hide();
this.openTooltipIndex=-1;
}
}
return(CanvasTooltip);
})(); // end CanvasTooltip
//
// end CanvasTooltip
////////////////////////////
var dataDef = { title: "US Population Chart",
xLabel: 'Date',
yLabel: 'Price',
labelFont: '19pt Arial',
dataPointFont: '10pt Arial',
renderTypes: [CanvasChart.renderType.lines, CanvasChart.renderType.points],
dataPoints: [{ x: '1-aug', y: 2100 },
{ x: '2-aug', y: 2000 },
{ x: '3-aug', y: 2200 },
{ x: '4-aug', y: 1800 },
{ x: '5-aug', y: 1500 },
{ x: '6-aug', y: 3000 },
]
};
CanvasChart.render('canvas', dataDef);
// addition
var tooltip=new CanvasTooltip('canvas',CanvasChart.dataXY);
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<canvas id="canvas" width=300 height=300></canvas>