如何在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 相关文章推荐
通过修改referer下载文件的方法
May 11 Javascript
JavaScript将页面表格导出为Excel的具体实现
Dec 27 Javascript
jquery无刷新验证邮箱地址实现实例
Feb 19 Javascript
JS操作COOKIE实现备忘记录的方法
Apr 01 Javascript
JS控制层作圆周运动的方法
Jun 20 Javascript
Jquery与Bootstrap实现后台管理页面增删改查功能示例
Jan 22 Javascript
Ajax基础知识详解
Feb 17 Javascript
Vue.js开发环境快速搭建教程
Mar 17 Javascript
强大的JavaScript响应式图表Chartist.js的使用
Sep 13 Javascript
自定义vue组件发布到npm的方法
May 09 Javascript
原生JS实现的轮播图功能详解
Aug 06 Javascript
详解js访问对象的属性和方法
Oct 25 Javascript
jquery插件实现图片悬浮
详解CocosCreator消息分发机制
Apr 16 #Javascript
CocosCreator入门教程之网络通信
Apr 16 #Javascript
JavaScript嵌入百度地图API的最详细方法
用javascript制作qq注册动态页面
利用javaScript处理常用事件详解
Apr 14 #Javascript
带你彻底理解JavaScript中的原型对象
Apr 14 #Javascript
You might like
php目录管理函数小结
2008/09/10 PHP
php db类库进行数据库操作
2009/03/19 PHP
php后门URL的防范
2013/11/12 PHP
浅析php单例模式
2014/11/25 PHP
PHP中的switch语句的用法实例详解
2015/10/21 PHP
php in_array() 检查数组中是否存在某个值详解
2016/11/23 PHP
PHP判断一个数组是另一个数组子集的方法详解
2017/07/31 PHP
Javascript判断对象是否相等实现代码
2013/03/18 Javascript
深入理解jQuery中live与bind方法的区别
2013/12/18 Javascript
drag-and-drop实现图片浏览器预览
2015/08/06 Javascript
js数组常用操作方法小结(增加,删除,合并,分割等)
2016/08/02 Javascript
Bootstrap3 datetimepicker控件使用实例
2016/12/13 Javascript
js 去掉字符串前后空格实现代码集合
2017/03/25 Javascript
Node.js中的http请求客户端示例(request client)
2017/05/04 Javascript
JavaScript闭包和回调详解
2017/08/09 Javascript
js实现鼠标跟随运动效果
2020/08/02 Javascript
Node.js实现发送邮件功能
2017/11/06 Javascript
axios 封装上传文件的请求方法
2018/09/26 Javascript
JS实现的杨辉三角【帕斯卡三角形】算法示例
2019/02/26 Javascript
layer.open提交子页面的form和layedit文本编辑内容的方法
2019/09/27 Javascript
[17:13]DOTA2 HEROS教学视频教你分分钟做大人-斯拉克
2014/06/13 DOTA
Python中字典映射类型的学习教程
2015/08/20 Python
利用Python开发微信支付的注意事项
2016/08/19 Python
Python获取昨天、今天、明天开始、结束时间戳的方法
2018/06/01 Python
Python中return self的用法详解
2018/07/27 Python
python实现大学人员管理系统
2019/10/25 Python
python进程间通信Queue工作过程详解
2019/11/01 Python
html5 canvas手势解锁源码分享
2020/01/07 HTML / CSS
瑞典耳机品牌:URBANISTA
2019/12/03 全球购物
牵手50新加坡:专为黄金岁月的单身人士而设的交友网站
2020/08/16 全球购物
导游的职业规划书范文
2013/12/27 职场文书
企业宣传标语
2014/06/09 职场文书
班组长安全工作职责
2014/07/15 职场文书
计划生育证明书写要求
2014/09/17 职场文书
html+css实现滚动到元素位置显示加载动画效果
2021/08/02 HTML / CSS
Python 实现Mac 屏幕截图详解
2021/10/05 Python