Canvas 绘制图表,滑动加载数据和显示(一)

mac2024-05-22  38

Canvas 绘制图表(折线图)

HTML 5 Canvas 参考手册 : https://www.w3school.com.cn/tags/html_ref_canvas.asp HTML 事件属性: https://www.w3school.com.cn/tags/html_ref_eventattributes.asp

使用Canvas 绘制图像,通常是用脚本(JS)实现的。 对于每一次显示移动或者点击显示提示框的,我的策略是清除画布,重新绘制,在浏览器上测试还行,(不知道在手机上加载会不会有异常,耗费资源或者内存较多)。

首先,第一步确定,canvas区域的大小,(即宽高),这时,宽高要在写在canvas标签里,不能定义style或者css, 否则的话,会造成变形; 设置宽为移动端的宽度的100%,可以在页面在window.onload 里JS动态获取移动端宽度,然后赋值给canvas标签的宽度。

canvas画布的宽高应写在标签里 : https://www.cnblogs.com/duanhuajian/archive/2012/10/15/2724244.html

然后,绘制坐标轴和基准线,(我的是Y轴固定刻度,所以我就绘制基准线的时候,也把Y轴数值写上了),还有Y轴的单位(或标题);

然后,计算点的位置,绘制坐标点,(每屏最多显示5个数据点); 然后,连线; 然后,canvas 定义 touch事件,使整个图表数据的坐标点根据触摸移动距离进行坐标点的改变(x坐标值),使不能显示的点根据你的移动显示出来,并且移动的时候请求分页接口(这个可以做个处理,根据移动距离的限制来加载更多数据); 然后,canvas 定义 onmousedown事件,点击坐标点的一定范围内,绘制该坐标点的提示框,

在进行滑动和点击操作的时候,清除画布,重新绘制,要不然会出现绘制重叠现象。

***还有个不太好的点是,滑动不太顺畅,有时会有点卡顿,这个还有待解决,后续整好了再一篇后记。

代码奉上:

