效果图:

view

<canvas id="radar-canvas" class="radar-canvas" type="2d"></canvas>

style

.radar-canvas
width 550rpx
height 550rpx
margin 0 auto

script

<script>
import { toRpx } from "@/utils/common" const numCount = 5 //元素个数
const numSlot = 4 //一条线上的总节点数
const mW = toRpx(275) //Canvas的宽度
const mCenter = mW / 2 //中心点
const mAngle = Math.PI * 2 / numCount //角度
const mRadius = mCenter - toRpx(43) //半径(减去的值用于给绘制的文本留空间) let canvas = null // canvas
let canvasCtx = null // canvas context export default {
name: 'RadarChart',
props: {
},
methods: {
// 初始化雷达图,在组件挂载的时候执行
initDrawRadar() {
console.log('init')
const query = uni.createSelectorQuery().in(this)
query.select('#radar-canvas').fields({ node: true, size: true }).exec((res) => {
canvas = res[0].node
canvasCtx = canvas.getContext('2d')
const dpr = uni.getSystemInfoSync().pixelRatio
canvas.width = res[0].width * dpr
canvas.height = res[0].height * dpr
canvasCtx.scale(dpr, dpr)
})
},
// 开始执行绘制
handleDraw(radarData) {
this.drawEdge()
this.drawLinePoint()
this.drawText(radarData)
this.drawSubText(radarData)
this.drawEdgeDot()
this.drawRegion(radarData, 'rgba(255, 105, 81, 0.4)')
},
// 绘制圆边
drawEdge() {
canvasCtx.strokeStyle = '#EEEEEE'
for (let i = 0; i < numSlot; i++) {
// 计算半径
let radius = mRadius / numSlot * (i + 1)
if (i === 3) {
canvasCtx.lineWidth = toRpx(4) // 设置线宽
canvasCtx.beginPath()
canvasCtx.arc(mCenter, mCenter, radius, 0, 2 * Math.PI,) // 开始画圆
canvasCtx.stroke()
} else {
canvasCtx.lineWidth = toRpx(1)
const space = 60 + 10 * (i+1)
this.drawDashCircle(mCenter, mCenter, radius, space)
}
}
},
// 绘制外边框圆点
drawEdgeDot(x, y) {
canvasCtx.fillStyle = '#EEEEEF'
canvasCtx.beginPath()
for (let k = 0; k < numCount; k++) {
let x = mCenter + mRadius * Math.cos(mAngle * k - Math.PI / 2)
let y = mCenter + mRadius * Math.sin(mAngle * k - Math.PI / 2)
canvasCtx.arc(x, y, toRpx(5), Math.PI * 2, 0, true)
canvasCtx.closePath()
}
canvasCtx.fill()
},
// 绘制虚线圆
drawDashCircle(x, y, radius, space = 100) {
const gap = 2 * Math.PI / space
canvasCtx.lineCap ='square' let start = 0; //从原点开始画
while (start <= 2 * Math.PI) {
let end = start + gap
canvasCtx.beginPath() //开始一个新的路径
canvasCtx.arc(x, y, radius, start, end, false)
start = gap + end
canvasCtx.stroke() //对当前路径进行描边
}
},
// 绘制连接点
drawLinePoint() {
canvasCtx.lineWidth = toRpx(1)
canvasCtx.beginPath()
for (let k = 0; k < numCount; k++) {
let x = mCenter + mRadius * Math.cos(mAngle * k - Math.PI / 2)
let y = mCenter + mRadius * Math.sin(mAngle * k - Math.PI / 2)
canvasCtx.moveTo(mCenter, mCenter)
canvasCtx.lineTo(x, y)
}
canvasCtx.stroke()
},
// 绘制文本信息
drawText(mData) {
canvasCtx.fillStyle = '#222325'
canvasCtx.font = `bold ${toRpx(14)}px PingFangSC-Medium, PingFang SC` //设置字体
for (let n = 0; n < numCount; n++) {
let x = mCenter + mRadius * Math.cos(mAngle * n - Math.PI / 2)
let y = mCenter + mRadius * Math.sin(mAngle * n - Math.PI / 2)
//通过不同的位置,调整文本的显示位置
const text = mData[n][0]
if (n === 0) {
canvasCtx.fillText(text, x - toRpx(12), y - toRpx(30))
}
if (n === 1) {
canvasCtx.fillText(text, x + toRpx(12), y)
}
if (n === 2) {
canvasCtx.fillText(text, x + toRpx(12), y + toRpx(20))
}
if (n === 3) {
canvasCtx.fillText(text, x - toRpx(36), y + toRpx(20))
}
if (n === 4) {
canvasCtx.fillText(text, x - toRpx(40), y)
}
}
},
// 绘制文本信息
drawSubText(mData) {
canvasCtx.fillStyle = '#8D949B'
canvasCtx.font = `${toRpx(11)}px PingFangSC-Medium, PingFang SC` //设置字体
for (let n = 0; n < numCount; n++) {
const x = mCenter + mRadius * Math.cos(mAngle * n - Math.PI / 2)
const y = mCenter + mRadius * Math.sin(mAngle * n - Math.PI / 2)
//通过不同的位置,调整文本的显示位置
const text = `(${mData[n][1]})`
if (n === 0) {
canvasCtx.fillText(text, x - canvasCtx.measureText(text).width / 2, y - toRpx(10))
}
if (n === 1) {
canvasCtx.fillText(text, x + canvasCtx.measureText(text).width, y + toRpx(16))
}
if (n === 2) {
canvasCtx.fillText(text, x + canvasCtx.measureText(text).width - toRpx(4), y + toRpx(40))
}
if (n === 3) {
canvasCtx.fillText(text, x - canvasCtx.measureText(text).width - toRpx(12), y + toRpx(40))
}
if (n === 4) {
canvasCtx.fillText(text, x - canvasCtx.measureText(text).width - toRpx(16), y + toRpx(16))
}
}
},
//绘制红色数据区域(数据和填充颜色)
drawRegion(mData, color){
// 关注公众号《码克吐温》获取完整代码
},
},
mounted() {
this.initDrawRadar()
}
}
</script>

