欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何在CocosCreator里畫(huà)個(gè)炫酷的雷達(dá)圖

 更新時(shí)間:2021年04月15日 16:28:38   作者:文弱書(shū)生陳皮皮  
這篇文章主要介紹了如何在CocosCreator里畫(huà)個(gè)炫酷的雷達(dá)圖,對(duì)Graphics感興趣的同學(xué),一定要看看,并且把代碼實(shí)踐一下

前言

雷達(dá)圖(Radar Chart) 也稱為網(wǎng)絡(luò)圖、星圖或蜘蛛網(wǎng)圖。

是以從同一點(diǎn)開(kāi)始的軸上表示的三個(gè)或更多個(gè)定量變量的二維圖表的形式顯示多元數(shù)據(jù)的圖形方法。

適用于顯示三個(gè)或更多的維度的變量。

雷達(dá)圖常用于📚數(shù)據(jù)統(tǒng)計(jì)或?qū)Ρ?,?duì)于查看哪些變量具有相似的值、變量之間是否有異常值都很有用。

同時(shí)在不少游戲中都有雷達(dá)圖的身影,可以很直觀地展示并對(duì)比一些數(shù)據(jù)。

例如王者榮耀中的對(duì)戰(zhàn)資料中就用到了:

那么在本篇文章中,皮皮就來(lái)分享下在 Cocos Creator 中如何利用 Graphics 組件來(lái)繪制炫酷的雷達(dá)圖~

文中會(huì)對(duì)原始代碼進(jìn)行一定的削減以保證閱讀體驗(yàn)。

雷達(dá)圖組件:https://gitee.com/ifaswind/eazax-ccc/blob/master/components/RadarChart.ts


預(yù)覽

先來(lái)看看效果吧~

在線預(yù)覽:https://ifaswind.gitee.io/eazax-cases/?case=radarChart

兩條數(shù)據(jù)

緩動(dòng)數(shù)據(jù)

花里胡哨

藝術(shù)就是爆炸

逐漸偏離主題

正文

Graphics 組件

在我們正式開(kāi)始制作雷達(dá)圖之前,讓我們先來(lái)大概了解一下 Cocos Creator 引擎中的 Graphics 組件。

Graphics 組件繼承于 cc.RenderComponent,利用該組件我們可以實(shí)現(xiàn)畫(huà)板和表格之類(lèi)的功能。

屬性(Properties)

下面是我們本次將會(huì)用到的屬性:

  • lineCap:設(shè)置或返回線條兩端的樣式(無(wú)、圓形線帽或方形線帽)
  • lineJoin:設(shè)置或返回兩條線相交時(shí)的拐角樣式(斜角、圓角或尖角)
  • lineWidth:設(shè)置或返回當(dāng)前畫(huà)筆的粗細(xì)(線條的寬度)
  • strokeColor:設(shè)置或返回當(dāng)前畫(huà)筆的顏色
  • fillColor:設(shè)置或返回填充用的顏色(油漆桶)

函數(shù)(Functions)

