如何在CocosCreator里画个炫酷的雷达图


Posted in Javascript onApril 16, 2021

前言

雷达图(Radar Chart) 也称为网络图、星图或蜘蛛网图。

是以从同一点开始的轴上表示的三个或更多个定量变量的二维图表的形式显示多元数据的图形方法。

适用于显示三个或更多的维度的变量。

如何在CocosCreator里画个炫酷的雷达图

雷达图常用于?数据统计或对比,对于查看哪些变量具有相似的值、变量之间是否有异常值都很有用。

同时在不少游戏中都有雷达图的身影,可以很直观地展示并对比一些数据。

例如王者荣耀中的对战资料中就用到了:

如何在CocosCreator里画个炫酷的雷达图

那么在本篇文章中,皮皮就来分享下在 Cocos Creator 中如何利用 Graphics 组件来绘制炫酷的雷达图~

文中会对原始代码进行一定的削减以保证阅读体验。

雷达图组件:https://gitee.com/ifaswind/eazax-ccc/blob/master/components/RadarChart.ts


预览

先来看看效果吧~

在线预览:https://ifaswind.gitee.io/eazax-cases/?case=radarChart

两条数据

如何在CocosCreator里画个炫酷的雷达图

缓动数据

如何在CocosCreator里画个炫酷的雷达图

花里胡哨

如何在CocosCreator里画个炫酷的雷达图

艺术就是爆炸

如何在CocosCreator里画个炫酷的雷达图

逐渐偏离主题

如何在CocosCreator里画个炫酷的雷达图

正文

Graphics 组件

在我们正式开始制作雷达图之前,让我们先来大概了解一下 Cocos Creator 引擎中的 Graphics 组件。

Graphics 组件继承于 cc.RenderComponent,利用该组件我们可以实现画板和表格之类的功能。

属性(Properties)

下面是我们本次将会用到的属性:

  • lineCap:设置或返回线条两端的样式(无、圆形线帽或方形线帽)
  • lineJoin:设置或返回两条线相交时的拐角样式(斜角、圆角或尖角)
  • lineWidth:设置或返回当前画笔的粗细(线条的宽度)
  • strokeColor:设置或返回当前画笔的颜色
  • fillColor:设置或返回填充用的颜色(油漆桶)

函数(Functions)

