基於HTML5 Canvas實現用戶交互

  不少人都有這樣的疑問,基於HTML5 Canvas實現的元素怎麼和用戶進行交互?在這裏咱們用到HT for Webhttp://www.hightopo.com/guide/guide/core/beginners/ht-beginners-guide.html)寫了個Demo進行示例。html

  場景以下所示,在該場景中雙擊溫度和溼度下的Node,會生成輸入框供用戶填寫內容,這以後,用戶按下「Enter」鍵能夠將輸入內容傳到Node中,同時刪除輸入框,地址::http://www.hightopo.com/demo/GraphInput/display.htmlnode

接下來咱們探討一下具體實現:
json

 

準備工做以下:app

 

<script src='ht.js'></script>

 

dataModel = new ht.DataModel();
graphView = new ht.graph.GraphView(dataModel);
graphView.addToDOM();

一、利用系統中定義好的矢量資源進行反序列化來實現場景圖:ide

ht.Default.xhrLoad('TemperatureIndex.json', function(text) {
    var json = ht.Default.parse(text);                    
    if(json.title) document.title = json.title;
    dataModel.deserialize(json);
} 

二、雙擊事件函數

  本例雙擊會產生輸入框,在咱們的HT中,GraphView默認內置了一些交互器,以實現基本的選擇、單雙擊、縮放、平移和編輯等交互的功能,內置的交互器有:佈局

  內置的Interactor在交互過程當中會派發事件,可經過GraphView#addInteractorListener進行監聽,簡寫爲mi(詳情可看HT for Web 入門手冊http://www.hightopo.com/guide/guide/core/beginners/ht-beginners-guide.html#ref_graphviewinteraction),在這裏,咱們用內置的graphView.addInteractorListener監聽雙擊事件:性能

graphView.addInteractorListener(function(e){
    if (e.kind !== 'doubleClickData') return;
    if (currentInput) removeInput();
                        
    var data = e.data;
    if (clickableTags[data.getTag()]){
        setTimeout(function(){
            createInput('input', data);
        }, 0);
    } 
 });

三、建立輸入框ui

  在雙擊事件發生時,首先須要判斷髮生雙擊事件的元素是否是場景中定義的標籤名‘temperature’和‘humidity’的node圖元,咱們用clickableTags對象來保存兩個node:spa

 

var clickableTags = {
    'temperature': true,
    'humidity': true
}

 

  在雙擊的圖元是‘temperature’或者‘humidity’時,調用createInput()函數生成輸入框,createInput()代碼以下:

function createInput(tagName,node){
    if (currentInput) {
        removeInput(graphView, currentInput);
        return;
    } else {                    
        var element = document.createElement(tagName);                
        graphView.getView().appendChild(element);
        element.bindingNode = node;
        ht.Default.setFocus(element);
        currentInput = element;
        layout(currentInput);//佈局
        return currentInput;
    }
}

  在createInput()函數中,用全局變量currentInput保存着當前生成的輸入框元素,爲保證再次生成輸入框時,調用removeInput()清除上次生成的輸入框元素,從而不影響性能。

四、佈局

  生成的輸入框應該放在哪兒?這就是layout()函數中所作的事情。layout()函數修改生成的輸入框的位置信息,讓其在GraphView拓撲圖組件上的位置恰好的node圖元的位置相同。

 

function layout(element){
    var rect = element.bindingNode.getRect();
    var x =  rect.x;
    var y =  rect.y;

    element.style.position = 'absolute';
    element.style.width = rect.width + 'px';
    element.style.height = rect.height + 'px';
    element.style.top = y + 'px';
    element.style.left = x + 'px';
    element.style.background = '#fff';
    element.style.color = '#000';
    element.style.textAlign = 'center';
}

 

  以‘temperature’爲例,在點擊標籤名爲‘temperature’的node圖元時,會在其上生成一個輸入框,獲取該node圖元的寬、高、位置信息,並分別賦值給絕對定位後輸入框的寬、高、位置,這樣便可讓輸入框恰好覆蓋住node圖元。

 

五、平移和縮放

 

  可能細心思考的朋友也會發現,在對整個場景圖進行平移和縮放時,按照上訴佈局方式,輸入框的位置和大小卻沒有跟隨着node圖元的位置進行改變,因此咱們在佈局時還須要思考到平移、縮放事件。

 

  首先,layout函數的內容中,元素的寬、高、位置信息必須加入平移和縮放產生的結果,因此,最終layout代碼以下:

 

function layout(element){
    var rect = element.bindingNode.getRect();
    var zoom = graphView.getZoom();
    var tx = graphView.tx();
    var ty = graphView.ty();
    rect.x *= zoom;
    rect.y *= zoom;
    rect.width *= zoom;
    rect.height *= zoom;
    var x = tx + rect.x;
    var y = ty + rect.y;

    element.style.position = 'absolute';
    element.style.width = rect.width + 'px';
    element.style.height = rect.height + 'px';
    element.style.top = y + 'px';
    element.style.left = x + 'px';
    element.style.background = '#fff';
    element.style.color = '#000';
    element.style.textAlign = 'center';
}

 

  其次,咱們須要對平移和縮放事件添加監聽,以便能在該事件發生時,再次調用layout()函數將輸入框的位置進行同步,在這裏,咱們用內置的交互器addPropertyChangeListener(簡寫爲mp),監聽zoom、translateX、translateY屬性的變化:

var changeProperties = {
    'zoom': true,
    'translateX': true,
    'translateY':true
}
graphView.mp(function(e) {
    if (changeProperties[e.property]) {
        var elements = document.getElementsByTagName('input');
        for (var i = 0; i < elements.length; i++) {
            layout(elements[i]);                                
        }
    }
});

六、更新node

  你們在Demo中能夠發現,咱們按下Enter鍵時,輸入的文字會同步到node中,其實這裏作了兩件事: 給node設值後刪除輸入框。

  a、給node設值,是用一個名爲setText()的函數來實現的,實現代碼以下:

 

function setText(tagName){
    var element = document.getElementsByTagName(tagName);
    if(!element) return;
    for (var i = 0; i < element.length; i++) {
        var value = (element[i].value) ? element[i].value : 32 ;
        element[i].bindingNode.s('text', value);
    }
}

 

  在檢測輸入框中值得存在性後,給node圖元賦值用到咱們HT的setStyle(簡寫爲s)方法。

  b、刪除輸入框

 

function removeInput(){
    if(!currentInput) return;
    graphView.getView().removeChild(currentInput);
    currentInput = null;
}

 

  c、添加Enter的事件監聽器

  由於沒有監聽鍵盤的內置交互器,因此咱們經過graphView.getView().addEventListener直接對底層的div添加監聽。

 

 

 

graphView.getView().addEventListener('keydown', function(event){
    if(ht.Default.isEnter(event)){
         setText('input');
         removeInput();
    } else if(ht.Default.isEsc(event)){
         removeInput();
    }                        
}, false);

 

  最後,再次貼上Demo地址(http://www.hightopo.com/demo/GraphInput/display.html),但願可以幫助那些須要在拓撲圖中加入原生HTML的朋友,也望你們不吝賜教。

相關文章
相關標籤/搜索