Web 組態運用之用戶數據 ARPU 分析圖

前言

作爲企業的發展,通過運營的有效管理,增加收入、降低成本,取得更好的經濟效益,是核心所在,在電信企業同樣如此。電信企業的利潤大體上是由業務收入和成本決定的,而收入和成本又可進一步分別分解表達爲不同的形式,其中在每用戶平均收入(ARPU)的比對上,可以很清楚地分析出各個時間段的流量對比,並且相應地制定出對策。實現上可以通過 2D 的形式來展示相應的流程,而 Hightopo(以下簡稱 HT )的 HT for Web 產品上提供了豐富的 2D 組態 可幫助我們快速上手,本系統的 ARPU 分析圖也是通過 HT 搭建而成。

界面簡介及效果預覽

通過 HT 的 2D 組態矢量圖標繪製了三個水池,並且通過管道的水滴動畫,串聯起了動畫流程。

代碼實現

一、水池動畫的實現

矢量圖適用於很多場合,其特點是放大後圖像不會失真,可以適應不同分辨率的屏幕都不會模糊,不僅可以實現完美的跨平臺,在大屏展示上的效果就不言而喻了。而 HT 的 2D 組態上有一套完整的矢量圖標,例如系統裏所實現的水池,是通過 ht.Default.setImage() 註冊一個自定義的矢量圖標 pool,而這個矢量圖標是通過 comps 定義了幾個 type 爲 shpe 的自定義圖形,我們可以通過 points 和 segments 來定義出想要的效果,而這是對應於 ht.Shape 裏的屬性,points 是繪製矢量圖形的點,而 segments 是描述點連接的樣式。通過自定義的方式繪製出矢量圖標 pool 後,我們還需要對所需用到的波動移動座標做數據綁定,方便後續來控制水波的水平移動,對於所需控制的變量我們給它起了一個屬性名稱 offsetX,對應的是所繪製的 comps 組件裏各個自定義圖形 shape 所涉及的矩形區域 rect 的橫座標 x。

繪製其中一個自定義矢量圖形 shape 的具體實現代碼如下:

ht.Default.setImage('pool', {
  dataBindings: [
    {
      attr: 'offsetX',
      valueType: 'Number',
      defaultValue: 0
    }
  ],
  width: 800,
  height: 200,
  clip: true,
  comps: [
    {
      type: 'shape',
      background: 'rgb(51, 153, 255)',
      rect: {
        func: function(data, view) {
          var x = data.a('offsetX') || 0;
          return [250 - x, 140, 300, 60];
        },
        value: [250, 140, 300, 60]
      },
      points: [250, 148, 290, 163, 333, 140, 421, 148, 510, 157, 550, 150, 550, 150, 550, 200, 250, 200],
      segments: [1, 4, 4, 2, 2]
    },
    ...
  ]
});

繪製成的圖形疊加在一起的效果:

水池的水波晃動實現的實質是繪製的各個自定義矢量圖形 shape 的橫座標錯位平移來達到一種水波的效果,我們可以通過不限定其平移的活動範圍來看一下這個原理實現的效果:

很顯然對於平移沒有邊界限定是不行的,通過對於邊界限定了一個範圍,並在這個範圍內定義了一個動畫對象 anim,然後通過 HT 的動畫函數**ht.Default.startAnim()**來啓動這個動畫效果:

// 水池晃動動畫
updatePoolDeep(pools) {
    // 設置每次位置水池晃動波紋偏移的值
    let offsetDlt = 2;
    // 設置水池晃動波紋動畫對象
    let anim = {
        frames: Infinity,
        interval: 50,
        action: () => {
            pools.each((p) => {
                // 設置水池晃動波紋偏移方向
                let offsetFlag = p.a('offsetFlag') || 1;
                // 根據偏移方向取水池晃動波紋偏移值
                let offset = (p.a('offsetX') || 0) + offsetDlt * offsetFlag;
                // 對水池晃動波紋限定邊界
                if (offset > 50) {
                    offset = 50;
                    offsetFlag = -1;
                } else if (offset < -50) {
                    offset = -50;
                    offsetFlag = 1;
                }
                // 對水池晃動波紋的偏移值和偏移方向進行數據綁定設置值
                p.a('offsetX', offset);
                p.a('offsetFlag', offsetFlag);
            });
        }
    };
    // 開啓動畫
    ht.Default.startAnim(anim);
}

爲了使水池裏面的效果更加地真實一點,我們在矢量圖標的上面註冊繪製了一張水紋的矢量圖片,最後實現的水池晃動效果如下:

二、水滴流動效果的實現

在各種行業的業務需求上,2D 視圖的流動效果是必不可少的,不僅可以用來表述活動的流程次序,也可以表達出兩兩互相關聯的效果。其實現的方式也多種多樣,而本系統是採用自己封裝了一個在矩形管道內隨機生成水滴的流動效果動畫。通過構造一個流動類,類裏面定義了基本的一些創建水滴節點、初始化水滴位置以及水滴動畫的進行。