要注意的点是,这里是封装成组件调用,在初始化的时候,const query = uni.createSelectorQuery().in(this),要加上in(this),否则会报找不到node节点的错误信息

toRpx方法是将数字大小转化为rpx响应式单位大小,方法如下:

export function toRpx(val) {
const res = uni.getSystemInfoSync()
const scaleRate = res.windowWidth / 375
return val * scaleRate
}

在页面中调用

<template>
<!--雷达图-->
<radar-chart :radarData="radarData" ref="radarRef"></radar-chart>
</template> import RadarChart from './components/radar' export default {
components: {
RadarChart,
},
data() {
return {
radarData:[["听力", 0], ["口语",0], ["语法",0], ["词汇",0], ["阅读",0]],
}
},
methods: {
getData() {
// 请求数据返回后,调用组件方法渲染
this.$refs.radarRef.handleDraw(this.radarData)
}
}
}

最后,欢迎关注本人公众号:(码克吐温)及时获取博客内容更新~

uniapp封装小程序雷达图组件实现的更多相关文章

  1. 支付宝小程序日期选择组件datePicker封装

    github 地址 https://github.com/iocool/antminDatePicker 最近在做支付宝小程序(以下简称小程序)开发,发现小程序的日期选择组件很不好用,比如安卓和IOS ...

  2. uni-app开发微信小程序引入UI组件库(Vant-weapp)步骤

    uni-app开发微信小程序引入UI组件库(Vant-weapp)步骤 这里以vant-weapp为例 uni-app官方文档介绍引入组件的方法 1. 新建相关目录 根目录下创建 wxcomponen ...

  3. 【重点突破】—— UniApp微信小程序开发教程学习Three

    一.实战 HBuilderX:在微信小程序中运行页面,需要设置->安全 开启微信小程序服务端口,HBuilder工具->设置->配置程序路径 网络请求.模板语法.打开页面.页面传参 ...

  4. 小程序解决方案 Westore - 组件、纯组件、插件开发

    数据流转 先上一张图看清 Westore 怎么解决小程序数据难以管理和维护的问题: 非纯组件的话,可以直接省去 triggerEvent 的过程,直接修改 store.data 并且 update,形 ...

  5. 微信小程序之自定义组件的应用

    小程序支持自定义组件,下面是一个简单的购物车组件,实现的效果如图: 效果图 创建组件 在根目录创建components目录,然后创建计数组件 count 如图: 组件内容 <!--compone ...

  6. 微信小程序自定义音频组件,自定义滚动条,单曲循环,循环播放

    小程序自定义音频组件,带滚动条 摘要:首先自定义音频组件,是因为产品有这样的需求,需要如下样式的 而微信小程序API给我们提供的就是这样的 而且产品需要小程序有后台播放功能,所以我们不考虑小程序的 a ...

  7. uniapp开发小程序

    uniapp开发小程序 uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS.Android.Web(响应式).以及各种小程序(微信/支付宝/百度/头条 ...

  8. 微信小程序引入ECharts组件

    首先打开ECharts网页 https://echarts.apache.org/zh/tutorial.html#%E5%9C%A8%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8 ...

  9. 原创:WeZRender:微信小程序Canvas增强组件

    WeZRender是一个微信小程序Canvas增强组件,基于HTML5 Canvas类库ZRender. 使用 WXML: <canvas style="width: 375px; h ...

随机推荐

  1. 续PA协商过程

    续PA协商过程 当sw3的接口恢复之后会发生2中情况. ①sw3的G0/0/2口先发BPDU ②sw3的G0/0/3口先发BPDU sw3先发送BPDU sw3和sw1的交互过程: sw3的2口恢复后 ...

  2. C语言:toascii()函数

    /* 头文件:#include <ctype.h> 定义函数:int toascii(int c); 函数说明:toascii()会将参数c 转换成7 位的unsigned char 值, ...

  3. 三分钟掌握共享内存 & Actor并发模型

    吃点好的,很有必要.今天介绍常见的两种并发模型: 共享内存&Actor 共享内存 面向对象编程中,万物都是对象,数据+行为=对象: 多核时代,可并行多个线程,但是受限于资源对象,线程之间存在对 ...

  4. Scala学习——函数高级操作

    scala函数高级操作 一.字符串高级操作 多行字符串和插值 package top.ruandb.scala.Course06 object StringApp { def main(args: A ...

  5. erase

    erase详细解释及原理 我们先定义一个字符串string string.erase(iterator) iterator表示要删除元素的迭代器. string.erase(it_begin,it_e ...

  6. Oracle导入dmp文件:ORACLE错误12899而拒绝行的问题如何解决

    原文链接:https://www.2cto.com/database/201804/736027.html

  7. Receiver class com.mchange.v2.c3p0.impl.NewProxyResultSet does not define or inherit an implementation of the resolved method 'abstract boolean isClosed()' of interface java.sql.ResultSet.

    背景: Mayabtis+springboot项目,连接数据库发生异常 报错内容: java.lang.AbstractMethodError: Receiver class com.mchange. ...

  8. 解决pip下载速度慢的问题

    解决链接:https://blog.csdn.net/u013901768/article/details/82343512 感谢这位博主的分享. 博客内容如下: 1. 安装时强制使用国内源进行安装, ...

  9. HTTP协议GET方法传参最大长度理解误区

    结论 HTTP 协议未规定GET和POST的长度 GET的最大长度是因为浏览器和WEB服务器显示了URI的长度 不同浏览器和WEB服务器,限制的最大长度不同 若要支持IE,则最大长度为2083 byt ...

  10. YGC问题排查

    在高并发下, Java程序的GC问题属于很典型的一类问题,带来的影响往往会被进一步放大. 不管是 「GC频率过快」还是「GC耗时太长」,由于GC期间都存在Stop The World问题,因此很容易导 ...