小程序 - 折線圖畫法

折線圖的原理其實很簡單:選定兩個座標點,調用moveTo()和lineTo()方法畫出直線,多個點連續連線也是一樣,一個一個點連接,但moveTo()是最開始的座標執行,後面只調用lineTo()即可。

效果圖:

首先是XML佈局

<!-- 折線圖 -->
<view class="canvas-view">
    <canvas class="canvas" canvas-id="canvasId"></canvas>
</view>

樣式CSS

/* 折線圖 */
.canvas-view {
    height: 100%;
    background: #FFFFFF;
    display: flex;
    align-items: center;
    margin-top: 48rpx;
}
.canvas{
    width: 100%;
    height: 640rpx;
}

JS代碼

主要函數介紹:

1. getEleWidth():獲取屏幕自適應寬度,自適應手機屏幕分辨率大小

2. drawYScale():劃分Y軸,設定Y軸的起點(Y軸座標原點是圖層最上面),Y軸總高度,然後劃分Y軸座標,座標分爲大刻度座標和小刻度座標,設定大刻度座標長度和小刻度座標長度,畫好Y軸座標之後,再畫刻度橫線

   

3. drawXScale():劃分X軸,同Y軸一樣,設定X軸座標原點和長度,劃分刻度值


4. drawDashLine() :畫虛線,X軸和Y軸的虛線,這個其實很簡單,確定虛線的起始座標和終點座標,畫直線就行了,主要是找到座標

5. drawCharts():畫折線,一個一個點連接,就成了一條折線圖

完整JS代碼

const app = getApp()