下面是我們本次將會(huì)用到的函數(shù):

  • moveTo(x, y):抬起畫(huà)筆并移動(dòng)到指定位置(不創(chuàng)建線條)
  • lineTo(x, y):放下畫(huà)筆并創(chuàng)建一條直線至指定位置
  • circle(cx, cy, r):在指定位置(圓心)畫(huà)一個(gè)圓
  • close():閉合已創(chuàng)建的線條(相當(dāng)于 lineTo(起點(diǎn))
  • stroke():繪制已創(chuàng)建(但未被繪制)的線條(將線條想象成默認(rèn)透明的,此行為則是賦予線條顏色)
  • fill():填充當(dāng)前線條包圍的區(qū)域(如果線條沒(méi)有閉合則會(huì)嘗試”模擬閉合“起點(diǎn)和終點(diǎn))
  • clear():擦掉當(dāng)前畫(huà)板上的所有東西

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

畫(huà)網(wǎng)格

先來(lái)看看一個(gè)標(biāo)準(zhǔn)的雷達(dá)圖有啥特點(diǎn):

發(fā)現(xiàn)了嗎?雷達(dá)圖的基本特點(diǎn)如下:

  • 有 3 條或以上的軸線
  • 軸與軸之間的夾角相同
  • 每條軸上除中心點(diǎn)外應(yīng)至少有 1 個(gè)刻度
  • 每條軸上都有相同的刻度
  • 刻度與刻度之間的距離也相同
  • 軸之間的刻度相連形成網(wǎng)格線

計(jì)算軸線角度

先算出軸之間的夾角度數(shù) [ 360 ÷ 軸數(shù) ],再計(jì)算所有軸的角度:

this.angles = [];
// 軸間夾角
const iAngle = 360 / this.axes;
for (let i = 0; i < this.axes; i++) {
    // 計(jì)算
    const angle = iAngle * i;
    this.angles.push(angle);
}

計(jì)算刻度坐標(biāo)

雷達(dá)圖至少擁有 3 條軸,且每條軸上都應(yīng)有 1 個(gè)或以上的刻度(不包含中心點(diǎn))

所以我們需使用一個(gè)二維數(shù)組來(lái)保存所有刻度的坐標(biāo),從最外層(即軸線的末端)的刻度開(kāi)始記錄,方便我們繪制時(shí)讀?。?/p>

// 創(chuàng)建一個(gè)二維數(shù)組
let scalesSet: cc.Vec2[][] = [];
for (let i = 0; i < 軸上刻度個(gè)數(shù); i++) {
    // 用來(lái)保存當(dāng)前層上的刻度坐標(biāo)
    let scales = [];
    // 計(jì)算刻度在軸上的位置
    const length = 軸線長(zhǎng)度 - (軸線長(zhǎng)度 / 軸上刻度個(gè)數(shù) * i);
    for (let j = 0; j < this.angles.length; j++) {
        // 將角度轉(zhuǎn)為弧度
        const radian = (Math.PI / 180) * this.angles[j];
        // 根據(jù)三角公式計(jì)算刻度相對(duì)于中心點(diǎn)(0, 0)的坐標(biāo)
        const pos = cc.v2(length * Math.cos(radian), length * Math.sin(radian));
        // 推進(jìn)數(shù)組
        scales.push(pos);
    }
    // 推進(jìn)二維數(shù)組
    scalesSet.push(scales);
}

繪制軸線和外網(wǎng)格線

軸線

連接中心點(diǎn) (0, 0) 和最外層 scalesSet[0] 的刻度即為軸線:

// 遍歷全部最外層的刻度
for (let i = 0; i < scalesSet[0].length; i++) {
    // 畫(huà)筆移動(dòng)至中心點(diǎn)
    this.graphics.moveTo(0, 0);
    // 創(chuàng)建線條
    this.graphics.lineTo(scalesSet[0][i].x, scalesSet[0][i].y);
}

外網(wǎng)格線

連接所有軸上最外層 scalesSet[0] 的刻度即形成外網(wǎng)格線:

// 畫(huà)筆移動(dòng)至第一個(gè)點(diǎn)
this.graphics.moveTo(scalesSet[0][0].x, scalesSet[0][0].y);
for (let i = 1; i < scalesSet[0].length; i++) {
    // 創(chuàng)建線條
    this.graphics.lineTo(scalesSet[0][i].x, scalesSet[0][i].y);
}
// 閉合當(dāng)前線條(外網(wǎng)格線)
this.graphics.close();

填充并繪制

這里需要注意先填充顏色再繪制線條,要不然軸線和網(wǎng)格線就被擋住了:

// 填充線條包圍的空白區(qū)域
this.graphics.fill();
// 繪制已創(chuàng)建的線條(軸線和外網(wǎng)格線)
this.graphics.stroke();

于是現(xiàn)在我們就有了這么個(gè)玩意兒:

繪制內(nèi)網(wǎng)格線

當(dāng)刻度大于 1 個(gè)時(shí)就需要繪制內(nèi)網(wǎng)格線,從刻度坐標(biāo)集的下標(biāo) 1 開(kāi)始繪制:

// 刻度大于 1 個(gè)時(shí)才繪制內(nèi)網(wǎng)格線
if (scalesSet.length > 1) {
    // 從下邊 1 開(kāi)始(下標(biāo) 0 是外網(wǎng)格線)
    for (let i = 1; i < scalesSet.length; i++) {
        // 畫(huà)筆移動(dòng)至第一個(gè)點(diǎn)
        this.graphics.moveTo(scalesSet[i][0].x, scalesSet[i][0].y);
        for (let j = 1; j < scalesSet[i].length; j++) {
            // 創(chuàng)建線條
            this.graphics.lineTo(scalesSet[i][j].x, scalesSet[i][j].y);
        }
        // 閉合當(dāng)前線條(內(nèi)網(wǎng)格線)
        this.graphics.close();
    }
    // 繪制已創(chuàng)建的線條(內(nèi)網(wǎng)格線)
    this.graphics.stroke();
}

就這樣我們雷達(dá)圖的底子就畫(huà)好啦:

畫(huà)數(shù)據(jù) 

編寫(xiě)畫(huà)線邏輯之前,先確定一下我們需要的數(shù)據(jù)結(jié)構(gòu):

  • 數(shù)值數(shù)組(必須,小數(shù)形式的比例,至少包含 3 個(gè)值)
  • 線的寬度(可選,不指定則使用默認(rèn)值)
  • 線的顏色(可選,不指定則使用默認(rèn)值)
  • 填充的顏色(可選,不指定則使用默認(rèn)值)
  • 節(jié)點(diǎn)的顏色(可選,不指定則使用默認(rèn)值)

具體的數(shù)據(jù)結(jié)構(gòu)如下(導(dǎo)出類(lèi)型方便外部使用):

/**
 * 雷達(dá)圖數(shù)據(jù)
 */
export interface RadarChartData {

    /** 數(shù)值 */
    values: number[];

    /** 線的寬度 */
    lineWidth?: number;

    /** 線的顏色 */
    lineColor?: cc.Color;

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

    /** 節(jié)點(diǎn)的顏色 */
    joinColor?: cc.Color;

}

繪制數(shù)據(jù)

繪制數(shù)據(jù)比較簡(jiǎn)單,我們只需要算出數(shù)據(jù)點(diǎn)在圖表中的位置,并將數(shù)據(jù)連起來(lái)就好了。

draw 函數(shù)中我們接收一份或以上的雷達(dá)圖數(shù)據(jù),并按照順序遍歷繪制出來(lái)(⚠️長(zhǎng)代碼警告):

/**
 * 繪制數(shù)據(jù)
 * @param data 數(shù)據(jù)
 */
public draw(data: RadarChartData | RadarChartData[]) {
    // 處理數(shù)據(jù)
    const datas = Array.isArray(data) ? data : [data];

    // 開(kāi)始繪制數(shù)據(jù)
    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;

        // 計(jì)算節(jié)點(diǎn)坐標(biāo)
        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);
        }

        // 創(chuàng)建線條
        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(); // 閉合線條
        
        // 填充包圍區(qū)域
        this.graphics.fill();
        // 繪制線條
        this.graphics.stroke();

        // 繪制數(shù)據(jù)節(jié)點(diǎn)
        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();
        }

    }
}

