數據可視化初探-從零開始開發一個渲染引擎概述

Demo

項目地址在這裏,歡迎fork和star

簡介:在以前的項目中用過一些圖表,好比Echart、chart.js等。遂對其比較感興趣,剛好最近在看canvas相關的知識,故嘗試寫了一下可視化的渲染引擎。通過斷斷續續的開發,目前已實現了大部分基礎圖形。html

整體架構設計

相信你們在項目開發中都或多或少的用過一些圖表,讓咱們再看一下一個簡單的圖表是什麼樣的。git

bar

截圖自EChart Demo
一個簡單的條形圖表都是由哪些基礎圖形組成的了?沒錯!正如看到的那樣主要由線段和文字以及矩形組成。這三個基礎圖形組成了一個簡單圖表。而後對這三個基礎圖形進行劃分,線段和文字爲一組構成了座標軸,全部矩形爲一組構成了圖表的主體。而後將座標軸和主體放入一個畫板中並繪製出來即造成了這個圖表。 依據上面的描述並參考Photoshop繪圖的理念能夠提取一個簡單的關係,如圖:

層級結構
以上即是一個整體的結構設計劃分,接下來只需實現相應的類便可。

Canvas類

做爲全部圖形表現的媒介,Canvas類必須有一個可供圖形呈現到網頁中的元素,故初始化需傳入一個html元素來肯定圖表的渲染位置,並須要提供相應的配置來肯定畫布的大小。此外做爲繪圖的入口還須要負責繪畫的統一調度,以及對相應事件的轉發和模擬,不然做爲HTMLCanvasElement中的一個圖像到目前爲止是不能原生響應相應的事件的,事件的訂閱和派發放到下面單獨介紹。github

Canvas類具體配置和經常使用方法以下

  • 配置canvas

    container: 畫板的容器,canvas渲染到html中的位置
    width: 畫布寬度
    height: 畫布高度
    style: 繪畫的各類樣式,具體設置項同原生API緩存

  • 主要方法架構

    draw() 繪製圖形。
    addLayer(options) 添加圖層
    addShape(type, options) 添加圖形
    remove(element) 移除圖形或圖層
    clear() 清除全部圖層
    update() 更新畫布
    ide

例子

const canvas = new ZE.Canvas('container', {
  width: 800,
  height: 600,
  style: {
    fillStyle: 'red'
  }
})
複製代碼

EventBus類

爲了讓canvas中的元素能夠得到單獨事件響應的能力,故而須要實現了此類,它是Canvas的基類。這個類主要提供了訂閱和分發事件的方法,還會記錄每種事件都由哪些元素訂閱,從而實現事件的冒泡機制。動畫

EventBus類的主要方法

  • addEventListener(type, func, [element, once])添加訂閱事件 (alias: on)
  • removeEventListener(type, [element, func])移除訂閱事件 (alias: off)
  • once(type, func, [element])添加只執行一次的事件
  • trigger(type, [element, ...data))觸發事件(alias: emit)

例子

canvas.on('test', (str) => { console.log(str)  });
canvas.emit('test', '您好');
複製代碼

Element類

何爲Element?其實Layer和Shape均可以當作是一個元素,他們有許多類似之處,Layer只是包含了若干個Shape的元素,因此提取出了Element這個類,它也是整個引擎最核心的類。咱們須要在這個類完成元素的屬性設置、動畫等相關方法。ui

Element類的屬性和方法

方法
  • setAttrs(attr)設置屬性(具體屬性視具體shape而定)
  • setStyle(style)設置樣式
  • animate(options)設置動畫 (默認設置後自動播放)
  • play() 播放動畫
  • stop() 中止動畫
  • update() 更新畫面,更改屬性或樣式後須要update纔會更新畫布內容
  • show() 顯示圖形
  • hide() 隱藏圖形
  • destroy() 從畫板中移除本身
  • getCanvas() 獲取畫板容器
  • getContext() 獲取畫筆
  • getStatus() 返回圖形當前狀態
屬性
  • attrs 圖形相關配置
  • style 圖形相關樣式
  • type 圖形類型
  • container 圖形的容器
  • zIndex 位置層級
  • computed 根據圖形樣式和屬性計算後獲得的一些值

例子

const ball = canvas.addShape('circle', {
  attrs: {
    x: 50, // 圓心x座標
    y: 50, // 圓心y座標
    r: 50 // 半徑
  },
  style: {
    fillStyle: 'red'
  },
  zIndex: 1, // 層級 默認 0
  event: { // 能夠直接再初始化時配置相應的事件
    click (e) {  console.log(e); }
  }
});
複製代碼

Layer類

Layer是繼承Element的,通過Element的封裝後須要在Layer裏面完成的事情就很少了。Layer做爲一個容器,主要固然要提供對元素的添加和刪除了,此外我的爲每個Layer設計了一個圖形的緩存,即每一個Layer都擁有一個本身的緩存畫板,當Canvas調用draw來進行繪畫時,能夠直接將緩存的圖像繪製到顯示的繪畫板中,這樣的初衷是爲了提升繪畫的效率(待驗證),但會增長內存的使用。spa

Layer類的主要方法和屬性

  • addLayer() 添加圖層
  • addShape() 添加圖形
  • remove() 移除一個子元素
  • clear() 清除子元素
  • palette 緩存的畫板
  • brush 緩存的畫筆
  • attrs 畫板的屬性

例子

const layer = canvas.addLayer({
  x: 10,
  y: 20
});

layer.addShape('text', {
  attrs: {
    text: 'hello'
  }
});
複製代碼

Shape類

Shape實際上是具體圖形的基類,它只是將每一個圖形都會用到方法提取到了這個公用的地方,好比圖形樣式的設置和繪製。

繪製其實分兩個步驟:1.建立圖形路徑。2.調用畫筆繪製到畫板。Shape類只負責後者。

因爲每種圖形的實現各不相同,而且有的須要不少複雜的數學知識在此就不一一描述了,具體能夠查看源碼或者搜索查看這些基本圖形的實現方法或者另寫相應的帖子再介紹。總之每種圖形最主要作的兩件事是1:實現圖形的路徑繪製。2:判斷一個座標點是否包含在圖形內。

一個初步的渲染引擎基本就差很少了,固然每一個人對於各個類的劃分可能不盡相同,而且對於各類機制的設計也千差萬別,最後再次附上本項目的GitHub地址,歡迎你們fork。

github.com/STMU1320/ze

相關文章
相關標籤/搜索