1. 程式人生 > >原生JavaScript 仿 echart 外掛製作自己的外掛庫

原生JavaScript 仿 echart 外掛製作自己的外掛庫

本文系原創,轉載請註明出處:

最近在做一個數據大屏的web專案,頁面中經常會用到一些報表、圖表之類的功能,對於圖表我選擇了 echart 外掛,因為 echart 做圖表非常方便,效果也比較滿足要求,但是對於圖表,我則用的是 html 的 table 標籤,每次做都要敲一大堆 html 程式碼,還要重複的編寫和修改 css 樣式。都面向物件程式設計這麼多年了,這種效率的開發方式怎麼能符合我的氣質?!!何況同樣是做圖表的 echart 的怎麼使用那麼方便? 於是我決定自己寫一個外掛庫。

好了,進入主題,今天我要跟大家分享一個table 外掛的製作思路,今後大家可以根據這個思路完善自己的外掛庫,當然如果我做好了,也會跟大家分享。話不多說,先看效果:

這是一個非常簡單的 table 報表,但是如果你想實現這樣的效果的話,我相信你也需要敲不少程式碼。而我的設計卻可以在 html 裡幾乎不用寫任何程式碼,並可以在其他場景無限複用。不信你看,我的 html 程式碼如下:

<head>
    <title>Document</title>
    <script src="cb_table.js"></script>
</head>

<body>
    <div id="mytable" style="width:300px; height:200px; background: black"></div>
    <script>
      var mytable = cb_tables.init('mytable');
      var option = {
        title: {
            text: '員工資訊表',
        },
        dataSet: {
            data: [['姓名', '性別', '年齡', '部門']],
            default: {
                columns: 4,
                rows: 6,
            }
        },
      };
      mytable.setOption(option);
    </script>
</body>

可以看到,我這裡用於顯示報表的是一個 div 塊,那麼首先要做的是引入我設計的 cb_table.js 外掛,然後初始化物件傳入 div 標籤的 id 屬性,最後根據需要設定自己喜歡的 option 屬性就可以實現上述的報表功能。怎麼樣?用過 echart 的夥伴是不是對於這種操作似曾相識啊?接下來看我怎麼實現的吧:

首先,我們新建一個 .js 檔案,外掛叫什麼名大家自己定吧,然後我需要定義一個物件,裡面定義一個 init() 方法,在 init() 方法中我們可以建立一個 table 物件,並進行簡單的樣式設定,程式碼如下:

var cb_tables = {
    init: function (id) {
        var div_tab = document.getElementById(id);
        var table = document.createElement("table");
        div_tab.appendChild(table);
        table.style.width = '100%';
        table.style.height = '100%';
        table.style.borderCollapse = 'collapse';
        return new Mytable(table);
    },
}

完成這步,就實現了將你的 div 中添加了一個 table 標籤,並且返回一個 Mytable 型別的物件。為什麼要這麼寫,因為這是一種很好的封裝設計,至始至終使用者都不知道我生成的 table 到底長什麼樣的,擁有哪些屬性和樣式,你能做的只能是按照我提供的介面呼叫我的方法。哈哈哈,私有化屬性就是這麼牛。

接下來看我的 Mytable 構造方法如何寫的吧,這裡需要強調一下,建構函式和普通的函式沒有什麼區別,但是建構函式首字母一定要大寫!