到這里我們已經(jīng)成功制作了一個(gè)可用的雷達(dá)圖:

但是!我們的征途是星辰大海!必須加點(diǎn)料!

完全靜態(tài)的雷達(dá)圖實(shí)在是太無(wú)趣太普通,得想想辦法讓它動(dòng)起來(lái)!

我們的雷達(dá)圖數(shù)據(jù)的數(shù)值是數(shù)組形式,想到怎么樣才能讓這些數(shù)值動(dòng)起來(lái)了嗎?

得益于 Cocos Creator 為我們提供的 Tween 緩動(dòng)系統(tǒng),讓復(fù)雜的數(shù)據(jù)動(dòng)起來(lái)變得異常簡(jiǎn)單!

我們只需要這樣,這樣,然后那樣,是不是很簡(jiǎn)單?

cc.tween 支持緩動(dòng)任意對(duì)象的任意屬性

緩動(dòng)系統(tǒng):http://docs.cocos.com/creator/manual/zh/scripting/tween.html

另外我在《一個(gè)全能的挖孔 Shader》中也是使用了緩動(dòng)系統(tǒng)來(lái)讓挖孔動(dòng)起來(lái)~

在線預(yù)覽:https://ifaswind.gitee.io/eazax-cases/?case=newGuide

我的思路是:

  1. 將當(dāng)前的數(shù)據(jù)保存到當(dāng)前實(shí)例的 this.curDatas
  2. 接收到新的數(shù)據(jù)時(shí),使用 cc.tween 對(duì) this.curData 的屬性進(jìn)行緩動(dòng)
  3. update 中調(diào)用 draw 函數(shù),每幀都重新繪制 this.curDatas 中的數(shù)據(jù)

每幀更新

// 當(dāng)前雷達(dá)圖數(shù)據(jù)
private curDatas: RadarChartData[] = [];

protected update() {
    if (!this.keepUpdating) return;
    // 繪制當(dāng)前數(shù)據(jù)
    this.draw(this.curDatas);
}

緩動(dòng)數(shù)據(jù)

/**
 * 緩動(dòng)繪制
 * @param data 目標(biāo)數(shù)據(jù)
 * @param duration 動(dòng)畫(huà)時(shí)長(zhǎng)
 */
public to(data: RadarChartData | RadarChartData[], duration: number) {
    // 處理重復(fù)調(diào)用
    this.unscheduleAllCallbacks();
    
    // 包裝單條數(shù)據(jù)
    const datas = Array.isArray(data) ? data : [data];

    // 打開(kāi)每幀更新
    this.keepUpdating = true;

    // 動(dòng)起來(lái)!
    for (let i = 0; i < datas.length; i++) {
        // 數(shù)值動(dòng)起來(lái)!
        // 遍歷數(shù)據(jù)中的全部數(shù)值,逐個(gè)讓他們動(dòng)起來(lá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();
        }
        // 樣式動(dòng)起來(lái)!
        // 沒(méi)有指定則使用原來(lái)的樣式!
        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(() => {
        // 關(guān)閉每幀更新
        this.keepUpdating = false;
    }, duration);
}

數(shù)值和樣式都動(dòng)起來(lái)了:

雷達(dá)圖組件:https://gitee.com/ifaswind/eazax-ccc/blob/master/components/RadarChart.ts

以上就是如何在CocosCreator里畫(huà)個(gè)炫酷的雷達(dá)圖的詳細(xì)內(nèi)容,更多關(guān)于CocosCreator畫(huà)個(gè)雷達(dá)圖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論