html绘图交互引擎,基于HTML5 Canvas实现用户交互

很多人都有这样的疑问,基于HTML5 Canvas实现的元素怎么和用户进行交互?在这里我们用到HT for Web(http://www.hightopo.com/guide...)写了个Demo进行示例。

场景如下所示,在该场景中双击温度和湿度下的Node,会生成输入框供用户填写内容,这之后,用户按下“Enter”键可以将输入内容传到Node中,同时删除输入框,地址::http://www.hightopo.com/demo/...

bVN7CT?w=571&h=414

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

准备工作如下:

dataModel = new ht.DataModel();

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

graphView.addToDOM();

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

ht.Default.xhrLoad('TemperatureIndex.json', function(text) {

var json = ht.Default.parse(text);

if(json.title) document.title = json.title;

dataModel.deserialize(json);

}

2、双击事件

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

bVN7GJ?w=775&h=139

内置的Interactor在交互过程中会派发事件,可通过GraphView#addInteractorListener进行监听,简写为mi(详情可看HT for Web 入门手册http://www.hightopo.com/guide...),在这里,我们用内置的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);

}

});

3、创建输入框

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

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()清除上次生成的输入框元素,从而不影响性能。

4、布局

生成的输入框应该放在哪儿?这就是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图元。

5、平移和缩放

可能细心思考的朋友也会发现,在对整个场景图进行平移和缩放时,按照上诉布局方式,输入框的位置和大小却没有跟随着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]);

}

}

});

6、更新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/...),希望能够帮助那些需要在拓扑图中加入原生HTML的朋友,也望大家不吝赐教。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
随着html5 相关技术的兴起,因其跨平台的特性,和标准的日益完善。html5相关技术越来越多的被应用到前沿app的开发中,尤其是html5 小游戏的开发。 Laro 是一个基于html5 canvas的用于平面2d或者2.5d游戏制作的轻量级游戏引擎。 因为当前canvas作为画布形态的dom元素,并提供了大量关于矢量图以及texture绘制的api,但是由于其本身提供的api太过于底层,在类似游戏这一类交互性,逻辑性较为复杂的app时。需要开发者编写大量底层的api来实现本身的业务逻辑。 Laro出现的目的是为了简化使用canvas制作游戏时的api调用。同时提供了一套“有限状态机”的开发模式,这种模式在对于游戏这一类的典型的“事件驱动”的模型的开发上。能够很好的做到模块间的低耦合,利于开发者梳理整个开发逻辑。 Laro 游戏引擎目前已经完成了游戏开发中所需要的模块和api的封装,并有一些实际的Demo和TestCase供使用者参考。而且随后会结合这个引擎整理出一套用于html5 小游戏开发的可视化编辑工具。 旨在帮助开发者更快更容易的搭建一款小游戏为目的。 目前已经开源到github (https://github.com/AlloyTeam/Laro) 我们团队希望通过Laro的不断完善,能够帮助更多的html5 小游戏开发者以更快的速度,更优的质量完成 html5小游戏 产业化的开发。 Version Log 0.1 - 基础模块搭建 0.2 - 融入jcanvas,配合鼠标事件处理 0.3 - 加强状态机模块 查看以下demo最好使用chrome  : ) http://hongru.github.com/proj/laro/examples/emberwind/ http://hongru.github.com/proj/laro/examples/typeshot/index.html http://hongru.github.com/proj/laro/examples/jxhome/ http://heroes.github.com/world-of-heroes/development/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值