Page({
    data: {
        list: [50, 150, 170, 200, 100, 267, 20, 50, 130, 124],
        h32: 32,
        h64: 64,
        h360: 360,
        h420: 420,
        s28: 28,
        s18: 18,
        //Y軸分成的大分段
        heightLineNum: 7,
        //X軸分成的大分段
        widthLineNum: 10,
        //Y軸一個分段的值
        yOneDuan: 50
    },

    onLoad: function (options) {
        //折線圖
        this.initChart()
    },

    // 初始化折線圖
    initChart: function () {
        const ctx = wx.createCanvasContext('canvasId')

        ctx.beginPath()
        ctx.setStrokeStyle('#999999')
        ctx.setFillStyle('#AAAAAA')
        ctx.setLineWidth(1)

        //座標原點,Y軸座標值從上往下是增加
        const leftBottomX = this.getEleWidth(this.data.h64)
        const leftBottomY = this.getEleWidth(this.data.h360)
        //Y座標
        const leftTopX = this.getEleWidth(this.data.h64)
        const leftTopY = this.getEleWidth(this.data.h32)
        //X座標
        const rightBottomX = this.getEleWidth(this.data.h420)
        const rightBottomY = this.getEleWidth(this.data.h360)

        const yHeight = this.getEleWidth(this.data.h360 - this.data.h32)
        const xWidth = this.getEleWidth(this.data.h420 - this.data.h64)

        //從Y軸座標開始畫座標系
        //Y軸座標到原點座標畫出Y軸線
        //畫完Y軸線,再從原點座標到X軸座標畫出X軸線
        ctx.moveTo(leftTopX, leftTopY)
        ctx.lineTo(leftBottomX, leftBottomY)
        ctx.lineTo(rightBottomX, rightBottomY)
        //設置字體大小
        ctx.setFontSize(this.getEleWidth(this.data.s28))
        //設置字的位置
        ctx.fillText("折線圖", this.getEleWidth(340), this.getEleWidth(32))

        //劃分Y軸
        this.drawYScale(ctx);
        //劃分X軸
        this.drawXScale(ctx);

        //畫折線
        this.drawCharts(ctx);
        ctx.stroke()
        ctx.draw(true)
    },

    //劃分Y軸
    drawYScale: function (ctx) {
        var that = this;

        //Y軸座標刻度橫座標起點
        var scaleStartX = this.getEleWidth(this.data.h64)
        //長的刻度
        var scaleEndX = this.getEleWidth(this.data.h64 + 18)
        //短的刻度
        var littleScaleEndX = this.getEleWidth(this.data.h64 + 9)

        //Y軸刻度總高度
        const yHeight = this.getEleWidth(this.data.h360)
        //一個大分段的長度,一共分爲6段
        var oneScaleX = yHeight / this.data.heightLineNum
        //大分段數字字體大小
        ctx.setFontSize(this.getEleWidth(this.data.s18))
        //大分段數字位置橫座標
        var textX = this.getEleWidth(this.data.h64 - 42)
        //大分段,長刻度:50-300
        for (var i = 1; i < this.data.heightLineNum; i++) {
            var scaleEndY = yHeight - oneScaleX * i
            //畫長刻度線條
            ctx.moveTo(scaleStartX, scaleEndY)
            ctx.lineTo(scaleEndX, scaleEndY)
            ctx.fillText(this.data.yOneDuan * i, textX, scaleEndY + this.getEleWidth(10))
            var littleScaleStartY = yHeight - oneScaleX * (i - 1)
            //小分段,短刻度
            for (var j = 1; j < 5; j++) {
                var littleScaleEndY = littleScaleStartY - (oneScaleX / 5) * j
                //畫短刻度線條
                ctx.moveTo(scaleStartX, littleScaleEndY)
                ctx.lineTo(littleScaleEndX, littleScaleEndY)
                ctx.stroke();
            }
        }
        //高和低虛線Y軸座標
        const lowlimitLineY = yHeight - oneScaleX * 2
        const middlelimitLineY = yHeight - oneScaleX * 4
        const highlimitLineY = yHeight - oneScaleX * 6

        //虛線總長度
        const rightBottomX = this.getEleWidth(this.data.h420)
        const space = this.getEleWidth(10)
        //限制虛線
        that.drawDashLine(ctx, scaleStartX, lowlimitLineY, rightBottomX, lowlimitLineY, space)
        that.drawDashLine(ctx, scaleStartX, middlelimitLineY, rightBottomX, middlelimitLineY, space)
        that.drawDashLine(ctx, scaleStartX, highlimitLineY, rightBottomX, highlimitLineY, space)
    },

    //劃分X軸
    drawXScale: function (ctx) {
        var that = this;
        //虛線總高度
        var scaleStartY = this.getEleWidth(that.data.h360)
        //虛線頂點Y軸高度
        var scaleEndY = this.getEleWidth(that.data.h32)
        //X軸總長度=X軸橫座標-向右偏移長度
        const xWidth = this.getEleWidth(that.data.h420 - that.data.h64)
        //X軸起始點
        const xMaginLeft = this.getEleWidth(that.data.h64)
        //一個分段的寬度
        const oneScaleX = xWidth / (that.data.widthLineNum + 1)
        const space = this.getEleWidth(10)
        for (var i = 0; i < that.data.widthLineNum + 1; i++) {
            var toEndX = xMaginLeft + oneScaleX * i;
            if (i > 0) {
                that.drawDashLine(ctx, toEndX, scaleStartY, toEndX, scaleEndY, space)
            }
            ctx.fillText(i, toEndX - this.getEleWidth(5), scaleStartY + this.getEleWidth(30))
        }
    },

    //畫虛線
    drawDashLine: function (ctx, x1, y1, x2, y2, dashLength) {
        //傳context對象,始點x和y座標,終點x和y座標,虛線長度
        ctx.beginPath()
        ctx.setLineWidth(0.5)
        var dashLen = dashLength === undefined ? 3 : dashLength,
        //得到橫向的寬度;
        xpos = x2 - x1,
        //得到縱向的高度;
        ypos = y2 - y1,
        numDashes = Math.floor(Math.sqrt(xpos * xpos + ypos * ypos) / dashLen);
        //利用正切獲取斜邊的長度除以虛線長度,得到要分爲多少段;
        for (var i = 0; i < numDashes; i++) {
            if (i % 2 === 0) {
                ctx.moveTo(x1 + (xpos / numDashes) * i, y1 + (ypos / numDashes) * i);
                //有了橫向寬度和多少段,得出每一段是多長,起點 + 每段長度 * i = 要繪製的起點;
            } else {
                ctx.lineTo(x1 + (xpos / numDashes) * i, y1 + (ypos / numDashes) * i);
            }
        }
        ctx.stroke();
    },

    //折線
    drawCharts: function (ctx) {
        ctx.beginPath()
        ctx.setStrokeStyle("#238E23")
        var that = this;
        var list = that.data.list;

        const yHeight = this.getEleWidth(that.data.h360)
        const xWidth = this.getEleWidth(that.data.h420 - this.data.h64)
        //X座標,一個空格的值
        const oneScaleX = xWidth / (that.data.widthLineNum + 1)
        //Y座標,一個空格的值
        var oneScaleY = yHeight / this.data.heightLineNum;

        for (var i = 0; i < list.length; i++) {
            var height = list[i];
            //計算X座標
            var x = oneScaleX * (i + 1) + this.getEleWidth(that.data.h64);
            //計算Y座標
            var y = yHeight - oneScaleY / this.data.yOneDuan * height
            if (i == 0) {
                ctx.moveTo(x, y)
            } else {
                ctx.lineTo(x, y)
            }
        }

        ctx.stroke()
        ctx.draw(true)
    },

    //獲取屏幕自適應寬度
    getEleWidth: function (w) {
        var real = 0;
        try {
            var res = wx.getSystemInfoSync().windowWidth;
            //以寬度480px設計做寬度的自適應
            var scale = (480 / 2) / (w / 2);
            real = Math.floor(res / scale);
            return real;
        } catch (e) {
            return false;
        }
    }
})


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章