【小程序】canvas绘制五维图

前几天收到一个需求,要根据五个数据信息绘制五维图,为了更好地契合产品经理的要求,选择了用canvas来绘制。

看了看绘制的方法,马马虎虎的画出了效果图,现在分享一下经历。

首先了解一下坐标系  
小程序中canvas的坐标原点在左上角,x轴向右为正,y轴向下为正。
例如:知道了某一个点A的坐标(X,Y),通过计算得到了点B距离A在X轴和Y轴的距离,通过B相对于A的位置,并通过“←减→加,↑减↓加”的计算方式在X和Y上计算得到B的坐标。

初步了解以后,接下来就开始在view上面一边磨枪一边上阵。

首先在wxml里面添加canvas

<view style="position: relative;display: flex;flex-direction: column;height: 400rpx;align-items: center;">
    <canvas id="myCanvas" type="2d" style="height: 400rpx;width: 600rpx;" />
</view>

然后再js文件里面添加小程序文档里面的方法

 DrawFive() {
        let _this = this
        wx.createSelectorQuery()
            .select('#myCanvas') // 在 WXML 中填入的 id
            .fields({ node: true, size: true })
            .exec((res) => {
                // Canvas 对象
                const canvas = res[0].node
                // Canvas 画布的实际绘制宽高
                const renderWidth = res[0].width
                const renderHeight = res[0].height
                // Canvas 绘制上下文
                const ctx = canvas.getContext('2d')

                // 初始化画布大小
                const dpr = wx.getWindowInfo().pixelRatio
                canvas.width = renderWidth * dpr
                canvas.height = renderHeight * dpr
                ctx.scale(dpr, dpr)
                // 绘制前清空画布
                ctx.clearRect(0, 0, canvas.width, canvas.height)
                //后续操作
             
            })
    }

考虑到我们要画的这个图用到了三角函数,需要声明一些方法和常量

        const cos = Math.cos
        const sin = Math.sin
        const PI = Math.PI

关于Math.PI指的是我们的圆周率π。需要注意的是,Math.cos和Math.sin跟我们数学计算中的不一样,数学中cos60°是角度,Math.cos(1.05)是弧度。

弧度和角度的计算公式:

弧度 = 角度*PI/180
角度 = 弧度*180/PI

 我们的画布,在wxml里面定义200*300px,我们设定圆点和半径,设定颜色和线条宽度画最外面那个圆

                var p0x = 150
                var p0y = 110
                var r = 70


                ctx.beginPath()
                ctx.strokeStyle = '#EB37221A'
                ctx.lineWidth = '4'
                ctx.arc(p0x, p0y, r, 0, 2 * Math.PI, true)
                ctx.stroke()
                ctx.closePath()

然后重新设置颜色和线条宽度,画五根线

                ctx.beginPath()
                ctx.strokeStyle = '#EB37221A'
                ctx.lineWidth = '1'
                ctx.stroke()
                ctx.moveTo(p0x, p0y)
                ctx.lineTo(p0x, p0y - r)
                ctx.stroke();
                var p1y = r * sin(PI / 180 * 18)
                var p1x = r * cos(PI / 180 * 15)

                ctx.moveTo(p0x, p0y)
                ctx.lineTo(p0x + p1x, p0y - p1y)
                ctx.stroke()
                var p2y = r * sin(PI / 180 * 54)
                var p2x = r * cos(PI / 180 * 54)

                ctx.moveTo(p0x, p0y)
                ctx.lineTo(p0x + p2x, p0y + p2y)
                ctx.stroke()
                var p3y = p2y
                var p3x = p2x

                ctx.moveTo(p0x, p0y)
                ctx.lineTo(p0x - p3x, p0y + p3y)
                ctx.stroke()
                var p4y = p1y
                var p4x = p1x
                ctx.moveTo(p0x, p0y)
                ctx.lineTo(p0x - p4x, p0y - p4y)
                ctx.stroke()
                ctx.closePath()

moveTo(x,y)是指点移动到这个位置,就单纯的移动过去就完事了。

lineTo(x,y)是指从目前这个点画到(x,y)这个点,一路走一路画,走到了也就画出了一条线。

画完了线,我们再标注外面的文字和数字。

