canvas 实现雷达图
1. data 数据
//data:雷达图所依据的数据
var data = {scoreData : [{ text : '攻击', value : '45' },{ text : '力量', value : '23' },{ text : '体质', value : '67' },{ text : '精神', value : '89' },{ text : '敏捷', value : '17' },{ text : '防御', value : '49' },{ text : '速度', value : '88' },{ text : '属性', value : '17' },{ text : '爱咋咋', value : '49' }],option : {polygonBgColor : ['#f97683', '#bf93d8', '#44909e'],//多边形背景色} //自定义样式}复制代码
2. 雷达图初始化
let init = {id : document.querySelector('#redarCanvas'), //html文件中有盛放canvas的标签canvasW : '560', //自定义canvas 宽度canvasH : '560', //自定义canvas 高度animationLoop : '', //data中scoreData的最大值会有动画效果animation : '',start : 'true', //雷达图是否要展示defaultStyle : {polygonBgColor : ['orange', 'red', 'skyblue'],//多边形背景色Bgshadow : { //雷达图阴影color : '',shadowBlur : 0},translate : {x : 1,y : 8/9}, //图像偏移量(相对于中心点的倍数)rayLine : {// 辐射线样式color : '#B7AB72'},pointLine : {// 连接分数点线条样式color : 'rgba(255,255,255,.1)'},points : {// 分数点样式size : 1/25},tipText : {color : 'rgb(75,66,66)', //Array || StringfontSize : '64'},alpha : 0.3,Reminder : { //右下角标注样式bgColor : ['#f97683','#bf93d8','#44909e'],text : ['高','中','低'],color : 'rgba(119,119,119,1)',width : .04,fontSize : 1,coordinate : {x : 1/7,y : 1.05}}}
}复制代码
3. 定义一个函数objAssign,将自定义样式替换成默认样式
function objAssign(source, target) {try {var json = JSON.parse(JSON.stringify(source))var assign = function(source, target) {for (var key in target) {if (key in source) {if (Object.prototype.toString.call(target[key])==='[object Object]') {assign(source[key], target[key])} else {if (target[key]!==source[key]) {source[key] = target[key]}}}}}assign(json, target)console.log(json)return json} catch (e) {console.error(e)return {}}
}
var pointScore = [] //将data.scoreData 中的value值保存在一个数组里面
var pointText = [] // text值也保存在一个数组中data.scoreData.sort(function(x, y) {return x.value-y.value
})data.scoreData.forEach(function(e) {pointText.push(e.text)pointScore.push(e.value)
})if (data.option) {var polygonStyle = objAssign(init.defaultStyle, data.option)
} else {var polygonStyle = init.defaultStyle
}if (pointScore.length==3) {polygonStyle.translate.y = 1/2//polygonStyle.Reminder.coordinate.y = 1.6
}复制代码
4. 绘制
let myCanvas = init.id
let ctx = myCanvas.getContext('2d')
/*设置画布宽高*/
myCanvas.width = init.canvasW
myCanvas.height = init.canvasHlet width = myCanvas.width,height = myCanvas.height
//根据设备的DPR设置画布的宽高
if (window.devicePixelRatio) {myCanvas.style.width = width+'px'myCanvas.style.height = height+'px'myCanvas.height = height*window.devicePixelRatiomyCanvas.width = width*window.devicePixelRatio
}
let polygonArr = []//存放多边形实例let Radius = myCanvas.height/2.6let numofSide = pointScore.length //n边形(n>=3)var maxScore = Math.max.apply(Math, pointScore)var end = false//闪烁
var twinkleSpeed = 1
var shadowBlur = 0/*多边形构造函数*/
function Polygon(option, ctx) {this.option = optionthis.pointY = option.pointY || 0this.pointX = option.pointX || 0this.translateX = this.pointXthis.translateY = this.pointYthis.lengthofSide = option.lengthofSidethis.numofSide = option.numofSide || 3this.animationFrame = 0this.scaleRate = 0 // 缩放比例this.radian = 360/this.numofSide/2*Math.PI/180this.shadow = option.shadow || undefined//多边形外接圆的半径 cos(360/numofside/2) = L/2/r;if (this.lengthofSide) {this.r = this.lengthofSide/2/Math.sin(this.radian)} else {this.r = option.r}this.isFill = option.isFill || falsethis.strokeStyle = option.strokeStyle || '#fff'this.fillStyle = option.fillStyle || '#000'this.ctx = ctxthis.isScale = option.isScalethis.line = 0this.drawlineSpeed = 5this.progress = 0polygonArr.push(this)
}Polygon.prototype.draw = function() {this.pointY = this.isScale ? 0 : this.option.pointYthis.pointX = this.isScale ? 0 : this.option.pointXthis.ctx.beginPath()this.ctx.strokeStyle = this.strokeStylethis.ctx.setLineDash([])var startX = this.pointX+this.r*Math.sin(2*Math.PI*0/this.numofSide)var startY = this.pointY+this.r*Math.cos(2*Math.PI*0/this.numofSide)this.ctx.moveTo(startX, startY)for (var i = 1; i <= this.numofSide; i++) {var X = this.pointX+this.r*Math.sin(2*Math.PI*i/this.numofSide)var Y = this.pointY+this.r*Math.cos(2*Math.PI*i/this.numofSide)this.ctx.lineTo(X, Y)}if (this.shadow) {this.ctx.shadowColor = this.shadow.colorthis.ctx.shadowBlur = 40this.ctx.shadowOffsetY = 30} else {this.ctx.shadowBlur = 0this.ctx.shadowColor = 'none'this.ctx.shadowOffsetY = 0this.ctx.shadowOffsetX = 0}if (this.isFill) {this.ctx.fillStyle = this.fillStylethis.ctx.fill()}this.ctx.closePath()this.ctx.stroke()
}
复制代码
5.
Polygon.prototype.update = function() {var t = this.animationFrame*16/1000this.scaleRate = -1/2*Math.pow(Math.E, (-6*t/1.5))*(-2*Math.pow(Math.E, (6*t/1.5))+Math.sin(12*t/1.5)+2*Math.cos(12*t/1.5))this.animationFrame += 1
}Polygon.prototype.scale = function() {this.ctx.save()this.ctx.translate(this.translateX, this.translateY)this.ctx.scale(this.scaleRate, this.scaleRate)this.draw()this.ctx.restore()
}//右下角标注
Polygon.prototype.drawReminder = function(text, color, x, y, height, width, bgColor, alpha) {var fontSize = width*polygonStyle.Reminder.fontSizethis.ctx.beginPath()this.ctx.fillStyle = bgColorthis.ctx.fillRect(x, y, width, height)this.ctx.textAlign = 'start'this.ctx.font = fontSize+'px 微软雅黑'this.ctx.fillStyle = 'rgba(75,66,66,'+alpha+')'this.ctx.textBaseline = 'middle'this.ctx.fillText(text, x+width*1.4, y+height/2)this.ctx.closePath()
}
//绘制辐射线
Polygon.prototype.drawLine = function(callback) {this.ctx.beginPath()for (var i = 1; i <= this.numofSide; i++) {this.ctx.lineWidth = myCanvas.height*.002this.ctx.strokeStyle = polygonStyle.rayLine.colorthis.ctx.setLineDash([5, 10])this.ctx.moveTo(this.pointX, this.pointY)var X = this.pointX+this.r*Math.sin(2*Math.PI*i/this.numofSide)var Y = this.pointY+this.r*Math.cos(2*Math.PI*i/this.numofSide)this.ctx.lineTo(X, Y)}this.ctx.stroke()this.ctx.closePath()
}Polygon.prototype.drawPoint = function(callback) {if (this.progress >= maxScore) {this.progress===maxScorecallback && callback()} else {this.progress += .4}/*连接分数点*/this.ctx.beginPath()this.ctx.shadowBlur = 0this.ctx.shadowColor = ''this.ctx.strokeStyle = polygonStyle.pointLine.colorthis.ctx.setLineDash([])this.ctx.lineWidth = '2'this.ctx.moveTo(this.pointX+0.92*(this.progress*pointScore[0]/maxScore/100*this.r-this.r*polygonStyle.points.size)*Math.sin(2*Math.PI*0/this.numofSide),this.pointY+0.92*(this.progress*pointScore[0]/maxScore/100*this.r-this.r*polygonStyle.points.size)*Math.cos(2*Math.PI*0/this.numofSide))for (var j = 1; j < this.numofSide; j++) {let len = this.progress*pointScore[j]/maxScore/100*this.rthis.ctx.lineTo(this.pointX+0.9*(len-this.r*polygonStyle.points.size)*Math.sin(2*Math.PI*j/this.numofSide), this.pointY+0.9*(len-this.r*polygonStyle.points.size)*Math.cos(2*Math.PI*j/this.numofSide))}this.ctx.fillStyle = 'rgba(255, 0, 0, 0.05)'this.ctx.fill()this.ctx.closePath()this.ctx.stroke()/*绘制分数点*/for (var i = 0; i < this.numofSide; i++) {if (pointScore[i]==maxScore && this.progress >= maxScore) {this.ctx.shadowBlur = shadowBlurthis.ctx.shadowColor = 'white'if (shadowBlur >= 40) {twinkleSpeed = -.2}if (shadowBlur <= 1) {twinkleSpeed = .2}shadowBlur += twinkleSpeed}this.ctx.beginPath()this.ctx.strokeStyle = 'white'this.ctx.setLineDash([])this.ctx.lineWidth = myCanvas.height*.004var r = this.r*polygonStyle.points.sizeif (maxScore===0) {this.ctx.arc(this.pointX, this.pointY, r, 0, 2*Math.PI, false)} else {this.ctx.arc(this.pointX+this.progress*pointScore[i]*0.94/maxScore/100*this.r*Math.sin(2*Math.PI*i/this.numofSide), this.pointY+this.progress*pointScore[i]*0.94/maxScore/100*this.r*Math.cos(2*Math.PI*i/this.numofSide), r, 0, 2*Math.PI, false)}this.ctx.fillStyle = '#FCF3DF'this.ctx.fill()this.ctx.stroke()}
}Polygon.prototype.render = function(callback) {if (this.animationFrame >= 1000/16*1.5 || this.isScale===false) {this.isScale = falsethis.draw()callback && callback()} else {this.update()this.scale()}
}Polygon.prototype.drawTip = function(text, color, x, y, fontSize, alpha) {this.ctx.beginPath()this.ctx.font = fontSize+'px 微软雅黑'this.ctx.fillStyle = 'rgba(75,66,66,'+alpha+')'this.ctx.textAlign = 'center'this.ctx.fillText(text, x, y)this.ctx.closePath()
}var layer1 = new Polygon({pointX : myCanvas.width/2*polygonStyle.translate.x,pointY : myCanvas.height/2*polygonStyle.translate.y,numofSide : numofSide,// lengthofSide: 100,r : myCanvas.width/3.5,strokeStyle : polygonStyle.polygonBgColor[0],fillStyle : polygonStyle.polygonBgColor[0],isFill : true,isScale : true,shadow : polygonStyle.Bgshadow
}, ctx)
var layer2 = new Polygon({pointX : myCanvas.width/2*polygonStyle.translate.x,pointY : myCanvas.height/2*polygonStyle.translate.y,numofSide : numofSide,r : myCanvas.width/3.5*2/3,fillStyle : polygonStyle.polygonBgColor[1],strokeStyle : polygonStyle.polygonBgColor[1],isFill : true,isScale : true
}, ctx)
var layer3 = new Polygon({pointX : myCanvas.width/2*polygonStyle.translate.x,pointY : myCanvas.height/2*polygonStyle.translate.y,numofSide : numofSide,r : myCanvas.width/3.5*1/3,fillStyle : polygonStyle.polygonBgColor[2],strokeStyle : polygonStyle.polygonBgColor[2],isFill : true,isScale : true
}, ctx)var totalFrame = 0
var change = 8
var alpha = 0
var change2 = 8
var alpha2 = 0
init.animationLoop = function() {totalFrame++ctx.clearRect(0, 0, myCanvas.width, myCanvas.height)for (var i = 0, len = polygonArr.length; i < len; i++) {if (totalFrame > 1000/16*1.5*1/5*i) {polygonArr[i].render()if (i===len-1) {polygonArr[i].render(function() {polygonArr[0].drawLine()for (var i = 2; i >= 0; i--) {let bgColor = polygonStyle.Reminder.bgColor[i], text = polygonStyle.Reminder.text[i],specX = 0,color = polygonStyle.Reminder.color,y = (polygonStyle.Reminder.coordinate.y*polygonStyle.translate.y)*myCanvas.height+myCanvas.height/8*change/8,width = polygonStyle.Reminder.width*myCanvas.heightif (i===0) {specX = width*polygonStyle.Reminder.fontSize}let x = polygonStyle.Reminder.coordinate.x*myCanvas.width+(2-i)*myCanvas.width*1/4+specXpolygonArr[0].drawReminder(text, color, x, y, width, width, bgColor, alpha)if (change > 0) {change -= 8/60alpha += 1/30}}for (var i = 0, len = pointScore.length; i < len; i++) {var restR = Radius*1/30var x = myCanvas.width/2*polygonStyle.translate.x+(Radius)*Math.sin(2*Math.PI*i/pointScore.length) -10var y = myCanvas.height/2*polygonStyle.translate.y+(Radius)*Math.cos(2*Math.PI*i/pointScore.length)+myCanvas.height/8*change2/8 + 25var fontSize = init.defaultStyle.tipText.fontSizeif (Array.isArray(polygonStyle.tipText.color)) {var color = polygonStyle.tipText.color[i]} else {var color = polygonStyle.tipText.color}polygonArr[0].drawTip(pointText[i], color, x, y, fontSize, alpha2)if (change2 > 0) {change2 -= 8/60alpha2 += 1/30} else {polygonArr[0].drawPoint()}}})}}}if (true) {init.animation = requestAnimationFrame(init.animationLoop)}
}
requestAnimationFrame(init.animationLoop)复制代码
github:https://github.com/dashaosunxigui/canvas-redar
转载于:https://juejin.im/post/5b188acaf265da6e5a2056c2
canvas 实现雷达图相关推荐
- 利用Canvas绘制雷达图
雷达图(蜘蛛网图)是一种常见的数据分析图表,本文采用canvas来绘制雷达图,并最终封装成一个小组件.首先来看一下最终的效果图: 如何画正多边形 以正五边形雷达图为例(其他任意正多边形也一样),如下图 ...
- HTML5 Canvas制作雷达图实战
雷达图又叫蜘蛛网图,是一种对各项数据查看很明显的表现图,在很多游戏中,对游戏中的每个角色的分析图一般也用这种图. 下面,用HTML5的Cavas来实现雷达图. 效果 一.创建Canvas var mW ...
- html5雷达图绘制,Canvas 绘制雷达图
最近做的一个需求,场景之一是绘制一个雷达图,找了一圈,似乎 AntV 下的 F2 很适合拿来主义: 但是接着又考虑了一下,我当前所做的项目并不是可视化项目,今后大概率也不会有这种可视化图表的需求,只是 ...
- canvas绘制雷达图
最近做的一个需求,场景之一是绘制一个雷达图,找了一圈,似乎 AntV 下的 F2 很适合拿来主义: 但是接着又考虑了一下,我当前所做的项目并不是可视化项目,今后大概率也不会有这种可视化图表的需求,只是 ...
- Canvas实现雷达图效果
一.使用Canvas实现原理 这里最难的就是坐标系旋转后的坐标使用.其他就是利用数学公式等计算一些数据. 还有就是一步一步的调试,最终才能实现想要的效果. 如果光看已经完成的代码可能体会不到具体的逻辑 ...
- html雷达图代码,HTML5 Canvas制作雷达图实战
代码实现 Document canvas{ } var mW = 400; var mH = 400; var mData = [ ['速度77', 21], ['力量72', 56], ['防守46 ...
- 带着canvas去流浪系列之六 绘制雷达图
[摘要] 用canvas原生API实现百度Echarts基本图表. 示例代码托管在:http://www.github.com/dashnowords/blogs 一. 任务说明 使用原生canvas ...
- 【带着canvas去流浪(6)】绘制雷达图
目录 一. 任务说明 二. 重点提示 三. 示例代码 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文 ...
- echarts 雷达图_【带着canvas去流浪】绘制雷达图
使用原生canvasAPI绘制雷达图.(截图以及数据来自于百度Echarts官方示例库[查看示例链接]). 二. 重点提示 雷达图绘制的看起来并不复杂,无非就是一些路径点的连线,其中的难点都在于一些细 ...
最新文章
- SpringMVC处理Date类型的成员变量方法
- 题目1465:最简真分数
- a*算法的优缺点_五种聚类算法一览与python实现
- mysql 查数据 default无效_导入mysql数据的时候提示Field * doesn't have a default value解决方法...
- 大端和小端的判断及转换
- mysql 多条记录选择一套_2020-11-09-Mysql(练习题第一套)
- 山西专科学校计算机专业排名,河南单招计算机专业专科学校排名
- 微课|《Python编程基础与案例集锦(中学版)》第3章例题讲解(2)
- monkey测试_adb monkey压力测试检测安卓手机的5大步骤,你知道吗?
- day6常用模块,数据库操作
- 基于大数据的舆情分析系统架构 - 架构篇
- 计算机桌面背景在哪里调整,电脑中怎么设置桌面背景
- 云计算笔记之admin-day-05-管理用户和组、tar备份与恢复、NTP时间同步、cron计划任务、总结和答疑
- (二)D3D9视频显示的流程与初始化
- Google Paly 上传支持64 位设备APP
- RPG Maker MV 密码宝箱
- Lync 2010升级到Lync 2013之更新CU2!
- 哈拉比数据库;为了家人
- 对话NASA传奇宇航员:太空中不止有未知与神秘,还有下午茶和电影
- vbox 虚拟机添加usb