Chinaunix首页 | 论坛 | 博客
  • 博客访问: 952027
  • 博文数量: 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

2015-07-12 23:41:49

很多可视化编辑器都或多或少有一些拖拽功能,比如从一个List列表中拖拽一个节点到拓扑组件上进行建模,并且在拖拽的过程中鼠标位置下会附带一个被拖拽节点的缩略图,那么今天我们就来实现这样的拖拽效果。

首先我们需要创建一个ListView列表,在列表中加入图片信息,让ListView列表不那么单调,先来看看效果图。

接下来我们一步一步来是想这个ListView列表,先来解决下数据,在这里我就列举一两个:

点击(此处)折叠或打开

  1. var products = [
  2.     {
  3.         ProductId : 1,
  4.         ProductName : "Chai",
  5.         QuantityPerUnit : "10 boxes x 20 bags",
  6.         UnitPrice : 18.00,
  7.         Description : "Soft drinks, coffees, teas, beers, and ales"
  8.     },
  9.     {
  10.         ProductId : 2,
  11.         ProductName : "Chang",
  12.         QuantityPerUnit : "24 - 12 oz bottles",
  13.         UnitPrice : 19.00,
  14.         Description : "Soft drinks, coffees, teas, beers, and ales"
  15.     },
  16.     ……
  17. ];

有了数据,我们就可以来创建ListView组件了:

点击(此处)折叠或打开

  1. var listView = new ht.widget.ListView(),
  2.     view = listView.getView();

  3. document.body.appendChild(view);

这时我们创建的是一个空的ListView组件,在浏览器上看不到任何东西,那么接下来我们就该把我们定义的数据添加到ListVIew组件上了:

点击(此处)折叠或打开

  1. products.forEach(function(product){
  2.     var data = new ht.Data();
  3.     data.a(product);
  4.     listView.dm().add(data);
  5. });

数据的添加是不是很简单,但是ListView组件上显示的内容默认是Data的name属性或displayName属性,在创建Data时,并没有对Data设置displayName或者name属性,所以这个时候在页面上看到的还是一个空的ListView组件,别急,我们可以在不设置displayName或name属性的情况下让组件显示效果图上的文本内容,请看:

点击(此处)折叠或打开

  1. listView.getLabel = function(data){
  2.     return data.a('ProductName') + ' - $' + data.a('UnitPrice').toFixed(2);
  3. };

嘿嘿,ListView组件提供了getLabel方法供用户重载来实现自定义显示文本内容,这下应该就可以显示文本内容了吧~

oh no~还是什么都没有,是不是还少了点什么呢~对了,忘记给ListView组件添加铺满浏览器的样式了,将厦门的样式添加到head标签中:

点击(此处)折叠或打开

  1. <style>
  2.     html, body {
  3.         padding: 0px;
  4.         margin: 0px;
  5.     }
  6.     .main {
  7.         margin: 0px;
  8.         padding: 0px;
  9.         position: absolute;
  10.         top: 0px;
  11.         bottom: 0px;
  12.         left: 0px;
  13.         right: 0px;
  14.     }
  15. </style>

接下来指定view的className属性:

点击(此处)折叠或打开

  1. view.className = 'main';

噢~总算出来了~


行高太小了,背景也太单调了,向效果图看齐:

点击(此处)折叠或打开

  1. listView.setRowHeight(50);
  2. listView.drawRowBackground = function(g, data, selected, x, y, width, height){
  3.     if(this.isSelected(data)){
  4.         g.fillStyle = '#87A6CB';
  5.     }
  6.     else if(this.getRowIndex(data) % 2 === 0){
  7.         g.fillStyle = '#F1F4F7';
  8.     }
  9.     else{
  10.         g.fillStyle = '#FAFAFA';
  11.     }
  12.     g.beginPath();
  13.     g.rect(x, y, width, height);
  14.     g.fill();
  15. };

通过setRowHeight()方法设置行高,通过重载drawRowBackground()方法绘制交叉背景。


嘿,有点样子了,和效果图越来越近了~那么就差图标了呢。


