基於 HTML5 Canvas 的簡易 2D 3D 編輯器

無論在任何領域,只要能讓非程序員能經過拖拽來實現 2D 和 3D 的設計圖就是很牛的,今天咱們不須要 3dMaxs 等設計軟件,直接用 HT 就能本身寫出一個 2D 3D 編輯器,實現這個功能我以爲成就感仍是爆棚的,哈哈!只要你會想,能作,就能根據這個編輯器延展成 big thing! html

   

咱們首先將全部須要用到的 json 文件做爲矢量圖輸出,矢量圖的好處是組件上的圖元縮放都不會失真,而且再也不須要爲 Retina 顯示屏提供不一樣尺寸的圖片, 在 devicePixelRatio 多樣化的移動時代, 要實現完美的跨平臺,矢量多是的最低成本的解決方案。 HT 經過 ht.Default.setImage 函數來註冊圖片,能夠是 base6四、jpg、 png 以及 json 格式的圖片: 
node

ht.Default.setImage('edit', 'images/default.json');
ht.Default.setImage('shape', 'images/edit.json');
ht.Default.setImage('edge', 'images/edge.json');
ht.Default.setImage('circle', 'images/circle.json');
ht.Default.setImage('roundRect', 'images/roundRect.json');
ht.Default.setImage('rect', 'images/rect.json');複製代碼

這邊我註冊的是頂部工具條的 6 個圖片,分別爲「編輯」、「不規則圖形」、「圓」、「圓角矩形」、「矩形」以及「連線」,功能如其名。主要操做:點擊工具條的任意一個圖標,在工具條下的空白處拖動鼠標,便可實現繪圖。 那麼接下來的步驟就是建立「工具條」,HT 封裝了工具條的組件 ht.widget.Toolbar 在這個函數的參數中填入工具條中的元素,具體操做方法請看 HT for Web 工具條手冊,這邊值得注意的一個點是,groupId 是將一個類型的元素分組,分組的好處是在咱們選中這個組中的任意一個元素的時候,其餘的元素都不選中,就能形成「單選」的效果: 
程序員

toolbar = new ht.widget.Toolbar();
toolbar.addItem(createItem('edit', 'edit', '編輯', [ new ht.graph.EditInteractor(graphView)]));//這邊最後一個參數數組可放置多個交互器,具體定義請參見 HT for Web 入門手冊
//addItem(item, index)可在指定index位置插入新元素,index爲空表明插入到最後。
toolbar.addItem(createItem('shape', 'shape', '不規則圖形',  [ new CreateShapeInteractor(graphView)]));
toolbar.addItem(createItem('circle', 'circle', '圓', [ new CreateNodeInteractor(graphView)]));
toolbar.addItem(createItem('roundRect', 'roundRect', '圓角矩形', [ new CreateNodeInteractor(graphView)]));
toolbar.addItem(createItem('rect', 'rect', '矩形', [ new CreateNodeInteractor(graphView)]));
toolbar.addItem(createItem('edge', 'edge', '連線', [ new CreateEdgeInteractor(graphView)]));
toolbar.getItemById('edit').selected = true;//默認選中「編輯」
toolbar.getSelectBackground = function(){//重載自定義選中背景顏色
    return '#eee';
}
複製代碼

在上面用到的 addItem 函數是向 ht.widget.Toolbar 工具條中添加元素,添加的元素是從 createItem 函數中傳回來的元素,咱們在這個函數中利用了 vector 矢量創造了一個矩形和一張圖片的結合體,咱們將以前註冊好的矢量圖傳給這個結合體中的「圖片」,而後經過控制這個圖片的「渲染顏色」,來過濾工具條選中和非選中狀態的顏色: 
數據庫

