效果图如下:
一.思路
折线的实现可以分为两种情况:
1.少于七个点
对于少于7个点的情况,主要是描点,画线,标注三部分
2.大于等于七个点
对于大于等于七个点的情况,主要是画线,标注极点
二.实现要点
最重要的是每个点位置的确定
<1>对数据进行处理
首先对数据进行处理
A.将空数组填充一定的内容
var noData= chartEntity.originArr.length==0?true:false;
//小于7的时候是7天数据,补全后面数据
var originArr2=[];
if(noData){
originArr2=[50,50,50,50,50,50,50];
}else{
originArr2=chartEntity.originArr.concat();
}
B.将少于7个点的数据补充为7个点,补充值为原数组最后一个数据
//补充数据
while(originArr2.length < 7){
originArr2.push(chartEntity.originArr[chartEntity.originArr.length-1]);
}
<2>极点的确定
因为是以最低点,最高点定边界,所以首先要先确定极点的值以及极点之间的差值
var canScale=window.devicePixelRatio;
//获得数组最大值
var maxWeight= chartEntity.originArr.length == 0 ? originArr2[0]:Math.max.apply(null, chartEntity.originArr);
//获得数组最大值的下标
var maxWeightIndex=chartEntity.originArr.lastIndexOf(maxWeight);
//获得数组最小值
var minWeight= chartEntity.originArr.length == 0 ? originArr2[0]:Math.min.apply(null, chartEntity.originArr);
//获得数组最小值的下标
var minWeightIndex=chartEntity.originArr.lastIndexOf(minWeight);
//获得最大值最小值差值
var weightDef= maxWeight-minWeight;
<3>各个点位置的确定
//最大值最小值一样
if(weightDef == 0){
weightDef = 1;
dateIsSame=true;
}
//初始坐标数据处理
for(var i=0;i<originArr2.length;i++){
hPercentArr[i] = (maxWeight-originArr2[i])/weightDef*chartEntity.waveHeight*canScale+minHeight;
wPercentArr[i] = dateArr[i]*($("#"+chartEntity.chartID).width()-15)*canScale+15;
console.info(hPercentArr[i]+"||"+wPercentArr[i]);
}
var diffWidth=(wPercentArr[1]-wPercentArr[0])/canScale;
<4>初始化画布
//画布初始化
var canvas=document.getElementById(chartEntity.chartID);
var context=canvas.getContext("2d");
canvas.width=$("#"+chartEntity.chartID).width()*canScale;
canvas.height=$("#"+chartEntity.chartID).height()*canScale;
<5>描线,标注
//画线
for(var i=0;i<originArr2.length;i++){
if(dateIsSame){
$("#"+chartEntity.chartID).css("marginTop","1.75rem");
}
context.lineTo(wPercentArr[i],hPercentArr[i]);
if(i < chartEntity.originArr.length){
if(dateIsSame){
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hPercentArr[i]/canScale-fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-13)+"px;'>"+chartEntity.originArr[i]+"</div>");
}else{
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hPercentArr[i]/canScale-1.5*fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-16)+"px;'>"+chartEntity.originArr[i]+"</div>");
}
}
}
if(chartEntity.originArr.length < 7){
context.strokeStyle = chartEntity.lineStrokeColor2;
}else{
context.strokeStyle = chartEntity.lineStrokeColor;
}
context.lineWidth = 1*canScale;
//绘制路径
context.stroke();
<6>标点
//描点
for(var i=0;i<originArr2.length;i++){
context.lineWidth=1*canScale;
context.beginPath();
context.arc(wPercentArr[i],hPercentArr[i],4*canScale,0,2*Math.PI);
context.closePath();
context.fillStyle= chartEntity.dotFillColor1;
context.fill();
context.beginPath();
context.arc(wPercentArr[i],hPercentArr[i],2*canScale,0,2*Math.PI);
context.closePath();
if(i < chartEntity.originArr.length){
context.fillStyle= chartEntity.dotFillColor2;
}else{
context.fillStyle= chartEntity.dotFillColor3;
}
context.fill();
}
三.完整代码
<!DOCTYPE html>
<html>
<head>
<title>canvas实现折线图</title>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
<!-- 引入 ECharts 文件 -->
<script src="js/echarts.js"></script>
<script src="js/jquery-3.0.0.min.js"></script>
</head>
<style type="text/css">
.chartBottom {
margin: 1rem 1.25rem;
margin-bottom: 0;
height: 6.25rem;
position: relative;
/* border: 1px solid #1daffa; */
border: 1px solid rgba(29, 175, 250, 0.5);
border-top: none;
background-color: #fff;
border-right: none;
}
.line {
width: 80%;
margin-left: 10%;
margin-top: .75rem;
}
.lineNum {
position: absolute;
top: 0rem;
left: 0;
width: 88%;
margin-left: 12%;
font-size: .5625rem;
color: #1daffa;
}
</style>
<body>
<div class="row chartBottom weightContent">
<canvas class="line" id="weight" width="100px" height="100px"></canvas>
<div id="weightNum" class="lineNum"></div>
</div>
</body>
<script type="text/javascript">
//初始化echarts实例
/*画图区域--开始*/
$(".line").css("height",$(window).width()/750*120);
$(".lineNum").css("height",$(window).width()/750*158);
/*$(".chartBottom").css("width","100%");*/
$("#weight").css("width",$(".chartBottom").width()*0.8);
$("#fat").css("width",$(".chartBottom").width()*0.8);
$("#waist").css("width",$(".chartBottom").width()*0.8);
var weight=[55.4,55.9,53];
//var weight=[55.4,55.9,53,56,57,58,56.7,53,55,54,53];
//体重折线图
var weightEntity={
"originArr":weight, //初始数组
"chartID":"weight", //折线canvas的id名称
"chartNumID":"weightNum", //折线点的区域的名称
"lineFillColor":"#c1e9fb", //折线填充颜色
"lineStrokeColor":"#1daffa", //折线颜色
"lineStrokeColor2":"#d2effe", //折线颜色(数据时不满足7个时)
"dotFillColor1":"#d2effe", //折线点的外圈颜色
"dotFillColor2":"#1daffa", //折线点的内圈颜色
"dotFillColor3":"#fff", //折线点的内圈颜色(没有数据时)
"waveHeight":(2*parseFloat($("html").css("font-size")))
};
getLineChart(weightEntity);
function getLineChart(chartEntity){
$("#"+chartEntity.chartNumID).empty();
console.info(chartEntity);
var fontSize=parseFloat($("html").css("font-size"));
console.info(fontSize);
var dateIsSame=false;
var noData= chartEntity.originArr.length==0?true:false;
var hPercentArr=[];
var wPercentArr=[];
//小于7的时候是7天数据,补全后面数据
var originArr2=[];
if(noData){
originArr2=[50,50,50,50,50,50,50];
}else{
originArr2=chartEntity.originArr.concat();
}
//补充数据
while(originArr2.length < 7){
originArr2.push(chartEntity.originArr[chartEntity.originArr.length-1]);
}
console.info(originArr2);
var canScale=window.devicePixelRatio;
//获得数组最大值
var maxWeight= chartEntity.originArr.length == 0 ? originArr2[0]:Math.max.apply(null, chartEntity.originArr);
//获得数组最大值的下标
var maxWeightIndex=chartEntity.originArr.lastIndexOf(maxWeight);
//获得数组最小值
var minWeight= chartEntity.originArr.length == 0 ? originArr2[0]:Math.min.apply(null, chartEntity.originArr);
//获得数组最小值的下标
var minWeightIndex=chartEntity.originArr.lastIndexOf(minWeight);
//获得最大值最小值差值
var weightDef= maxWeight-minWeight;
console.info(maxWeight+"||"+minWeight);
var minHeight=2*fontSize;
//获取每天间隔
var dateArr=[];
var date=0;
var dateLength=0;
if(chartEntity.originArr.length > 7){
dateLength=chartEntity.originArr.length;
}else{
dateLength=7;
}
for(var i=0;i<dateLength;i++){
if(i == 0){
dateArr.push(0);
}else{
date+=1/(dateLength-1);
dateArr.push(date);
}
}
console.info(dateArr);
console.info(chartEntity.waveHeight);
//最大值最小值一样
if(weightDef == 0){
weightDef = 1;
dateIsSame=true;
}
console.info("--------"+$("#"+chartEntity.chartID).width());
//初始坐标数据处理
for(var i=0;i<originArr2.length;i++){
hPercentArr[i] = (maxWeight-originArr2[i])/weightDef*chartEntity.waveHeight*canScale+minHeight;
wPercentArr[i] = dateArr[i]*($("#"+chartEntity.chartID).width()-15)*canScale+15;
console.info(hPercentArr[i]+"||"+wPercentArr[i]);
}
var diffWidth=(wPercentArr[1]-wPercentArr[0])/canScale;
//画布初始化
var canvas=document.getElementById(chartEntity.chartID);
var context=canvas.getContext("2d");
canvas.width=$("#"+chartEntity.chartID).width()*canScale;
canvas.height=$("#"+chartEntity.chartID).height()*canScale;
//小于7个点
if(chartEntity.originArr.length <= 7){
console.info("originArr2.length"+originArr2.length);
//画线
for(var i=0;i<originArr2.length;i++){
if(dateIsSame){
$("#"+chartEntity.chartID).css("marginTop","1.75rem");
}
context.lineTo(wPercentArr[i],hPercentArr[i]);
if(i < chartEntity.originArr.length){
if(dateIsSame){
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hPercentArr[i]/canScale-fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-13)+"px;'>"+chartEntity.originArr[i]+"</div>");
}else{
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hPercentArr[i]/canScale-1.5*fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-16)+"px;'>"+chartEntity.originArr[i]+"</div>");
}
}
}
//alert($("#weightNum").children("div:eq("+maxWeightIndex+")").attr("style")+"("+maxWeightIndex+")");
if(chartEntity.originArr.length < 7){
context.strokeStyle = chartEntity.lineStrokeColor2;
}else{
context.strokeStyle = chartEntity.lineStrokeColor;
}
context.lineWidth = 1*canScale;
//绘制路径
context.stroke();
//描点
for(var i=0;i<originArr2.length;i++){
context.lineWidth=1*canScale;
context.beginPath();
context.arc(wPercentArr[i],hPercentArr[i],4*canScale,0,2*Math.PI);
context.closePath();
context.fillStyle= chartEntity.dotFillColor1;
context.fill();
context.beginPath();
context.arc(wPercentArr[i],hPercentArr[i],2*canScale,0,2*Math.PI);
context.closePath();
if(i < chartEntity.originArr.length){
context.fillStyle= chartEntity.dotFillColor2;
}else{
context.fillStyle= chartEntity.dotFillColor3;
}
context.fill();
}
}else{
for(var i=0;i<chartEntity.originArr.length;i++){
context.lineTo(wPercentArr[i],hPercentArr[i]);
var hNumPosition=0;
//用户数据不变
if(dateIsSame){
$("#"+chartEntity.chartID).css("marginTop","1.75rem");
if(i == chartEntity.originArr.length-1){
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hPercentArr[i]/canScale-fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-10)+"px;'>"+chartEntity.originArr[i]+"</div>");
}
}else{
//用户数据变化
hNumPosition=hPercentArr[i]/canScale;
if(i == maxWeightIndex){
//最高点
if((chartEntity.originArr.length-1-maxWeightIndex)*diffWidth <= 24){
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hNumPosition-1.5*fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[chartEntity.originArr.length-1]/canScale-10-24)+"px;'>"+chartEntity.originArr[i]+"</div>");
}else{
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hNumPosition-1.5*fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-10)+"px;'>"+chartEntity.originArr[i]+"</div>");
}
}else if(i == minWeightIndex){
//最低点
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hNumPosition+0.5*fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-16)+"px;'>"+chartEntity.originArr[i]+"</div>");
}
//最后一点
if(i == chartEntity.originArr.length-1 && minWeight != chartEntity.originArr[chartEntity.originArr.length-1] && maxWeight != chartEntity.originArr[chartEntity.originArr.length-1]){
//当前一个点比最后一个点高,这时候数据应该展示在折线下方
if(hPercentArr[i] > hPercentArr[i-1]){
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hNumPosition+0.5*fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-10)+"px;'>"+chartEntity.originArr[i]+"</div>");
}else{
$("#"+chartEntity.chartNumID).append("<div class='weightNum-item' style='position:absolute;top:"+(hNumPosition-1.5*fontSize+0.75*fontSize)+"px;left:"+(wPercentArr[i]/canScale-10)+"px;'>"+chartEntity.originArr[i]+"</div>");
}
}
}
}
//alert($("#weightNum").children("div:eq("+maxWeightIndex+")").attr("style")+"("+maxWeightIndex+")");
console.info(chartEntity.lineStrokeColor);
context.strokeStyle = chartEntity.lineStrokeColor;
context.lineWidth = 1*canScale;
context.stroke();
}
}
</script>
</html>