在微信小程序中如何使用canvas绘制天气折线图(微信小程序做天气应用)
折线
效果图:
自定义组件 line-chart
canvastype="2d"id="line"class="line-class"style="width:{ { width} } px; height:{ { height} } px"/>
Component({ externalClasses:['line-class'], properties:{ width:String, height:String, data:Array, } , observers:{ width(){ //这里监听width变化重绘canvas //动态传入width好像只能这样了.. constquery=this.createSelectorQuery(); query .select('#line') .fields({ node:true,size:true} ) .exec(res=> { constcanvas=res[0].node; constctx=canvas.getContext('2d'); constwidth=res[0].width; //画布宽度 constheight=res[0].height; //画布高度 console.log(`宽度:${ width} ,高度:${ height} `); constdpr=wx.getSystemInfoSync().pixelRatio; canvas.width=width*dpr; canvas.height=height*dpr; ctx.scale(dpr,dpr); //开始绘图 this.drawLine(ctx,width,height,this.data.data); } ); } , } , methods:{ drawLine(ctx,width,height,data){ constMax=Math.max(...data); constMin=Math.min(...data); //把canvas的宽度,高度按一定规则平分 conststartX=width/(data.length*2),//起始点的横坐标X baseY=height*0.9,//基线纵坐标Y diffX=width/data.length, diffY=(height*0.7)/(Max-Min); //高度预留0.2写温度 ctx.beginPath(); ctx.textAlign='center'; ctx.font='13pxMicrosoftYaHei'; ctx.lineWidth=2; ctx.strokeStyle='#ABDCFF'; //画折线图的线 data.forEach((item,index)=> { constx=startX+diffX*index, y=baseY-(item-Min)*diffY; ctx.fillText(`${ item} °`,x,y-10); ctx.lineTo(x,y); } ); ctx.stroke(); //画折线图背景 ctx.lineTo(startX+(data.length-1)*diffX,baseY); //基线终点 ctx.lineTo(startX,baseY); //基线起点 constlingrad=ctx.createLinearGradient(0,0,0,height*0.7); lingrad.addColorStop(0,'rgba(255,255,255,0.9)'); lingrad.addColorStop(1,'rgba(171,220,255,0)'); ctx.fillStyle=lingrad; ctx.fill(); //画折线图上的小圆点 ctx.beginPath(); data.forEach((item,index)=> { constx=startX+diffX*index, y=baseY-(item-Min)*diffY; ctx.moveTo(x,y); ctx.arc(x,y,3,0,2*Math.PI); } ); ctx.fillStyle='#0396FF'; ctx.fill(); } , } , } );
data 就是温度数组,如 [1, 2, ...]
因为不知道温度数值有多少个,因此这里的 width 动态传入
有个小问题,就是宽度过大的话真机不会显示...
//获取scroll-view的总宽度 wx.createSelectorQuery() .select('.hourly') .boundingClientRect(rect=> { this.setData({ scrollWidth:rect.right-rect.left, } ); } ) .exec();
viewclass="title"> 小时概述/view> scroll-viewscroll-xscroll-yclass="scroll"show-scrollbar="{ { false} } "enhanced="{ { true} } "> viewclass="hourly"> viewwx:for="{ { time} } "wx:key="index"> { { item} } /view> /view> line-chartline-class="line"width="{ { scrollWidth} } "height="100"data="{ { temp} } "/> /scroll-view>
这里写 scroll-x 和 scroll-y,要不会出现绝对定位偏移的问题,也不知道为什么
.scroll{ position:relative; height:150px; width:100%; } .hourly{ display:flex; height:150px; position:absolute; top:0; } .hourly> view{ min-width:3.5em; text-align:center; } .line{ //折线图绝对定位到底部 position:absolute; bottom:0; }
这里使用绝对定位其实是想模拟墨迹天气这种折线图和每一天在一个块内的效果,所以 hourly 要和 scroll-view 等高,canvas 需要定位一下
主要是不知道墨迹天气怎么实现的,只能暂时这样
三阶贝塞尔曲线
效果图
emmm,好像并不怎么圆滑
计算控制点
首先写一个点类
classPoint{ constructor(x,y){ this.x=x; this.y=y; } }
也就是使用 bezierCurveTo 的时候最后一个点是下一个点,前两个是控制点
浓缩一下就是
这里的 a 和 b 可以是任意正数
因此定义一个计算某点的控制点 A 和 B 的方法
/** *计算当前点的贝塞尔曲线控制点 *@param{ Point} previousPoint:前一个点 *@param{ Point} currentPoint:当前点 *@param{ Point} nextPoint1:下一个点 *@param{ Point} nextPoint2:下下个点 *@param{ Number} scale:系数 */ calcBezierControlPoints( previousPoint, currentPoint, nextPoint1, nextPoint2, scale=0.25 ){ letx=currentPoint.x+scale*(nextPoint1.x-previousPoint.x); lety=currentPoint.y+scale*(nextPoint1.y-previousPoint.y); constcontrolPointA=newPoint(x,y); //控制点A x=nextPoint1.x-scale*(nextPoint2.x-currentPoint.x); y=nextPoint1.y-scale*(nextPoint2.y-currentPoint.y); constcontrolPointB=newPoint(x,y); //控制点B return{ controlPointA,controlPointB} ; }
这里 scale 就是 a 和 b,不过将它们的取值相等
但是第一个点没有 previousPoint,倒数第二个点没有 nextPoint2
因此当点是第一个的时候,使用 currentPoint 代替 previousPoint
当倒数第二个点的时候,使用 nextPoint1 代替 nextPoint2
至于最后一个点,不需要做任何事,因为 bezierCurveTo 第三个参数就是下一个点,只需要提供坐标就能连起来,不需要计算控制点
因此绘制三阶贝塞尔曲线的方法:
/** *绘制贝塞尔曲线 *ctx.bezierCurveTo(控制点1,控制点2,当前点); */ drawBezierLine(ctx,data,options){ const{ startX,diffX,baseY,diffY,Min} =options; ctx.beginPath(); //先移动到第一个点 ctx.moveTo(startX,baseY-(data[0]-Min)*diffY); data.forEach((e,i)=> { letcurPoint,prePoint,nextPoint1,nextPoint2,x,y; //当前点 x=startX+diffX*i; y=baseY-(e-Min)*diffY; curPoint=newPoint(x,y); //前一个点 x=startX+diffX*(i-1); y=baseY-(data[i-1]-Min)*diffY; prePoint=newPoint(x,y); //下一个点 x=startX+diffX*(i+1); y=baseY-(data[i+1]-Min)*diffY; nextPoint1=newPoint(x,y); //下下个点 x=startX+diffX*(i+2); y=baseY-(data[i+2]-Min)*diffY; nextPoint2=newPoint(x,y); if(i===0){ //如果是第一个点,则前一个点用当前点代替 prePoint=curPoint; } elseif(i===data.length-2){ //如果是倒数第二个点,则下下个点用下一个点代替 nextPoint2=nextPoint1; } elseif(i===data.length-1){ //最后一个点直接退出 return; } const{ controlPointA,controlPointB} =this.calcBezierControlPoints( prePoint, curPoint, nextPoint1, nextPoint2 ); ctx.bezierCurveTo( controlPointA.x, controlPointA.y, controlPointB.x, controlPointB.y, nextPoint1.x, nextPoint1.y ); } ); ctx.stroke(); } ,
以上就是“在微信小程序中如何使用canvas绘制天气折线图”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注行业资讯频道。
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: 在微信小程序中如何使用canvas绘制天气折线图(微信小程序做天气应用)
本文地址: https://pptw.com/jishu/727.html