function createItem(id, iconName, toolTip, interactorsArr){
    var item = {
        id: id,//工具條元素的惟一標示,若是設置可經過getItemById獲取
        unfocusable: true,//工具條元素是否不可獲取焦點,默認鼠標滑過期會顯示一個矩形邊框,可設置爲true關閉此效果
        icon: iconName,//工具條元素的圖標
        toolTip: toolTip,//工具條元素的文字提示
        groupId: 'bar'//對工具條元素進行分組,同一個分組內的元素選中會自動出現互斥效果
    };
    var json = ht.Default.getImage(iconName);
    var width = json ? json.width : 16;
    var height = json ? json.height : 16;

    item.icon = {
        width: width + 8,
        height: height + 8,
        fitSize: json ? json.fitSize : false,
        comps: [
            {
                type: 'rect',//組件類型
                rect: [0, 0, width + 8, height + 8]//指定組件繪製在矢量中的矩形邊界
            },
            {
                type: 'image',
                name: iconName,
                color: {//渲染顏色,HT系統會自動採用該顏色對圖片內容進行渲染
                    func: (data, view) => {
                        return '#000'
                    }
                }
            }
        ]
    };

    item.action = function(){//函數類型,工具條元素被點擊時調用
        for(var i = 0; i < toolbar.getItems().length; i++){
            toolbar.getItems()[i].icon.comps[1].color = '#000';
        }
        item.icon.comps[1].color = '#1E90FF';
        graphView.setInteractors(interactorsArr);//組合多個交互器
        graphView.sm().clearSelection();//每次工具條有點擊的時候就清空全部的選中
    }

    return item;
}
複製代碼

接着利用 HT 封裝的面板組件 ht.widget.BorderPane 將整個界面分紅兩個部分:頂部和底部。咱們又利用 HT 封裝的 ht.widget.SplitView 分割組件將底部分爲上下兩個部分,最後將這個外邊框 borderPane 添加進 body 體中:
json

splitView = new ht.widget.SplitView(graphView, g3d, 'v', 0.5);
borderPane = new ht.widget.BorderPane();
borderPane.setTopView(toolbar);
borderPane.setCenterView(splitView);
borderPane.addToDOM();  複製代碼

整個場景都製做完畢,接着就是功能部分。咱們把製做「不規則圖形」做爲一個單獨的部分放到 CreateShapeInteractor.js 中,製做「圓」、「圓角矩形」以及「矩形」三個部分分爲一個部分放到 CreateNodeInteractor.js 中,將「連線」分爲一個部分放到 CreateEdgeInteractor.js 中,接下來咱們將對這三個 js 文件一個個解析。 這三個 js 文件的共同點是經過 HT 封裝的繼承函數 ht.Default.def 繼承並建立新的類,這三個類咱們在前面的代碼中是有提到的: CreateShapeInteractor、CreateNodeInteractor 以及 CreateEdgeInteractor 類,都大同小異,咱們這裏重點解析 CreateNodeInteractor.js 文件。 首先就是要聲明定義一個 CreateNodeInteractor 類,就是這三個部分:
數組


