Wie zeichne ich ein Wetterliniendiagramm im WeChat-Applet? Im folgenden Artikel erfahren Sie, wie Sie mit Canvas ein Wetterliniendiagramm im WeChat-Applet zeichnen und eine Bezier-Kurve dritter Ordnung verwenden, um die Temperaturpunkte anzupassen, um sie glatt zu machen und am unteren Rand der Kurve eine Hintergrundfarbe zu erhalten . Ich hoffe, es ist für alle hilfreich!
Rendering:
Benutzerdefiniertes Komponentenliniendiagramm
<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(); }, }, });
Daten sind das Temperaturarray, z. B. [1, 2, ...]
weil nein Wir wissen, wie viele Temperaturwerte es gibt, daher wird die Breite hier dynamisch übergeben
Es gibt ein kleines Problem: Wenn die Breite zu groß ist, zeigt die reale Maschine sie nicht an ...
// 获取 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>
Schreiben Sie hier scroll-x und scroll-y, sonst gibt es ein Problem mit dem absoluten Positionierungsversatz, und ich weiß nicht warum
.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; }
Die hier verwendete absolute Positionierung dient eigentlich dazu, den Effekt eines Liniendiagramms wie Moji zu simulieren Wetter und jeden Tag in einem Block, also stündlich, muss mit Scroll-Ansicht usw. kombiniert werden. Hoch muss die Leinwand positioniert werden
Hauptsächlich, weil ich nicht weiß, wie ich Moji Weather erreichen kann, also kann ich das nur vorübergehend tun
Rendering
emmm, es scheint, dass es nicht dasselbe ist. Nicht sehr glatt
Kontrollpunkte berechnen
Schreiben Sie zuerst a Punktklasse
class Point { constructor(x, y) { this.x = x; this.y = y; } }
Canvas Bezier-Kurven-Zeichenwerkzeug (karlew.com)
http://wx.karlew.com/canvas/bezier/
Über die obige Website können Sie die Bedeutung jedes Parameters dritter Ordnung erfahren Bezier-Kurve
Das heißt, bei Verwendung von bezierCurveTo ist der letzte Punkt der nächste Punkt und die ersten beiden sind Kontrollpunkte
Berechnung von Kontrollpunkten Referenz: Methode zur Bestimmung von Bezier-Kurven-Kontrollpunkten – Baidu-Bibliothek
https://wenku.baidu.com/view/c790f8d46bec0975f565e211.html
Kondensiert ist es
wobei a und b beliebige positive Zahlen sein können
Definieren Sie also eine Methode zur Berechnung der Kontrollpunkte A und B von ein bestimmter Punkt
/** * 计算当前点的贝塞尔曲线控制点 * @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 }; }
Hier ist die Skala a und b, aber machen Sie ihre Werte gleich
Aber der erste Punkt hat keinen previousPoint und der vorletzte Punkt hat keinen nextPoint2
Wenn der Punkt also der erste ist, Verwenden Sie currentPoint anstelle von previousPoint
Wenn es der vorletzte Punkt ist, verwenden Sie nextPoint1 anstelle von nextPoint2
Was den letzten Punkt betrifft, müssen Sie nichts tun, da der dritte Parameter von bezierCurveTo der nächste Punkt ist. Sie müssen nur Koordinaten angeben, um eine Verbindung herzustellen. Es ist also nicht erforderlich, Kontrollpunkte zu berechnen:
/** * 绘制贝塞尔曲线 * 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(); },
[Verwandte Lernempfehlungen:
Mini-Tutorial zur ProgrammentwicklungDas obige ist der detaillierte Inhalt vonBringen Sie Schritt für Schritt bei, wie Sie mit Canvas ein Wetterliniendiagramm im WeChat-Applet zeichnen (mit Code).. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!