// html <div class="canvas-container" id='canvas'> <canvas id="chartsCanvas" width=0 height=350 onmousedown="cnvs_getCoordinates(event)" ontouchstart="touchStartEvent(event)" ontouchend="touchEndEvent(event)" ></canvas> </div> //JS code var datalist = [ {time: 1579992000000 , bmp:60}, {time: 1578984000000 , bmp:120}, {time: 1577973000000 , bmp:110}, {time: 1576962000000 , bmp:200}, {time: 1575951000000 , bmp:80}, ] // y轴数据刻度 var yAxisdata = [ 0,60,120,180,240,300 ] var yAxisTickValue = [] var c, ctx, width , height var padding window.onload = function loadEvent() { // offsetHeight, offsetWidth c = document.getElementById("chartsCanvas"); ctx = c.getContext("2d"); c.width = document.body.clientWidth; //canvas的宽度自适应屏幕宽度100% width = c.width; height = c.height; padding = 40; baseline(yAxisdata) drawCoordinatePoints(datalist, 0) } // 清除画布 function clearCanvas() { ctx.clearRect(0,0,c.width,c.height); } // 绘制y轴的数据的基准线 function baseline(data) { ctx.textAlign = 'left'; ctx.fillStyle='#333E47' ctx.font="14px sans-serif"; ctx.fillText("BMP", padding - 25, padding); //在位置 y=data 绘制基准线, Math.max.apply(null, data) , ctx.strokeStyle="#F5F5F5"; // ctx.strokeStyle="#B3B8BB"; yAxisTickValue = [] for (var i = 0 ; i < data.length ; i++) { var yheight = (Math.max.apply(null, data) - 2*padding) / (data.length -1) *i + padding*2 // console.log('yheight',yheight) yAxisTickValue.push(yheight) ctx.lineWidth= 1; ctx.beginPath(); ctx.moveTo(padding, yheight); ctx.lineTo(width-padding, yheight); ctx.stroke(); ctx.textAlign = 'left'; ctx.fillStyle='#B3B8BB' ctx.font="10px sans-serif"; ctx.fillText(data[data.length-1 - i], padding - 25, yheight+2); } } var pointsList = [] // 画坐标点 function drawCoordinatePoints(list, moveX) { pointsList = [] var yMax = Math.max.apply(null, yAxisTickValue) for(var i=0; i<list.length ; i++) { ctx.fillStyle="#2ECD70"; // 计算y轴坐标点 var yValue = yMax - list[i].bmp * ((Math.max.apply(null, yAxisdata) - 2*padding) / (yAxisdata.length -1))/60 // 计算x轴坐标点: 最多显示5个点 var xValue = (width-padding) - ((width-padding*2)/4)*(4-i) - moveX ctx.beginPath(); ctx.arc(xValue,yValue,4,0,Math.PI*2,true); // console.log('yValue',yMax - list[i].bmp) ctx.closePath(); ctx.fill(); pointsList.push({xdata: xValue, ydata: yValue , bmp:list[i].bmp }) ctx.textAlign = 'center'; ctx.fillStyle='#B3B8BB'; ctx.font="10px sans-serif"; ctx.fillText( dateFormat(list[i].time,'MM/DD HH:mm') , xValue, yMax+30); } drawLine(pointsList) } // 连接两点 function drawLine(list) { ctx.setLineDash([0]); ctx.strokeStyle="#2ECD70"; ctx.lineWidth= 3; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(list[0].xdata, list[0].ydata); // ctx.lineTo(list[1].xdata, list[1].xdata); for(var i=1; i<list.length ; i++) { ctx.lineTo(list[i].xdata, list[i].ydata); } ctx.stroke(); ctx.closePath(); ctx.save(); } var imgData; // 画布的触摸事件 function cnvs_getCoordinates(e){ x=e.clientX; y=e.clientY; for (var i=0;i<pointsList.length;i++){ if(e.offsetX > (pointsList[i].xdata-10) && e.offsetX < (pointsList[i].xdata+10) && e.offsetY > (pointsList[i].ydata-10) && e.offsetY < (pointsList[i].ydata+10) ){ // location.reload( true ) clearCanvas() baseline(yAxisdata) drawCoordinatePoints(datalist, 0) drawValueBox(pointsList[i]) } } } // 点击绘制的坐标点,显示虚线和数值框 function drawValueBox(point) { ctx.setLineDash([4]); ctx.lineWidth = 1; ctx.strokeStyle = '#F36176'; ctx.beginPath(); ctx.moveTo(point.xdata, Math.max.apply(null, yAxisTickValue)); ctx.lineTo(point.xdata, Math.min.apply(null, yAxisTickValue)-5); ctx.stroke(); ctx.closePath(); ctx.fillStyle="#2ECD70"; ctx.fillRect(point.xdata-20,Math.min.apply(null, yAxisTickValue)-30,40,20); ctx.textAlign = 'center'; ctx.fillStyle='#fff'; ctx.font="10px sans-serif"; ctx.fillText( point.bmp , point.xdata, Math.min.apply(null, yAxisTickValue)-15); } var startx = 0 // touch事件 function touchStartEvent(e) { startx = e.touches[0].clientX // console.log('startx', e.touches[0].clientX) } function touchEndEvent(e) { // console.log('endx', e.changedTouches[0].clientX) var touchX = startx - e.changedTouches[0].clientX console.log('touchX',touchX) if(datalist.length < 10) { datalist.push({time: 1579992000000 , bmp:60}) } if(e.changedTouches[0].clientX >= padding && e.changedTouches[0].clientX <= (width-padding) ) { // 滑动绘制还不是很流畅 clearCanvas() baseline(yAxisdata) drawCoordinatePoints(datalist, touchX) } } //日期转换格式---(yyyy-MM-dd hh:mm:ss格式转换成其他格式) function dateFormat(date,format){ if(date){ date = new Date(date); var year = date.getFullYear(); var day = date.getDate() < 10 ? '0'+date.getDate() : date.getDate(); var month = date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1; var minutes = (Array(2).join(0) + date.getMinutes()).slice(-2); var hours = (Array(2).join(0) + date.getHours()).slice(-2); var secondes = (Array(2).join(0) + date.getSeconds()).slice(-2); var arrMonthChinese = ['一','二','三','四','五','六','七','八','九','十','十一','十二'] var monthChinese = arrMonthChinese[date.getMonth()] switch(format) { case 'year': return year + '年'; // break; case 'month': return monthChinese + '月'; case 'year_month': return year + '-' + month; case 'yyyy-MM-dd': return year + '-' + month+ '-' + day; case 'h_m_s': return {hours: hours, minutes: minutes, secondes: secondes }; case 'month_day': return month + '月' + day + '日'; case 'HH:mm': return hours + ':' + minutes; case 'MM/DD HH:mm': return month + '/' + day + ' ' + hours + ':' + minutes; case '/': return year + '/' + month + '/' + day + ' ' + hours + ':' + minutes ; default: return year + '年' + month + '月' + day + '日' + ' ' + hours + ':' + minutes ; } } }
最新回复(0)