var CreateNodeInteractor = function (graphView) {
    CreateNodeInteractor.superClass.constructor.call(this, graphView);                
};
ht.Default.def(CreateNodeInteractor, ht.graph.Interactor, { //自定義類,第一個參數爲類名,第二個參數爲繼承的類,第三個參數爲此類的方法
    //這邊從新繪製這個類的方法
}複製代碼

接着就是向這個類中添加咱們須要的功能,主要的功能是「鼠標點擊事件的觸發」以及「觸摸屏幕事件的觸發」,咱們經過對事件的監聽來繪製圖形,首先就是判斷鼠標左鍵或者觸屏是否點擊:
bash

handle_touchstart: function (e) {//觸屏 開始點擊
    ht.Default.preventDefault(e);//阻止全部的默認交互事件
    if (ht.Default.isLeftButton(e)) {//鼠標左鍵是否點擊
        this._graphView.setFocus(e);//設置焦點
        this.p1 = this._graphView.lp(e);//獲取當前邏輯座標點
        this.startDragging(e);//調用 startDragging 開始拖拽函數 
    }
} 
複製代碼

而後對鼠標彈起或者觸屏是否結束進行事件的判斷,並直接生成一個 ht.Node 節點。HT 把單純的點擊事件和拖拽事件分爲兩種命名格式,單純的點擊事件爲 handle_* 方法,拖拽事件是 handleWindow* 方法。上面的代碼就是從點擊工具條的能觸發 CreateNodeInteractor 類的元素開始,到放到界面中生成圖元結束。並無拖拽的過程,會有一個默認的大小: 
編輯器

HT 默認調用 ht.graph.DefaultInteractor 事件,裏面有一系列的操做,咱們如今要作的拖拽跟這個有衝突,因此在前面咱們先將這個默認的事件阻止,獲取鼠標點下的第一個點的邏輯座標和第二個點的邏輯座標,根據這兩個座標的點生成一個矩形,而後開始繪製節點:
ide

handleWindowTouchMove: function(e) {
    ht.Default.preventDefault(e);//阻止事件的默認行爲,經常使用於屏蔽觸屏上默認DoubleTap縮放等行爲
    if (!this.p1) {
        return;
    }
    this.p2 = this._graphView.lp(e);
    const rect = ht.Default.unionPoint(this.p1, this.p2);//將 p1 和 p2 兩個點組合成一個矩形
    if (this.node) {
        this.node.setRect(rect);
    }
    else {
        if (!rect.width || !rect.height) {
            return;
        }
        this._graphView.dm().beginTransaction();//相似數據庫裏開啓事務,從beginTransaction()到endTransaction()之間全部的修改可被一次性撤銷或重作
        this.createNode(rect, false);//調用 createNode 函數
    }
}複製代碼

而後在拖拽結束的時候作結束繪製圖形的操做,這裏我是直接設置繪製結束後就將工具條選中「編輯」的元素:函數

handleWindowTouchEnd: function(e) {
    ht.Default.preventDefault(e);
    this._graphView.dm().endTransaction();//相似數據庫裏結束事務,從beginTransaction()到endTransaction()之間全部的修改可被一次性撤銷或重作
    if (!this.node && this.p1) {
        this.createNode({
            x: this.p1.x - 25,
            y: this.p1.y - 25,
            width: 50,
            height: 50
        }, true);
    }
    var continuousCreating = false;
    if (!continuousCreating) {
        for(var i = 0; i < toolbar.getItems().length; i++){
            toolbar.getItems()[i].selected = false;
            toolbar.getItems()[i].icon.comps[1].color = '#000';
        }
       toolbar.getItemById('edit').selected = true;
        toolbar.getItemById('edit').icon.comps[1].color = '#1E90FF';
        borderPane.iv();
        this._graphView.setEditable(true);
        this._graphView.sm().ss(this.node);
    }
    else {
        this.node = this.p1 = this.p2 = null;
    }
}複製代碼

最後,咱們只要知道如何繪製圖元就行了,在 HT 中,基礎的圖元均可以經過設置樣式中的 shape 或者 shape3d 來生成不一樣的圖元,咱們這邊就是經過這種途徑,若是想要在界面中生成複雜圖形,如:機櫃模型,能夠參考這篇文章:http://www.cnblogs.com/xhload3d/p/7887229.html 

createNode: function(rect, click) {
    // create instance
    if (ht.Default.isFunction(this.type)) {
        this.node = this.type(rect, click);
    }
    else {
        this.node = new ht.Node();
    }
    this.node.setTall(50);//爲 3D 圖形作準備,設置其厚度,纔會有立體感
    if(toolbar.getItemById('circle').selected){//若是工具條的 ‘circle’ 被選中
        this.node.s({//設置 style 樣式
            "shape": "oval",//橢圓形,爲空時顯示爲圖片,可設置多邊形類型參見入門手冊
            "shape.background": "#D8D8D8",//多邊形類型圖元背景
            "shape.border.width": 1,//多邊形類型圖元邊框寬度
            "shape.border.color": "#979797",//多邊形類型圖元邊框顏色
            "shape3d": "sphere"//爲空時顯示爲六面立方體,其餘可選值爲box|sphere|cylinder|cone|torus|star|rect|roundRect|triangle|rightTriangle|parallelogram|trapezoid
        });
    }else if(toolbar.getItemById('roundRect').selected){
        this.node.s({
            "shape": "roundRect",//圓角矩形
            "shape.background": "#D8D8D8",
            "shape.border.width": 1,
            "shape.border.color": "#979797",
            "shape3d": "roundRect"
        });
    }else if(toolbar.getItemById('rect').selected){
        this.node.s({
           "shape": "rect",//矩形
            "shape.background": "#D8D8D8",
            "shape.border.width": 1,
            "shape.border.color": "#979797",
            "shape3d": "rect"
        });
    }
    // set bounds
    if (click) {
        this.node.setPosition(rect.x + rect.width / 2, rect.y + rect.height / 2);//設置 node 的座標點
     }
     else {
        this.node.setRect(rect);//設置 node 的外矩形邊框大小
     }
    // add to data model
    this._graphView.dm().add(this.node);//將這個 node 添加進數據容器 DataModel 中
}複製代碼

到此,建立 ht.Node 節點的聲明所有結束,你們能夠根據本身的想象建立你想要的編輯器!

相關文章
相關標籤/搜索