欧美亚洲中文,在线国自产视频,欧洲一区在线观看视频,亚洲综合中文字幕在线观看

      1. <dfn id="rfwes"></dfn>
          <object id="rfwes"></object>
        1. 站長資訊網(wǎng)
          最全最豐富的資訊網(wǎng)站

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          微信小程序中如何繪制天氣折線圖?下面本篇文章就來給大家介紹一下在微信小程序中使用canvas繪制天氣折線圖的方法,以及使用三階貝塞爾曲線擬合溫度點,使之變得圓滑,曲線底部有背景色,希望對大家有所幫助!

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          折線

          效果圖:

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          自定義組件 line-chart

          <canvas type="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() {       // 這里監(jiān)聽 width 變化重繪 canvas       // 動態(tài)傳入 width 好像只能這樣了..       const query = this.createSelectorQuery();       query         .select('#line')         .fields({ node: true, size: true })         .exec(res => {           const canvas = res[0].node;           const ctx = canvas.getContext('2d');           const width = res[0].width; // 畫布寬度           const height = res[0].height; // 畫布高度            console.log(`寬度: ${width}, 高度: ${height}`);            const dpr = 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) {       const Max = Math.max(...data);       const Min = Math.min(...data);        // 把 canvas 的寬度, 高度按一定規(guī)則平分       const startX = 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 = '13px Microsoft YaHei';       ctx.lineWidth = 2;       ctx.strokeStyle = '#ABDCFF';        // 畫折線圖的線       data.forEach((item, index) => {         const x = 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); // 基線起點       const lingrad = 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) => {         const x = 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 就是溫度數(shù)組,如 [1, 2, …]

          因為不知道溫度數(shù)值有多少個,因此這里的 width 動態(tài)傳入

          有個小問題,就是寬度過大的話真機不會顯示…

           // 獲取 scroll-view 的總寬度  wx.createSelectorQuery()       .select('.hourly')       .boundingClientRect(rect => {         this.setData({           scrollWidth: rect.right - rect.left,         });       })       .exec();
          <view class="title">小時概述</view> <scroll-view scroll-x scroll-y class="scroll" show-scrollbar="{{false}}" enhanced="{{true}}">     <view class="hourly">       <view wx:for="{{time}}" wx:key="index">{{item}}</view>     </view>     <line-chart line-class="line" width="{{scrollWidth}}" height="100" data="{{temp}}" /> </scroll-view>

          這里寫 scroll-x 和 scroll-y,要不會出現(xiàn)絕對定位偏移的問題,也不知道為什么

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          .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; }

          這里使用絕對定位其實是想模擬墨跡天氣這種折線圖和每一天在一個塊內(nèi)的效果,所以 hourly 要和 scroll-view 等高,canvas 需要定位一下

          主要是不知道墨跡天氣怎么實現(xiàn)的,只能暫時這樣

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          三階貝塞爾曲線

          效果圖

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          emmm,好像并不怎么圓滑

          計算控制點

          首先寫一個點類

          class Point {   constructor(x, y) {     this.x = x;     this.y = y;   } }

          Canvas貝塞爾曲線繪制工具 (karlew.com)

          http://wx.karlew.com/canvas/bezier/

          通過上面這個網(wǎng)站可以知道三階貝塞爾曲線各個參數(shù)的意義

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          也就是使用 bezierCurveTo 的時候最后一個點是下一個點,前兩個是控制點

          控制點的計算參考: 貝塞爾曲線控制點確定的方法 – 百度文庫

          https://wenku.baidu.com/view/c790f8d46bec0975f565e211.html

          濃縮一下就是

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          這里的 a 和 b 可以是任意正數(shù)

          因此定義一個計算某點的控制點 A 和 B 的方法

          /**  * 計算當前點的貝塞爾曲線控制點  * @param {Point} previousPoint: 前一個點  * @param {Point} currentPoint: 當前點  * @param {Point} nextPoint1: 下一個點  * @param {Point} nextPoint2: 下下個點  * @param {Number} scale: 系數(shù)  */ calcBezierControlPoints(   previousPoint,   currentPoint,   nextPoint1,   nextPoint2,   scale = 0.25 ) {   let x = currentPoint.x + scale * (nextPoint1.x - previousPoint.x);   let y = currentPoint.y + scale * (nextPoint1.y - previousPoint.y);    const controlPointA = new Point(x, y); // 控制點 A    x = nextPoint1.x - scale * (nextPoint2.x - currentPoint.x);   y = nextPoint1.y - scale * (nextPoint2.y - currentPoint.y);    const controlPointB = new Point(x, y); // 控制點 B    return { controlPointA, controlPointB }; }

          這里 scale 就是 a 和 b,不過將它們的取值相等

          但是第一個點沒有 previousPoint,倒數(shù)第二個點沒有 nextPoint2

          因此當點是第一個的時候,使用 currentPoint 代替 previousPoint

          當?shù)箶?shù)第二個點的時候,使用 nextPoint1 代替 nextPoint2

          手把手教你在微信小程序中使用canvas繪制天氣折線圖(附代碼)

          至于最后一個點,不需要做任何事,因為 bezierCurveTo 第三個參數(shù)就是下一個點,只需要提供坐標就能連起來,不需要計算控制點

          因此繪制三階貝塞爾曲線的方法:

          /**  * 繪制貝塞爾曲線  * 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) => {     let curPoint, prePoint, nextPoint1, nextPoint2, x, y;      // 當前點     x = startX + diffX * i;     y = baseY - (e - Min) * diffY;     curPoint = new Point(x, y);      // 前一個點     x = startX + diffX * (i - 1);     y = baseY - (data[i - 1] - Min) * diffY;     prePoint = new Point(x, y);      // 下一個點     x = startX + diffX * (i + 1);     y = baseY - (data[i + 1] - Min) * diffY;     nextPoint1 = new Point(x, y);      // 下下個點     x = startX + diffX * (i + 2);     y = baseY - (data[i + 2] - Min) * diffY;     nextPoint2 = new Point(x, y);      if (i === 0) {       // 如果是第一個點, 則前一個點用當前點代替       prePoint = curPoint;     } else if (i === data.length - 2) {       // 如果是倒數(shù)第二個點, 則下下個點用下一個點代替       nextPoint2 = nextPoint1;     } else if (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(); },

          【相關(guān)學習推薦:小程序開發(fā)教程】

          贊(0)
          分享到: 更多 (0)
          網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號