function Mytable(table) {
  this.table = table;
    this.setOption = function (option) {
        // 複製全部設定屬性
        option = copyOption(options, option);
        this.table.style.border = option.table.border;
        if(!!option.title.text){
            var th = document.createElement("caption");
            table.appendChild(th);
            th.innerHTML = option.title.text;
            th.style.color = option.title.textStyle.color ;
            th.style.textAlign = option.title.x ;
            th.style.width = '100%';
            th.style.height = '15%';
            th.style.fontFamily = option.title.textStyle.fontFamily;
            th.style.fontSize = option.title.textStyle.fontSize;
            table.style.height = '87%';
        }

        var columns = option.dataSet.default.columns,
            rows = option.dataSet.default.rows;
        for (var i = 0; i < rows; i++) {
            var tr = document.createElement("tr");
            table.appendChild(tr);
            tr.style.height = 100 / rows + '%';
            tr.style.backgroundColor = i % 2 ? option.table.dColor : option.table.sColor;
            for (var j = 0; j < columns; j++) {
                var td = document.createElement("td");
                tr.appendChild(td);
                td.style.width = 100 / columns + '%';
                td.style.border = option.table.border;
                td.style.color = i == 0 ? option.table.header.textStyle.color : option.table.td.textStyle.color;
                td.style.fontFamily = i == 0 ? option.table.header.textStyle.fontFamily : option.table.td.textStyle.fontFamily;
                td.style.fontSize = i == 0 ? option.table.header.textStyle.fontSize : option.table.td.textStyle.fontSize;
                td.style.textAlign = 'center';
                td.innerHTML = !option.dataSet.data[i] ? '' : !option.dataSet.data[i][j] ? '' : option.dataSet.data[i][j];
            }
        }
    }
}

可以看到,在建構函式中,首先將傳入的 table 物件 存入到自己的 table 物件中,方便後面使用,如果不想 table 公開,可以宣告為私有物件。 然後新增一個 setOption() 的公有方法,方法有一個形參 option ,用於設定 tbale 的樣式。接下來的程式碼就好理解了,根據 option 設定 table 的樣式,根據行數和列數動態給 table 新增行和列,顯示錶格,並設定樣式。

一切看起來似乎非常的順利,不過你有沒有發現,我給出的 html 程式碼中好像並沒有那麼多的設定啊?這個 setOption() 方法中的那麼多設定哪來的?看到這裡就是本篇文章的關鍵之處了。關鍵!關鍵!關鍵!重要的事情說三遍,我們的外掛中需要規定一些預設的配置和配置物件,這個配置可以根據專案需要寫成通用的樣式,這樣當用戶不需要特殊修改時,報表也會呈現出你想要達到的樣子。好了,繼續看程式碼:

var textStyle = {
        color: 'cyan',
        fontFamily: '楷體',
        fontSize: '12px',
    }

    var options = {
        title: {
            text: '',
            x: 'center',
            y: 'middle',
            textStyle: textStyle,
        },
        table: {
            border: '1px solid rgb(0,0,0)',
            sColor: 'rgba(0,0,0,0.1)',
            dColor: 'rgba(21,22,88,0.5)',
            header: {
                textStyle: textStyle,
            },
            td: {
                textStyle: textStyle,
            }
        },
        dataSet: {
            data: '',
            default: {
                columns: 4,
                rows: 6,
            }
        },
    };

我這裡只列舉了幾個簡單的樣式物件,當你的需求要求越來越高時,這個預設的樣式可以完善的越來越詳細,並且抽象成更具體的樣式物件,就像 echart 那樣。那麼問題來了,當用戶設定了某些樣式,而其他樣式預設時,我們的 table 是如何獲取到全部的樣式屬性的呢?這時要編寫一個方法,我姑且叫做 copyOption() 方法,程式碼如下:

 var copyOption = function (source, option) {
        if (!option) {
            option = source;
            return option;
        };
        for (var prop in source) {
            if (typeof (source[prop]) == 'object') {
                if(!!option[prop]){
                    option[prop] = copyOption(source[prop], option[prop]);
                }
                else{
                    option[prop] = source[prop];
                }
            } else {
                if(!option[prop]){
                    option[prop] = source[prop];
                }
            }
        }
        return option;
    }

方法有兩個形參,需要複製的源option,使用者提供的設定option,最後返回合併完整的option 物件。這個方法中使用到了遞迴的程式設計思想,需要注意的是,我們的物件型別是引用型別,不可以直接複製,因為直接複製的是引用地址,這時需要將物件遞迴呼叫該函式進行進一步深入複製,這塊不理解的小夥伴可以留言,或者搜尋一下 JavaScript 深複製,學習一下相關的知識回頭再理解這段程式碼。

以上,就實現了文章開頭我所提到的所有功能,雖然功能還不太完善,但是用起來確實方便多了,以後我也會進一步完善。對於這篇文章感興趣的小夥伴可以跟我一起探討,多提寶貴意見,感謝大家支援!!!