對於水滴節點的創建,定義了一個方法:

createNode() {
    let node;
    // 判斷水池中是否有遺留,存在則設定節點爲水池刪除的最後一個節點
    if (this._pool.length) {
        node = this._pool.pop();
    }
    else {
        // 創建新的水滴節點
        node = new ht.Node();
        // 設置水滴圖片
        node.setImage(WATER_IMG);
    }
    // 取出流動對應的第一條管道
    let firstPath = this.option.paths[0];
    // 設置隨機的偏移量
    let offset = Math.floor(Math.random() * OFFSET_MAX * 2) - OFFSET_MAX;
    // 設置水滴的位置
    node.p(this.getStartPositon(firstPath.rect, firstPath.orientation, offset));
    // 設置水滴的朝向角度
    node.setRotation(this.getRotation(firstPath.orientation));
    // 設置水滴的數據綁定
    node.a({
        // 流動管道
        pathIndex: 0,
        // 動畫步進
        step: Math.random() * 2 + 3,
        // 偏移量
        offset: offset
    });
    return node;
}

流動類裏定義了一個設置水滴在規定的矩形 rect 裏流動的方法,其參數所表示的意義爲:

  • rect:水滴流動的矩形範圍;
  • orientation:水滴流動的朝向,TOP | BOTTOM | RIGHT | LEFT;
  • offset:水滴流動的偏移量;
getStartPositon(rect, orientation, offset) {
    // 水滴流動的矩形區域 rect 的座標位置
    let { x, y, width, height } = rect;
    // 判斷水滴朝向位置,並相應地進行位置的偏移
    switch (orientation) {
        case TOP:
            return {
                x: x + width / 2 + offset,
                y: y + height
            };
        case RIGHT:
            return {
                x: x,
                y: y + height / 2 + offset
            };
        case BOTTOM:
            return {
                x: x + width / 2 + offset,
                y: y
            };
        case LEFT:
            return {
                x: x + width,
                y: y + height / 2 + offset
            };
    }
}

根據水滴的朝向 orientation,還設置了它的旋轉方法:

getRotation(orientation) {
    switch (orientation) {
        case TOP:
            return Math.PI;
        case RIGHT:
            return - Math.PI / 2;
        case BOTTOM:
            return 0;
        case LEFT:
            return Math.PI / 2;
    }
}

三、圖紙疊加的實現

很多場合下,不同於小彈窗的實現,如果需要一個模糊狀態的彈窗窗口,我們可以通過疊加一張背景透明的圖紙來達到這種效果。

創建另外一個彈窗圖紙的 GraphView 取名爲 g2dPop,通過點擊事件來渲染加載這張圖紙呈現:

其實現的監聽代碼邏輯如下:

// 開啓主圖紙事件監聽
this.g2d.mi(this.handleInteractive, this);
// 開啓彈窗圖紙事件監聽
this.g2dPop.mi(this.popHandleInteractive, this);
    
// 主圖紙監聽事件
handleInteractive(e) {
    const {kind, data} = e;
    // 監聽事件爲點擊圖元
    if (kind === 'clickData') {
        // 獲取圖元標籤
        let tag = data.getTag();
        if (!tag) return;
        // 判斷圖元的標籤
        if (tag.indexOf('poolClick') >= 0) {
            // 反序列化彈窗圖紙
            ht.Default.xhrLoad('displays/pop.json', json => {
                if (!json) return;
                this.g2dPopDm.deserialize(json);
            });
        }
    }
}

// 彈窗監聽事件
popHandleInteractive(e) {
    const {kind, data} = e;
    // 監聽事件爲點擊圖元
    if (kind === 'clickData') {
        // 獲取圖元標籤
        let tag = data.getTag();
        if (!tag) return;
        // 判斷圖元的標籤
        if (tag === 'back') {
            // 清除彈窗圖紙
            this.g2dPopDm.clear();
        }
    }
}

總結

2D 組態上實現的矢量圖標可以運用在許多的場合,不僅可以在電信企業表達用戶數據流量的水池效果,在很多工業上的工藝流程也可以得以體現,例如 PID-進料系統可視化界面,豐富的 2D 組態可以搭建許多好玩的場景,HT 自身豐富的 2D 組態 更是能幫助用戶快速上手實現不一樣的可視化系統!

2019 我們也更新了數百個工業互聯網 2D/3D 可視化案例集,在這裏你能發現許多新奇的實例,也能發掘出不一樣的工業互聯網:https://mp.weixin.qq.com/s/ZbhB6LO2kBRPrRIfHlKGQA

同時,你也可以查看更多案例及效果:https://www.hightopo.com/demos/index.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章