我们首先在Js的data里面添加默认数据

    data: {
        result: { "radar_map": [{ "id": 1, "name": "字词认读", "score": 20 }, { "id": 2, "name": "观察感知", "score": 20 }, { "id": 3, "name": "分析判断", "score": 20 }, { "id": 4, "name": "推理想象", "score": 10 }, { "id": 5, "name": "语言表达", "score": 20 }] }

    },

然后在获取这些值

                var key0 = _this.data.result.radar_map[0].name
                var score0 = _this.data.result.radar_map[0].score
                var key1 = _this.data.result.radar_map[1].name
                var score1 = _this.data.result.radar_map[1].score
                var key2 = _this.data.result.radar_map[2].name
                var score2 = _this.data.result.radar_map[2].score
                var key3 = _this.data.result.radar_map[3].name
                var score3 = _this.data.result.radar_map[3].score
                var key4 = _this.data.result.radar_map[4].name
                var score4 = _this.data.result.radar_map[4].score

设定颜色和微调位置,把数据画上去

                ctx.fillStyle = '#666666'
                ctx.fillText(key0, p0x - 20, p0y - r - 10 - 15)
                ctx.fillText(key1, p0x + r + 10, p0y - p1y - 10)
                ctx.fillText(key2, p0x + p2x + 10, p0y + p2y + 10)
                ctx.fillText(key3, p0x - p3x - 60, p0y + p3y + 10)
                ctx.fillText(key4, p0x - p4x - 60, p0y - p4y - 10)

                ctx.fillStyle = '#EB3722'
                ctx.fillText(' ' + score0, p0x - 20 + 15, p0y - r - 10 - 15 + 15)
                ctx.fillStyle = '#000AFF'
                ctx.fillText(' ' + score1, p0x + r + 10 + 15, p0y - p1y - 10 + 15)
                ctx.fillStyle = '#EB22D7'
                ctx.fillText(' ' + score2, p0x + p2x + 10 + 15, p0y + p2y + 10 + 15)
                ctx.fillStyle = '#32EB22'
                ctx.fillText(' ' + score3, p0x - p3x - 60 + 15, p0y + p3y + 10 + 15)
                ctx.fillStyle = '#EBA722'
                ctx.fillText(' ' + score4, p0x - p4x - 60 + 15, p0y - p4y - 10 + 15)
                ctx.stroke()

最后设置填充颜色和线条颜色,计算比例,我这边的需求是每项最多是20。

                ctx.beginPath()
                ctx.fillStyle = '#EB372280';
                ctx.strokeStyle = '#EB3722'

                var pp0y = p0y - r * score0 / 20
                var pp0x = p0x
                ctx.moveTo(pp0x, pp0y)
                var pp1y = r * sin(PI / 180 * 18) * score1 / 20
                var pp1x = r * cos(PI / 180 * 15) * score1 / 20

                ctx.lineTo(p0x + pp1x, p0y - pp1y)
                ctx.stroke()
                var pp2y = r * sin(PI / 180 * 54) * score2 / 20
                var pp2x = r * cos(PI / 180 * 54) * score2 / 20


                ctx.lineTo(p0x + pp2x, p0y + pp2y)
                ctx.stroke()
                var pp3y = r * sin(PI / 180 * 54) * score3 / 20
                var pp3x = r * cos(PI / 180 * 54) * score3 / 20


                ctx.lineTo(p0x - pp3x, p0y + pp3y)
                ctx.stroke()
                var pp4y = r * sin(PI / 180 * 18) * score4 / 20
                var pp4x = r * cos(PI / 180 * 15) * score4 / 20

                ctx.lineTo(p0x - pp4x, p0y - pp4y)
                ctx.stroke()
                ctx.lineTo(pp0x, pp0y)
                ctx.stroke()
                ctx.fill()
                ctx.closePath()

至此就画了个差不离了。

效果如下:

总结一下,

1.了解canvas的坐标系

2.了解Math.cos和Math.sin 

3.配置不同stroke颜色要用beginPath()和closePath()包住

4.微信api演示中setStrokeStyle(),setLineWidth(),setFillStyle()通通都要改成strokeStyle='',lineWidth=‘’,fillStyle=‘’。

私以为第四点才是最坑的,因为前面的问题都有文档。

好啦,新手操作到此结束。

欢迎大家批评指正!

猜你喜欢

转载自blog.csdn.net/u010055598/article/details/132407110