效果图:

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. 史上最全的Nginx配置文档

    Nginx是一个异步框架的Web服务器,也可以用作反向代理,负载平衡器 和 HTTP缓存.该软件由Igor Sysoev 创建,并于2004年首次公开发布.同名公司成立于2011年,以提供支持.Ngi ...

  2. 北京大公司:你是熟悉Map集合吗?

    <对线面试官>系列目前已经连载30篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...

  3. 小猿圈-IT自学人的小圈子 【强力推荐学习】

    笔记链接 https://book.apeland.cn/details/322/ 学习视频 https://www.apeland.cn/python

  4. SpringBoot+Redis 实现消息订阅发布

    什么是 Redis Redis 是一个开源的使用 ANSI C语言编写的内存数据库,它以 key-value 键值对的形式存储数据,高性能,读取速度快,也提供了持久化存储机制. Redis 通常在项目 ...

  5. 鸿蒙轻内核定时器Swtmr:不受硬件和数量限制,满足用户需求

    摘要:本文通过分析鸿蒙轻内核定时器模块的源码,掌握定时器使用上的差异. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十四 软件定时器Swtmr>,作者:zhushy . 软件定时器(S ...

  6. python基础之读取xml

    python怎么操作xml文件详细介绍链接:https://www.jb51.net/article/50812.htm 从结构上来说,xml很像常见的HTML超文本标记语言.不过超文本语言被设计用来 ...

  7. .net 5+ 知新:【2】 .Net Framework 、.Net 、 .NET Standard的概念与区别

    作为了解历史和眼睛或者过程,我们需要将 .Net Framwork ..Net. .Net Stander几个概念进行下理解. .net 代表跨平台框架,从.net 5开始就统一叫.net,废弃原来的 ...

  8. 【Azure API 管理】在APIM中使用客户端证书验证API的请求,但是一直提示错误"No client certificate received."

    API 管理 (APIM) 是一种为现有后端服务创建一致且现代化的 API 网关的方法. 问题描述 在设置了APIM客户端证书,用户保护后端API,让请求更安全. 但是,最近发现使用客户端证书的API ...

  9. Querydsl与SpringBoot集成

    Querydsl为大多数数据库提供了一种基于Java的类型安全,类SQL的查询方式.相比JPA,Querydsl能提供更加强大的查询方式,比如关联查询.相比MyBatis,Querydsl省去了XML ...

  10. vue+element实现分页--之--前端分页

    效果图: 访问的数据量小,一次返回所有数据,再次利用elementUI-Table 和el-pagination组件进行展示,关键点事数据的筛选 官网的完整案例 <div class=" ...