Chinaunix首页 | 论坛 | 博客
  • 博客访问: 951989
  • 博文数量: 110
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1997
  • 用 户 组: 普通用户
  • 注册时间: 2013-12-15 22:39
个人简介

HT for Web 3D. HT for modern web and mobile app development. www.hightopo.com

文章分类

全部博文(110)

文章存档

2020年(1)

2018年(28)

2017年(21)

2016年(10)

2015年(28)

2014年(19)

2013年(3)

我的朋友

分类: HTML5

2017-05-23 15:33:45

很多人都有这样的疑问,基于HTML5 Canvas实现的元素怎么和用户进行交互?在这里我们用到()写了个Demo进行示例。场景如下所示,在该场景中双击温度和湿度下的Node,会生成输入框供用户填写内容,这之后,用户按下“Enter”键可以将输入内容传到Node中,同时删除输入框,地址::



接下来我们探讨一下具体实现:


准备工作如下:
  1. <script src='ht.js'></script>


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

1、利用系统中定义好的矢量资源进行反序列化来实现场景图:


  1. ht.Default.xhrLoad('TemperatureIndex.json',
  2. function(text) {
  3.     var json = ht.Default.parse(text);
  4.     if(json.title) document.title = json.title;
  5.     dataModel.deserialize(json);
  6. }

2、双击事件

  本例双击会产生输入框,在我们的HT中,GraphView默认内置了一些交互器,以实现基本的选择、单双击、缩放、平移和编辑等交互的功能,内置的交互器有:


  内置的Interactor在交互过程中会派发事件,可通过GraphView#addInteractorListener进行监听,简写为mi(详情可看),在这里,我们用内置的graphView.addInteractorListener监听双击事件:

  1. graphView.addInteractorListener(function(e){
  2.     if (e.kind !== 'doubleClickData') return;
  3.     if (currentInput) removeInput();
  4.                         
  5.     var data = e.data;
  6.     if (clickableTags[data.getTag()]){
  7.         setTimeout(function(){
  8.             createInput('input', data);
  9.         }, 0);
  10.     }
  11.  });


3、创建输入框

  在双击事件发生时,首先需要判断发生双击事件的元素是不是场景中定义的标签名‘temperature’和‘humidity’的node图元,我们用clickableTags对象来保存两个node:


  1. var clickableTags = {
  2.     'temperature': true,
  3.     'humidity': true
  4. }


  在双击的图元是‘temperature’或者‘humidity’时,调用createInput()函数生成输入框,createInput()代码如下:

  1. function createInput(tagName,node){
  2.     if (currentInput) {
  3.         removeInput(graphView, currentInput);
  4.         return;
  5.     } else {
  6.         var element = document.createElement(tagName);
  7.         graphView.getView().appendChild(element);
  8.         element.bindingNode = node;
  9.         ht.Default.setFocus(element);
  10.         currentInput = element;
  11.         layout(currentInput);//布局
  12.         return currentInput;
  13.     }
  14. }

  在createInput()函数中,用全局变量currentInput保存着当前生成的输入框元素,为保证再次生成输入框时,调用removeInput()清除上次生成的输入框元素,从而不影响性能。

4、布局

  生成的输入框应该放在哪儿?这就是layout()函数中所做的事情。layout()函数修改生成的输入框的位置信息,让其在GraphView拓扑图组件上的位置刚好的node图元的位置相同。


  1. function layout(element){
  2.     var rect = element.bindingNode.getRect();
  3.     var x = rect.x;
  4.     var y = rect.y;

  5.     element.style.position = 'absolute';
  6.     element.style.width = rect.width + 'px';
  7.     element.style.height = rect.height + 'px';
  8.     element.style.top = y + 'px';
  9.     element.style.left = x + 'px';
  10.     element.style.background = '#fff';
  11.     element.style.color = '#000';
  12.     element.style.textAlign = 'center';
  13. }

   以‘temperature’为例,在点击标签名为‘temperature’的node图元时,会在其上生成一个输入框,获取该node图元的宽、高、位置信息,并分别赋值给绝对定位后输入框的宽、高、位置,这样即可让输入框刚好覆盖住node图元。

 

5、平移和缩放

 

  可能细心思考的朋友也会发现,在对整个场景图进行平移和缩放时,按照上诉布局方式,输入框的位置和大小却没有跟随着node图元的位置进行改变,所以我们在布局时还需要思考到平移、缩放事件。


  首先,layout函数的内容中,元素的宽、高、位置信息必须加入平移和缩放产生的结果,所以,最终layout代码如下:



  1. function layout(element){
  2.     var rect = element.bindingNode.getRect();
  3.     var zoom = graphView.getZoom();
  4.     var tx = graphView.tx();
  5.     var ty = graphView.ty();
  6.     rect.x *= zoom;
  7.     rect.y *= zoom;
  8.     rect.width *= zoom;
  9.     rect.height *= zoom;
  10.     var x = tx + rect.x;
  11.     var y = ty + rect.y;

  12.     element.style.position = 'absolute';
  13.     element.style.width = rect.width + 'px';
  14.     element.style.height = rect.height + 'px';
  15.     element.style.top = y + 'px';
  16.     element.style.left = x + 'px';
  17.     element.style.background = '#fff';
  18.     element.style.color = '#000';
  19.     element.style.textAlign = 'center';
  20. }

  其次,我们需要对平移和缩放事件添加监听,以便能在该事件发生时,再次调用layout()函数将输入框的位置进行同步,在这里,我们用内置的交互器addPropertyChangeListener(简写为mp),监听zoom、translateX、translateY属性的变化:

  1. var changeProperties = {
  2.     'zoom': true,
  3.     'translateX': true,
  4.     'translateY':true
  5. }

  1. graphView.mp(function(e) {
  2.     if (changeProperties[e.property]) {
  3.         var elements = document.getElementsByTagName('input');
  4.         for (var i = 0; i < elements.length; i++) {
  5.             layout(elements[i]);
  6.         }
  7.     }
  8. });


6、更新node

  大家在Demo中可以发现,我们按下Enter键时,输入的文字会同步到node中,其实这里做了两件事: 给node设值后删除输入框。

  a、给node设值,是用一个名为setText()的函数来实现的,实现代码如下:

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



  在检测输入框中值得存在性后,给node图元赋值用到我们HT的setStyle(简写为s)方法。

  b、删除输入框


  1. function removeInput(){
  2.     if(!currentInput) return;
  3.     graphView.getView().removeChild(currentInput);
  4.     currentInput = null;
  5. }


  c、添加Enter的事件监听器

  因为没有监听键盘的内置交互器,所以我们通过graphView.getView().addEventListener直接对底层的div添加监听。



  1. graphView.getView().addEventListener('keydown', function(event){
  2.     if(ht.Default.isEnter(event)){
  3.          setText('input');
  4.          removeInput();
  5.     } else if(ht.Default.isEsc(event)){
  6.          removeInput();
  7.     }
  8. }, false);


  最后,再次贴上Demo地址(),希望能够帮助那些需要在拓扑图中加入原生HTML的朋友,也望大家不吝赐教。

 



阅读(2152) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~