不管在任何领域,只要能让非程序员能通过拖拽来实现 2D 和 3D 的设计图就是很牛的,今天我们不需要 3dMaxs 等设计软件,直接用 HT 就能自己写出一个 2D 3D 编辑器,实现这个功能我觉得成就感还是爆棚的,哈哈!只要你会想,能做,就能根据这个编辑器延展成 big thing!

本例地址:http://www.hightopo.com/demo/drag-create-data/

下面是实现效果图:

我们首先将所有需要用到的 json 文件作为矢量图输出,矢量图的好处是组件上的图元缩放都不会失真,并且不再需要为 Retina 显示屏提供不同尺寸的图片, 在 devicePixelRatio 多样化的移动时代, 要实现完美的跨平台,矢量可能是的最低成本的解决方案。

HT 通过 ht.Default.setImage 函数来注册图片,可以是 base64、jpg、 png 以及 json 格式的图片:

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 体中:

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, { //自定义类,第一个参数为类名,第二个参数为继承的类,第三个参数为此类的方法
//这边重新绘制这个类的方法
}

接着就是向这个类中添加我们需要的功能,主要的功能是“鼠标点击事件的触发”以及“触摸屏幕事件的触发”,我们通过对事件的监听来绘制图形,首先就是判断鼠标左键或者触屏是否点击:

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 事件,里面有一系列的操作,我们现在要做的拖拽跟这个有冲突,所以在前面我们先将这个默认的事件阻止,获取鼠标点下的第一个点的逻辑坐标和第二个点的逻辑坐标,根据这两个坐标的点生成一个矩形,然后开始绘制节点:

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 节点的声明全部结束,大家可以根据自己的想象创建你想要的编辑器!

基于 HTML5 Canvas 的简易 2D 3D 编辑器的更多相关文章

  1. 基于HTML5 Canvas的网页画板实现教程

    HTML5的功能非常强大,尤其是Canvas的应用更加广泛,Canvas画布上面不仅可以绘制任意的图形,而且可以实现多种多样的动画,甚至是一些交互式的应用,比如网页网版.这次我们要来看的就是一款基于H ...

  2. 基于 HTML5 的 WebGL 楼宇自控 3D 可视化监控

    前言 智慧楼宇和人们的生活息息相关,楼宇智能化程度的提高,会极大程度的改善人们的生活品质,在当前工业互联网大背景下受到很大关注.目前智慧楼宇可视化监控的主要优点包括: 智慧化 -- 智慧楼宇是一个生态 ...

  3. 基于html5 canvas 的客户端异步上传图片的插件,支持客户端压缩图片尺寸

    /** * Created by xx on 15-05-28. * 基于html5 canvas 的客户端异步上传画片的插件 * 在实际应用中,常常要用于上传图片的功能.在现在越来越多的手机weba ...

  4. 基于 HTML5 Canvas 的智能安防 SCADA 巡逻模块

    基于 HTML5 Canvas 的智能安防 SCADA 巡逻模块 前言 最近学习了 HT for Web flow 插件,除了正常的 flow 效果,其中还有两个十分好用的两个接口 getPercen ...

  5. 一款基于HTML5 Canvas的画板涂鸦动画

    今天给各网友分享一款基于HTML5 Canvas的画板涂鸦动画.记得之前我们分享过一款HTML5 Canvas画板工具,可以切换不同的笔刷,功能十分强大.本文今天要再来分享一款基于HTML5 Canv ...

  6. 基于HTML5 Canvas和jQuery 的绘图工具的实现

    简单介绍 HTML5 提供了强大的Canvas元素.使用Canvas并结合Javascript 能够实现一些很强大的功能.本文就介绍一下基于HTML5 Canvas 的绘图工具的实现.废话少说,先看成 ...

  7. JavaScript 基于HTML5 canvas 获取文本占用的像素宽度

    基于HTML5 canvas 获取文本占用的像素宽度   by:授客 QQ:1033553122 直接上代码   // 获取单行文本的像素宽度 getTextPixelWith(text, fontS ...

  8. 基于HTML5 Canvas实现的图片马赛克模糊特效

    效果请点击下面网址: http://hovertree.com/texiao/html5/1.htm 一.开门见山受美国肖像画家Chuck Close的启发,此脚本通过使用HTML5 canvas元素 ...

  9. HTML5 Canvas 高仿逼真 3D 布料图案效果

    HTML5 规范引进了很多新特性,其中最令人期待的之一就是 Canvas 元素,HTML5 Canvas 提供了通过 JavaScript 绘制图形的方法,非常强大.下面给大家分享一个 HTML5 C ...

随机推荐

  1. 解析PHP多种序列化与反序列化的方法

    1. serialize和unserialize函数这两个是序列化和反序列化PHP中数据的常用函数. 复制代码 代码如下: <?php$a = array('a'=> 'Apple' ,' ...

  2. 80端口被系统服务【kernel&System】占用解决方案

    netstat -ano | findstr port    //查看端口占用情况 tasklist | findstr port   //查看端口被占用的具体服务名 运行net stop http ...

  3. LINUX 笔记-条件测试

    格式:test condition 文件测试状态 -d 目录 -s 文件长度大于0,非空 -f 正规文件 -w 可写 -l 符号链接 -u 文件有suid位设置 -r 可读 -x 可执行 字符串测试 ...

  4. LeetCode 81. Search in Rotated Sorted Array II(在旋转有序序列中搜索之二)

    Follow up for "Search in Rotated Sorted Array":What if duplicates are allowed? Would this ...

  5. SQL语句查询表中的所有约束

    select * from sysobjects where parent_obj in(select id from sysobjects where name='表名') 或者 exec sp_h ...

  6. struts2(五)之struts2拦截器与自定义拦截器

    前言 前面介绍了struts2的输入验证,如果让我自己选的话,肯定是选择xml配置校验的方法,因为,能使用struts2中的一些校验规则,就无需自己编写了, 不过到后面应该都有其他更方便的校验方法,而 ...

  7. Chinese Rings

    Chinese Rings Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...

  8. SSH框架整合--applicationContext.xml文件配置实例

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...

  9. css元素选择器 first-child nth-child

    E:first-child   只要E元素是它的父级的第一个子元素,就选中.它需要同时满足两个条件,    (1)是"第一个子元素",    (2)是"这个子元素刚好是E ...

  10. 全局作用域 eval

    eval是在caller的作用域里运行传给它的代码: var x = 'outer';   (function() {     var x = 'inner';     eval('x'); // & ...