下面是我们本次将会用到的函数:

  • moveTo(x, y):抬起画笔并移动到指定位置(不创建线条)
  • lineTo(x, y):放下画笔并创建一条直线至指定位置
  • circle(cx, cy, r):在指定位置(圆心)画一个圆
  • close():闭合已创建的线条(相当于 lineTo(起点)
  • stroke():绘制已创建(但未被绘制)的线条(将线条想象成默认透明的,此行为则是赋予线条颜色)
  • fill():填充当前线条包围的区域(如果线条没有闭合则会尝试”模拟闭合“起点和终点)
  • clear():擦掉当前画板上的所有东西

Graphics 组件文档:http://docs.cocos.com/creator/manual/zh/components/graphics.html?h=graphics

画网格

先来看看一个标准的雷达图有啥特点:

如何在CocosCreator里画个炫酷的雷达图

发现了吗?雷达图的基本特点如下:

  • 有 3 条或以上的轴线
  • 轴与轴之间的夹角相同
  • 每条轴上除中心点外应至少有 1 个刻度
  • 每条轴上都有相同的刻度
  • 刻度与刻度之间的距离也相同
  • 轴之间的刻度相连形成网格线

计算轴线角度

先算出轴之间的夹角度数 [ 360 ÷ 轴数 ],再计算所有轴的角度:

this.angles = [];
// 轴间夹角
const iAngle = 360 / this.axes;
for (let i = 0; i < this.axes; i++) {
    // 计算
    const angle = iAngle * i;
    this.angles.push(angle);
}

计算刻度坐标

雷达图至少拥有 3 条轴,且每条轴上都应有 1 个或以上的刻度(不包含中心点)

如何在CocosCreator里画个炫酷的雷达图

所以我们需使用一个二维数组来保存所有刻度的坐标,从最外层(即轴线的末端)的刻度开始记录,方便我们绘制时读取:

// 创建一个二维数组
let scalesSet: cc.Vec2[][] = [];
for (let i = 0; i < 轴上刻度个数; i++) {
    // 用来保存当前层上的刻度坐标
    let scales = [];
    // 计算刻度在轴上的位置
    const length = 轴线长度 - (轴线长度 / 轴上刻度个数 * i);
    for (let j = 0; j < this.angles.length; j++) {
        // 将角度转为弧度
        const radian = (Math.PI / 180) * this.angles[j];
        // 根据三角公式计算刻度相对于中心点(0, 0)的坐标
        const pos = cc.v2(length * Math.cos(radian), length * Math.sin(radian));
        // 推进数组
        scales.push(pos);
    }
    // 推进二维数组
    scalesSet.push(scales);
}

绘制轴线和外网格线

轴线

连接中心点 (0, 0) 和最外层 scalesSet[0] 的刻度即为轴线:

// 遍历全部最外层的刻度
for (let i = 0; i < scalesSet[0].length; i++) {
    // 画笔移动至中心点
    this.graphics.moveTo(0, 0);
    // 创建线条
    this.graphics.lineTo(scalesSet[0][i].x, scalesSet[0][i].y);
}

外网格线

连接所有轴上最外层 scalesSet[0] 的刻度即形成外网格线:

// 画笔移动至第一个点
this.graphics.moveTo(scalesSet[0][0].x, scalesSet[0][0].y);
for (let i = 1; i < scalesSet[0].length; i++) {
    // 创建线条
    this.graphics.lineTo(scalesSet[0][i].x, scalesSet[0][i].y);
}
// 闭合当前线条(外网格线)
this.graphics.close();

填充并绘制

这里需要注意先填充颜色再绘制线条,要不然轴线和网格线就被挡住了:

// 填充线条包围的空白区域
this.graphics.fill();
// 绘制已创建的线条(轴线和外网格线)
this.graphics.stroke();

于是现在我们就有了这么个玩意儿:

如何在CocosCreator里画个炫酷的雷达图

绘制内网格线

当刻度大于 1 个时就需要绘制内网格线,从刻度坐标集的下标 1 开始绘制:

// 刻度大于 1 个时才绘制内网格线
if (scalesSet.length > 1) {
    // 从下边 1 开始(下标 0 是外网格线)
    for (let i = 1; i < scalesSet.length; i++) {
        // 画笔移动至第一个点
        this.graphics.moveTo(scalesSet[i][0].x, scalesSet[i][0].y);
        for (let j = 1; j < scalesSet[i].length; j++) {
            // 创建线条
            this.graphics.lineTo(scalesSet[i][j].x, scalesSet[i][j].y);
        }
        // 闭合当前线条(内网格线)
        this.graphics.close();
    }
    // 绘制已创建的线条(内网格线)
    this.graphics.stroke();
}

就这样我们雷达图的底子就画好啦:

如何在CocosCreator里画个炫酷的雷达图

画数据 

编写画线逻辑之前,先确定一下我们需要的数据结构:

  • 数值数组(必须,小数形式的比例,至少包含 3 个值)
  • 线的宽度(可选,不指定则使用默认值)
  • 线的颜色(可选,不指定则使用默认值)
  • 填充的颜色(可选,不指定则使用默认值)
  • 节点的颜色(可选,不指定则使用默认值)

具体的数据结构如下(导出类型方便外部使用):

/**
 * 雷达图数据
 */
export interface RadarChartData {

    /** 数值 */
    values: number[];

    /** 线的宽度 */
    lineWidth?: number;

    /** 线的颜色 */
    lineColor?: cc.Color;

    /** 填充的颜色 */
    fillColor?: cc.Color;

    /** 节点的颜色 */
    joinColor?: cc.Color;

}

绘制数据

绘制数据比较简单,我们只需要算出数据点在图表中的位置,并将数据连起来就好了。

draw 函数中我们接收一份或以上的雷达图数据,并按照顺序遍历绘制出来(⚠️长代码警告):

/**
 * 绘制数据
 * @param data 数据
 */
public draw(data: RadarChartData | RadarChartData[]) {
    // 处理数据
    const datas = Array.isArray(data) ? data : [data];

    // 开始绘制数据
    for (let i = 0; i < datas.length; i++) {
        // 装填染料
        this.graphics.strokeColor = datas[i].lineColor || defaultOptions.lineColor;
        this.graphics.fillColor = datas[i].fillColor || defaultOptions.fillColor;
        this.graphics.lineWidth = datas[i].lineWidth || defaultOptions.lineWidth;

        // 计算节点坐标
        let coords = [];
        for (let j = 0; j < this.axes; j++) {
            const value = datas[i].values[j] > 1 ? 1 : datas[i].values[j];
            const length = value * this.axisLength;
            const radian = (Math.PI / 180) * this.angles[j];
            const pos = cc.v2(length * Math.cos(radian), length * Math.sin(radian))
            coords.push(pos);
        }

        // 创建线条
        this.graphics.moveTo(coords[0].x, coords[0].y);
        for (let j = 1; j < coords.length; j++) {
            this.graphics.lineTo(coords[j].x, coords[j].y);
        }
        this.graphics.close(); // 闭合线条
        
        // 填充包围区域
        this.graphics.fill();
        // 绘制线条
        this.graphics.stroke();

        // 绘制数据节点
        for (let j = 0; j < coords.length; j++) {
            // 大圆
            this.graphics.strokeColor = datas[i].lineColor || defaultOptions.lineColor;
            this.graphics.circle(coords[j].x, coords[j].y, 2);
            this.graphics.stroke();
            // 小圆
            this.graphics.strokeColor = datas[i].joinColor || defaultOptions.joinColor;
            this.graphics.circle(coords[j].x, coords[j].y, .65);
            this.graphics.stroke();
        }

    }
}

到这里我们已经成功制作了一个可用的雷达图:

如何在CocosCreator里画个炫酷的雷达图

但是!我们的征途是星辰大海!必须加点料!

完全静态的雷达图实在是太无趣太普通,得想想办法让它动起来!

我们的雷达图数据的数值是数组形式,想到怎么样才能让这些数值动起来了吗?

得益于 Cocos Creator 为我们提供的 Tween 缓动系统,让复杂的数据动起来变得异常简单!

我们只需要这样,这样,然后那样,是不是很简单?

cc.tween 支持缓动任意对象的任意属性

缓动系统:http://docs.cocos.com/creator/manual/zh/scripting/tween.html

另外我在《一个全能的挖孔 Shader》中也是使用了缓动系统来让挖孔动起来~

在线预览:https://ifaswind.gitee.io/eazax-cases/?case=newGuide

我的思路是:

  1. 将当前的数据保存到当前实例的 this.curDatas
  2. 接收到新的数据时,使用 cc.tweenthis.curData 的属性进行缓动
  3. update 中调用 draw 函数,每帧都重新绘制 this.curDatas 中的数据

每帧更新

// 当前雷达图数据
private curDatas: RadarChartData[] = [];

protected update() {
    if (!this.keepUpdating) return;
    // 绘制当前数据
    this.draw(this.curDatas);
}

缓动数据

/**
 * 缓动绘制
 * @param data 目标数据
 * @param duration 动画时长
 */
public to(data: RadarChartData | RadarChartData[], duration: number) {
    // 处理重复调用
    this.unscheduleAllCallbacks();
    
    // 包装单条数据
    const datas = Array.isArray(data) ? data : [data];

    // 打开每帧更新
    this.keepUpdating = true;

    // 动起来!
    for (let i = 0; i < datas.length; i++) {
        // 数值动起来!
        // 遍历数据中的全部数值,逐个让他们动起来!
        for (let j = 0; j < this.curDatas[i].values.length; j++) {
            // 限制最大值为 1(即 100%)
            const value = datas[i].values[j] > 1 ? 1 : datas[i].values[j];
            cc.tween(this.curDatas[i].values)
                .to(duration, { [j]: value })
                .start();
        }
        // 样式动起来!
        // 没有指定则使用原来的样式!
        cc.tween(this.curDatas[i])
            .to(duration, {
                lineWidth: datas[i].lineWidth || this.curDatas[i].lineWidth,
                lineColor: datas[i].lineColor || this.curDatas[i].lineColor,
                fillColor: datas[i].fillColor || this.curDatas[i].fillColor,
                joinColor: datas[i].joinColor || this.curDatas[i].joinColor
            })
            .start();
    }

    this.scheduleOnce(() => {
        // 关闭每帧更新
        this.keepUpdating = false;
    }, duration);
}

数值和样式都动起来了:

如何在CocosCreator里画个炫酷的雷达图

雷达图组件:https://gitee.com/ifaswind/eazax-ccc/blob/master/components/RadarChart.ts

以上就是如何在CocosCreator里画个炫酷的雷达图的详细内容,更多关于CocosCreator画个雷达图的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
给Flash加一个超链接(推荐使用透明层)兼容主流浏览器
Jun 09 Javascript
使用非html5实现js板连连看游戏示例代码
Sep 22 Javascript
jquery attr方法获取input的checked属性问题
May 26 Javascript
浅析Node.js中的内存泄漏问题
Jun 23 Javascript
基于javascript实现浏览器滚动条快到底部时自动加载数据
Nov 30 Javascript
jQuery实现的超简单点赞效果实例分析
Dec 31 Javascript
微信小程序实现分享到朋友圈功能
Jul 19 Javascript
vue 监听某个div垂直滚动条下拉到底部的方法
Sep 15 Javascript
vue基于element-ui的三级CheckBox复选框功能的实现代码
Oct 15 Javascript
JS前端广告拦截实现原理解析
Feb 17 Javascript
Node.js API详解之 net模块实例分析
May 18 Javascript
详解Vue项目的打包方式(生成dist文件)
Jan 18 Vue.js
jquery插件实现图片悬浮
详解CocosCreator消息分发机制
Apr 16 #Javascript
CocosCreator入门教程之网络通信
Apr 16 #Javascript
JavaScript嵌入百度地图API的最详细方法
用javascript制作qq注册动态页面
利用javaScript处理常用事件详解
Apr 14 #Javascript
带你彻底理解JavaScript中的原型对象
Apr 14 #Javascript
You might like
国内咖啡文化
2021/03/03 咖啡文化
提升PHP执行速度全攻略
2006/10/09 PHP
比较全的JS checkbox全选、取消全选、删除功能代码
2008/12/19 Javascript
Extjs ajax同步请求时post方式参数发送方式
2009/08/05 Javascript
基于Jquery实现表格动态分页实现代码
2011/06/21 Javascript
JS操作Cookie写入和读取实例代码
2013/10/20 Javascript
与Math.pow 相反的函数使用介绍
2014/08/04 Javascript
一个JavaScript去除字符串末尾的空白实例代码
2014/09/22 Javascript
js绘制圆形和矩形的方法
2015/08/05 Javascript
ES6中非常实用的新特性介绍
2016/03/10 Javascript
微信小程序开发教程-手势解锁实例
2017/01/06 Javascript
angular学习之ngRoute路由机制
2017/04/12 Javascript
Node.js中的require.resolve方法使用简介
2017/04/23 Javascript
详解jquery选择器的原理
2017/08/01 jQuery
vue+mockjs模拟数据实现前后端分离开发的实例代码
2017/08/08 Javascript
vue2 router 动态传参,多个参数的实例
2017/11/10 Javascript
vue结合Echarts实现点击高亮效果的示例
2018/03/17 Javascript
vue实现选项卡及选项卡切换效果
2018/04/24 Javascript
JS+HTML实现的圆形可点击区域示例【3种方法】
2018/08/01 Javascript
vue element 生成无线级左侧菜单的实现代码
2019/08/21 Javascript
如何使用vue slot创建一个模态框的实例代码
2020/05/24 Javascript
返回上一个url并刷新界面的js代码
2020/09/12 Javascript
[53:13]DOTA2-DPC中国联赛 正赛 DLG vs PHOENIX BO3 第三场 1月18日
2021/03/11 DOTA
[10:05]DOTA2-DPC中国联赛 正赛 iG vs PSG.LGD 选手采访
2021/03/11 DOTA
玩转python爬虫之cookie使用方法
2016/02/17 Python
Python深入06——python的内存管理详解
2016/12/07 Python
解读! Python在人工智能中的作用
2017/11/14 Python
python flask中静态文件的管理方法
2018/03/20 Python
python实现列表中由数值查到索引的方法
2018/06/27 Python
css3实现背景颜色渐变让图片不再是唯一的实现方式
2012/12/18 HTML / CSS
奶茶店创业计划书
2014/08/14 职场文书
党的群众路线教育实践活动心得体会(乡镇)
2014/11/03 职场文书
2014年个人工作总结范文
2014/11/07 职场文书
2014年禁毒工作总结
2014/11/24 职场文书
Mongo服务重启异常问题的处理方法
2021/07/01 MongoDB
搭建zabbix监控以及邮件报警的超级详细教学
2022/07/15 Servers