点击(此处)折叠或打开

  1. ht.Default.setImage('1', 40, 40, 'data:image/jpeg;base64,...');
  2. ht.Default.setImage('2', 40, 40, ‘data:image/jpeg;base64,...');
    ......

    listView.setIndent(60);

    listView.getIcon = function(data){

        return data.a('ProductId');

    };


通过ht.Default.setImage()方法定义ProductId对应的图片资源,以ProductId作为图片的别名,然后接下来定义icon位置大小为60,重载ListView的getIcon方法返回数据中定义的ProductId属性,如此就可以看到图标了。


还没完,效果图上显示的图片是圆形的,这该如何是好呢?别急,我们有万能的矢量,上么样的图形都难不倒我们:

点击(此处)折叠或打开

  1. ht.Default.setImage('productIcon', {
  2.     width: 50,
  3.     height: 50,
  4.     clip: function(g, width, height) {
  5.         g.beginPath();
  6.         g.arc(width/2, height/2, Math.min(width, height)/2-3, 0, Math.PI * 2, true);
  7.         g.clip();
  8.     },
  9.     comps: [
  10.         {
  11.             type: 'image',
  12.             stretch: 'uniform',
  13.             rect: [0, 0, 50, 50],
  14.             name: {func: function(data){return data.a('ProductId');}}
  15.         }
  16.     ]
  17. });

在代码中我们定义了一个名称为productIcon的矢量,在矢量中通过clip属性定义裁切区域,效果就是超出该裁切区域外的内容将被隐藏。现在矢量定义好了,我们只需要在ListView的getIcon()方法中返回我们定义的矢量名称就可以实现圆形图标了:

点击(此处)折叠或打开

  1. listView.getIcon = function(data){
  2.     return 'productIcon';
  3. };

到这里,和效果图的效果就一模一样了~那么接下来我们就该创建3D拓扑组件了,来看看效果图:


很简单,就在3D拓扑中放两个正方体:

点击(此处)折叠或打开

  1. var g3d = new ht.graph3d.Graph3dView();

    var node = new ht.Node();

    node.s3(30, 30, 30);

    node.p3(-30, 15, 0);

    node.s('all.color', '#87A6CB');

    g3d.dm().add(node);

    node = new ht.Node();

    node.s3(30, 30, 30);

    node.p3(30, 15, 0);

    node.s('all.color', '#87A6CB');

    node.setElevation(15);

    g3d.dm().add(node);

这是你会发现并没有像效果图中显示的那么会有网格效果,并且视角也不对,没事,待我添加几个属性:


点击(此处)折叠或打开

  1. g3d.setEye(-100, 100, 80);
  2. g3d.setGridVisible(true);
  3. g3d.setGridColor('#F1F4F7');

如此就和效果图一模一样了~

和3D拓扑是两个独立的组件,我们该如何将这两个组件组合在一起呢?这时候,我想到了BorderPane组件,将List组件放在左边,将3D拓扑组件放在右边:


点击(此处)折叠或打开

  1. var borderPane = new ht.widget.BorderPane();

  2. borderPane.setLeftView(listView, 350);
  3. borderPane.setCenterView(g3d);

看,成功将两个组件合并在一起了,离成功不远了。接下来就是今天的重头戏了,该如何实现拖拽List上的节点到3D拓扑上,并实现节点的图标吸附到3D拓扑的图元上呢,我给大家细细道来。


首先先来了解下ListView的handleDragAndDrop()方法,draganddrop一共有4个状态:prepare、begin、between和end,可更具这4个不同状态来做不同的业务处理。

第一步,我们来实现鼠标附带图标的效果,在拖拽ListView的节点时,在鼠标下方增加一个该节点的缩略图:

思路是这样的:

1. 在prepare状态时获取当前拖拽节点的ProductId属性,并通过调用ht.Default.toCanvas()方法将当前拖拽节点结合矢量productIcon获得一个canvas对象;

2. 在begin状态时根据鼠标当前位置设置canvas对象的left和top属性,并将其添加到DOM树中;

3. 在between状态时,根据鼠标位置信息,重新设置canvas对象的left和top属性,令canvas对象一直跟着鼠标在移动;

4. 在end状态时,将canvas对象移除DOM树。

点击(此处)折叠或打开

  1. var dragImage = null,
  2.     productId = null;
  3. listView.handleDragAndDrop = function(e, state) {
  4.     if (state === 'prepare') {
  5.         var data = listView.getDataAt(e);
  6.         listView.sm().ss(data);
  7.         if (dragImage && dragImage.parentNode) {
  8.             document.body.removeChild(dragImage);
  9.         }
  10.         dragImage = ht.Default.toCanvas('productIcon', 30, 30, 'uniform', data);
  11.         productId = data.a('ProductId');
  12.     }
  13.     else if (state === 'begin') {
  14.         if (dragImage) {
  15.             var pagePoint = ht.Default.getPagePoint(e);
  16.             dragImage.style.left = pagePoint.x - dragImage.width / 2 + 'px';
  17.             dragImage.style.top = pagePoint.y - dragImage.height / 2 + 'px';
  18.             document.body.appendChild(dragImage);
  19.         }
  20.     }
  21.     else if (state === 'between') {
  22.         if (dragImage) {
  23.             var pagePoint = ht.Default.getPagePoint(e);
  24.             dragImage.style.left = pagePoint.x - dragImage.width / 2 + 'px';
  25.             dragImage.style.top = pagePoint.y - dragImage.height / 2 + 'px';
  26.         }
  27.     }
  28.     else {
  29.         if (dragImage) {
  30.             if (dragImage.parentNode) {
  31.                 document.body.removeChild(dragImage);
  32.             }
  33.             dragImage = null;
  34.             productId = null;
  35.         }
  36.     }
  37. }

如此在拖拽ListView节点时就能够看到有一个小图标一直跟着鼠标在移动。


OK,接下来该解决图元吸附功能,当鼠标拖拽ListView节点到3D拓扑上的图元是,将该节点的图标设置为图元当前面的贴图。

思路是这样子的:

1. 在between状态时,通过ht.Default.containedInView()方法判断殿前鼠标是否在3D拓扑组件上;

2. 若鼠标在3D拓扑上,则通过g3d.getHitFaceInfo()方法,根据鼠标当前信息获取当前鼠标下的图元表面信息;

3. 若当前鼠标在图元的某个表面上,则先保存该图元表面信息的贴图,然后设置当前图元表面的贴图为拖拽节点对应的图片,最后将当前图元表面信息缓存下来,当鼠标离开该表面时,还原图元的贴图;

4. 在end状态时,如果当前鼠标位置在某个图元表面时,就将当前拖拽节点的对应的图片做为当前图元表面的贴图。

那么接下来就需要对ListView组件的handleDragAndDrop()方法做些微的修改了。

点击(此处)折叠或打开

  1. listView.handleDragAndDrop = function(e, state) {
  2.     if (state === 'prepare') {
  3.         var data = listView.getDataAt(e);
  4.         listView.sm().ss(data);
  5.         if (dragImage && dragImage.parentNode) {
  6.             document.body.removeChild(dragImage);
  7.         }
  8.         dragImage = ht.Default.toCanvas('productIcon', 30, 30, 'uniform', data);
  9.         productId = data.a('ProductId');
  10.     }
  11.     else if (state === 'begin') {
  12.         if (dragImage) {
  13.             var pagePoint = ht.Default.getPagePoint(e);
  14.             dragImage.style.left = pagePoint.x - dragImage.width / 2 + 'px';
  15.             dragImage.style.top = pagePoint.y - dragImage.height / 2 + 'px';
  16.             document.body.appendChild(dragImage);
  17.         }
  18.     }
  19.     else if (state === 'between') {
  20.         if (dragImage) {
  21.             var pagePoint = ht.Default.getPagePoint(e);
  22.             dragImage.style.left = pagePoint.x - dragImage.width / 2 + 'px';
  23.             dragImage.style.top = pagePoint.y - dragImage.height / 2 + 'px';

  24.             if (ht.Default.containedInView(e, g3d)) {
  25.                 if (lastFaceInfo) {
  26.                     lastFaceInfo.data.s(lastFaceInfo.face + '.image', lastFaceInfo.oldValue);
  27.                     lastFaceInfo = null;
  28.                 }
  29.                 var faceInfo = g3d.getHitFaceInfo(e);
  30.                 if (faceInfo) {
  31.                     faceInfo.oldValue = faceInfo.data.s(faceInfo.face + '.image');
  32.                     faceInfo.data.s(faceInfo.face + '.image', productId);
  33.                     lastFaceInfo = faceInfo;
  34.                 }
  35.             }
  36.         }
  37.     }
  38.     else {
  39.         if (dragImage) {
  40.             if (lastFaceInfo) {
  41.                 lastFaceInfo.data.s(lastFaceInfo.face + '.image', lastFaceInfo.oldValue);
  42.                 lastFaceInfo = null;
  43.             }
  44.             if (ht.Default.containedInView(e, g3d)) {
  45.                 var faceInfo = g3d.getHitFaceInfo(e);
  46.                 if (faceInfo) {
  47.                     faceInfo.data.s(faceInfo.face + '.image', productId);
  48.                 }
  49.             }
  50.             if (dragImage.parentNode) {
  51.                 document.body.removeChild(dragImage);
  52.             }
  53.             dragImage = null;
  54.             productId = null;
  55.         }
  56.     }
  57. };

在看看最后的效果图吧


今天就到这吧,将的内容有点多,涉及到HT for Web的知识点也比较多,下面附上本次Demo的源代码,感兴趣的朋友可以载下来看看,同时也欢迎大家留言质询。

下载源码

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