您好,登錄后才能下訂單哦!
今天小編給大家分享一下在微信小程序中如何使用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() { // 這里監聽 width 變化重繪 canvas // 動態傳入 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 的寬度, 高度按一定規則平分 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 就是溫度數組,如 [1, 2, ...]
因為不知道溫度數值有多少個,因此這里的 width 動態傳入
有個小問題,就是寬度過大的話真機不會顯示...
// 獲取 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,要不會出現絕對定位偏移的問題,也不知道為什么
.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,好像并不怎么圓滑
計算控制點
首先寫一個點類
class Point { 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 ) { 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,倒數第二個點沒有 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) => { 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) { // 如果是倒數第二個點, 則下下個點用下一個點代替 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(); },
以上就是“在微信小程序中如何使用canvas繪制天氣折線圖”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。