资讯专栏INFORMATION COLUMN

基于HTML5和WebGL的三维可视立体动态流程图

printempw / 3408人阅读

摘要:甲方爸爸的企业,部门繁多,流程复杂,为了提升业务流程的效率,同时优化企业自身的管理,提出了一个将其现有业务流程进行三维可视化的需求。利用获取模型数据,然后三行代码便可建造一个流程图系统。

惯例先扯点闲篇

记得15年那会儿,我给人讲WebGL,还得从头科普,三令五申要用Chrome,末了再强调一句,记得启用WebGL功能。今天,但凡懂点儿Web开发的,都会来一句“网页3D用WebGL”。

怎么形容这种感受呢?

这两年的技术发展,大家想必都看在眼里,单用“爆发”二字,实在难以描述其中的惊天巨变。而回到网页3D这个话题上,我想,最大的驱动力,莫过于16年至今虚拟现实的迅速崛起,彻底推进了三维可视化技术的突飞猛进,而物联网发力,又开启了一扇通往新世界的大门。

游戏界至今争论不休的Unity还是HTML5,依我看至少WebGL活的好好的,而插件技术么,则让我想起了一首悲伤的歌:dying in the sun…

三维可视化应用盘点

今天当然还是给大家介绍一款最新的三维可视化成果,开始正题前,我想先盘点一下这两年还算靠谱的一些三维应用。

工业上的,偏销售侧有复杂零件的三维展示,依托零件原有模型,做好参数转换,比较容易实现。偏管理侧,主要是厂房车间和生产设备的三维监控;电力行业,有无人值守变电站的巡检和监控,结合三维可以进行远程巡检作业,降低人工作业的风险;仓库和粮仓,引入三维后,可以结合库房系统、环境系统,进行全方位的管理;矿山和隧道,这个很好理解:作业越是危险、对环境要求越高的地方,越是需要虚拟仿真;3D家装设计, 通常是在线模式,拖拽设计,结合家具贩售,早先是以Stage3D为主,这两年也看到很多WebGL的案例;博物馆、图书馆、档案馆的导览结合解说,复杂设备的虚拟仿真培训商品展示,这个比较多了,像虚拟试衣间,商品的三维在线浏览,比如刚看到这款霸气侧漏的零食:

开个玩笑。其实不管哪个行业,三维应用很大的一部分工作量源于建模,而这次我们想分享的是和上述应用完全不同的一个案例。

正文

这次的案例,对模型几乎没有要求,只是用了最基本的形状元素,但是却可以解决企业在不断发展壮大,尤其是信息化进程不断深入的过程中,都会遇到的问题,那就是如何将复杂流程可视化

什么是流程?

流程,看起来很简答的两个字,英文process。我们这里所说的流程,主要是企业的业务流程,如生产流程、各类行政申请流程、财务审批流程、人事处理流程、质量控制及客服流程等等。

业务流程对于企业的意义不仅仅在于对企业关键业务的一种描述;更在于对企业的业务运营有着指导意义,这种意义体现在对资源的优化、对企业组织机构的优化以及对管理制度的一系列改变。这种优化的目的实际也是企业所追求的目标:降低企业的运营成本,提高对市场需求的响应速度,争取企业利润的最大化。(from智库百科MBAlib)

为啥我突然开始拽商业名词了呢?因为,这是甲方爸爸给出的一个难题。

甲方爸爸的企业,部门繁多,流程复杂,为了提升业务流程的效率,同时优化企业自身的管理,提出了一个将其现有业务流程进行三维可视化的需求。

为什么要把流程进行三维可视化呢?

通俗的说,因为平面的实在是看不清理不顺了;时髦点讲,只有“升维”,才能展示和包容更多的信息。

看到这里的三体同好们,请不要吝啬向我隔空挥舞小手!

举个栗子

甲方爸爸的信息实在太多,而且不能透露,我们就找其中几个简单的举个例子,比如每个企业每天都在处理的报账流程:(应要求马赛克了一些文字)

报账流程业务图:

二层逻辑交互图:

报账业务基础架构:

看到如此复杂的结构,无论你是以下哪种表情,都不要惊慌:

流程结构梳理

我们先简单梳理下流程图的大致框架,一共可以分为五层:

业务流程层:起点、审批、服务;

应用逻辑层:WEB服务、数据库服务、定时作业服务、接口服务、应用服务;

实例:WEB服务实例、财辅数据库实例、支付服务实例;

OS:EB服务OS、财辅数据库OS;

硬件:主机、交换机。

初稿

为了让这五层之间的关系一目了然,分层展示是必不可少的。每个层次里,又分为几个模块,层次与层次、模块与模块之间都有业务上的联系。根据梳理的逻辑关系,我先整了一个初稿。流程和业务先用简单的方块和圆柱代替,底层的机柜模型,直接用存货模型,机房相关的模型,我们大大的有。

虽然看起来层次感有了,不过这只是个整体的框架demo,我们在这基础上一步步修改。

圆弧效果

说下层模型的圆弧效果。为了把每个层做成圆弧的效果,我把层模型拆解成了由9个简单模型组合而成,上图给你们看(原谅我图画得渣渣)。

模型1、2、3、4、5均为厚度相同的立方体,模型6、7、8、9为大小相等的1/4圆柱体,9个模型组合成层模型。

模型1的代码:(模型1,2,3,4,5差不多,就只贴模型1的代码了。)

var centerNode = new mono.Cube({
    width: width,
    height: height,
    depth: depth,
});
centerNode.s({
    "m.type": "phong",
    "m.color": color
});
    

模型6的代码:

var leftTopCylinder = new mono.Cylinder({
    radiusTop: radius,
    radiusBottom: radius,
    height: height,
    arcLength: Math.PI / 2, //圆柱的圆弧所占长度
    arcStart: Math.PI //圆弧开始的角度
});
    
leftTopCylinder.s({
    "m.type": "phong",
    "m.color": color
});
leftTopCylinder.p(-width / 2, 0, -depth / 2);

9个模型合并:

var combo = new mono.ComboNode([centerNode, leftNode, rightNode, topNode, bottomNode, leftTopCylinder, rightTopCylinder, leftBottomCylinder, rightBottomCylinder],["+"],true);
背景和配色

首先,增加了背景图片,选取的是一张星空的图片,之后根据背景修改了配色和部分模型。

效果还是不错的,看起来更加大气。增加背景代码:

network.setClearColor(0, 0, 0);
network.setClearAlpha(0);
network.setBackgroundImage("./images/background.jpg");
文字

图中显示文字类似对话框的东西其实是billboard,先创建一个billboard,再用canvas画一张图作为贴图贴到上面就可以了。

var billboard = new mono.Billboard();
var canvas = document.createElement("canvas");
    var context = canvas.getContext("2d");
    context.font = "130px 微软雅黑";

    var array = [];
    if (text.indexOf("
")) {
        array = text.split("
");
    } else {
        array = [text]
    }
    var length = 0;
    for (var i = 0; i < array.length; i++) {
        if (i == 0) {
            length = context.measureText(array[i]).width;
        } else {
            length = Math.max(context.measureText(array[i]).width, length);
        }
    }

    var size = mono.Utils.getMaxTextSize(array, context.font);
    var width = mono.Utils.nextPowerOfTwo(length);
    var oHeight = size.height;
    var arrowHeight = 40;
    var arrowWidth = 80;
    var height = mono.Utils.nextPowerOfTwo(oHeight + arrowHeight);

    canvas.height = height;
    canvas.width = width;
    var lineHeight =(height - arrowHeight - 40) / array.length;
    var oLineHeight = oHeight / array.length;
    var radius = width / 16;

    var context = canvas.getContext("2d");
    context.globalAlpha = 0.9;
    context.fillStyle = bgColor;
    context.save();
    context.beginPath();
    context.moveTo(radius + 10, 10);
    context.lineTo(width - radius - 10, 10);
    context.arcTo(width - 10, 10, width - 10, radius + 10, radius);
    context.lineTo(width - 10, height - arrowHeight - radius - 10);
    context.arcTo(width - 10, height - arrowHeight - 10, width - radius -10, height - arrowHeight - 10, radius);
    context.lineTo(width / 2 + arrowWidth / 2 - 10, height - arrowHeight - 10);
    context.lineTo(width / 2 - 10, height - 10);
    context.lineTo(width / 2 - arrowWidth / 2 - 10, height - arrowHeight - 10);
    context.lineTo(radius + 10, height - arrowHeight - 10);
    context.arcTo(10, height - arrowHeight - 10, 10, height - arrowHeight - radius - 10, radius);
    context.lineTo(10, radius + 10);
    context.arcTo(10, 10, radius + 10, 10, radius);
    context.closePath();
    context.fill();
    context.globalAlpha = 1;
    context.lineWidth = 10;
    context.strokeStyle = bgColor;
    context.stroke();
    context.restore();

    context.fillStyle = fontColor;
    context.textBaseline = "middle";
    context.font = "120px 微软雅黑";
    for (var i = 0; i < array.length; i++) {
        var text = array[i];
        length = context.measureText(text).width;
        context.fillText(text, (width - length) / 2, lineHeight * (i + 0.5));
    }
    billboard.s({
        "m.texture.image": canvas,
        "m.texture.offset": new mono.Vec2(0, 0.005),
        "m.texture.anisotropy": 8,
        "m.alignment": mono.BillboardAlignment.bottomCenter
    });
obj模型

这两种属于obj模型,是设计小姐姐做的,然后我们通过make.Default.register函数定义模型,通过make.Default.load函数加载使用模型。

为了使效果更逼真,我们给模型做了环境贴图。

 object3d.setStyle("m.envmap.image", make.Default.getEnvMap("envmap5"));

动画

单纯的静态图看起来有些单调,所以我们给连线加了动画效果:找一张一半透明一半有颜色的图片,作为贴图贴在连线上,利用动画函数使贴图不断平移,就实现了下面的效果。

实体模型

最底层的模型采用了实体模型,真实感更强:

嵌套关系

上面也提到过,层与层、层内各个模块中之间存在错综复杂的多层嵌套关系,为了展现这种关系,那肯定就要连线,话不多说,直接上图。

线的类型有两种,层与层之间的连线类型是link,每层模块之间的连线类型是pathLink,创建pathLInk代码如下:

createPathLink: function (data) {
    var box = main.sceneManager.getDataBox();
    var fromNode = main.sceneManager.getNodeByDataOrId(data.fromId);
    var toNode = main.sceneManager.getNodeByDataOrId(data.toId);
    var radius = data.path.radius || 3;
    var color = data.path.color || "yellow";
    var endCap = data.path.endCap;
    var startCap = data.path.startCap;
    var linkType = data.routeType;
    var flow = data.path.flow || "";
    var workflowId = data.workflowId || ""; 
    if (fromNode && toNode) {
        var link = new mono.PathLink(fromNode, toNode, data.id);
        var plength = link.getPath().getLength();
        link.setRadius(radius);
        link.s({
            "m.type": "phong",
            "m.color": color,
            "m.ambient": color
        });
        link.workflowId = workflowId;
        if (endCap) {
            var endCapSize = data.path.endCapSize || 10;
            var endCapR = data.path.endCapR || 2;
            link.setEndCap(endCap);
            link.setEndCapSize(endCapSize);
            link.setEndCapR(endCapR);
        }
        if (startCap) {
            var startCapSize = data.path.startCapSize || 10;
            var startCapR = data.path.startCapR || 2;
            link.setStartCap(startCap);
            link.setStartCapSize(startCapSize);
            link.setStartCapR(startCapR);
        }
        if (linkType) {
            link.setLinkType(linkType);
        }
        box.add(link);
    }
}

link类型连线与pathLink类型连线大体相同,之所以层与层之间选择link类型,有两个原因:一是当镜头拉近时,link类型的连线粗细不会改变,二是便于控制拐点,就是下图中的红圈处。

link.setLinkType("control");//control属性控制连线的拐点
link.setControls(controls);//controls为数组

这样就可以呈现图中的伞状效果啦。

为了增加点朦胧感以及让伞状效果更好,我们特意添加了一点光环,有没有感觉金光从天而降呢?此时请想象自己45°角仰望天空,金光照在脸上。

流程动画

基础打好,下面就可以加上动画,执行流程了。先上图:

点击左边的按钮,出现图中的白色小球,沿着连线运动,完整展现整个流程步骤。当然,镜头会随着小球切换,这样小球时刻在视线正中,妈妈再也不用担心我的视线被挡住。

镜头切换的代码也很简单:

var pos = link.getPointAt(v);
workflowSphere.p(pos);
billboard.p(pos.clone().add(new mono.Vec3(0, 250, 0)));
var camera = main.sceneManager.network3d.getCamera();
camera.lookAt(pos);
camera.p(pos.clone().sub(this._cameraOffset));
数据

最后聊聊数据。为了方(tou)便(lan),我们将流程图的所有数据都存放在后台。在后端页面,可以设置流程图的结构、逻辑、流程节点的样式等。

利用Ajax获取模型数据,然后三行代码便可建造一个3D流程图系统。

dataManager.addCategoryFromJson(loadData.categories);
dataManager.addDataTypeFromJson(loadData.datatypes);
dataManager.addDataFromJson(loadData.datas);

同样,可以在后端页面设置连线的样式、颜色、起点、终点等等,获取到连线数据后,利用上文提到的方法便可绘制出所需要的连线。连动画的起点、走向同样可以在后端页面设置。

如果甲方爸爸觉得某个流程有问题,需要修改时,不要怕,默默打开后端页面改几个节点就好了。速度这么快,快夸我快夸我。

总而言之,只需要通过数据配置即可生成不同的三维流程,满足客户的各种需求。

对demo感兴趣的同学,可以给我发邮件:tw-service@servasoft.com,甲方爸爸的数据不能给你,demo还是可以给你们看一眼的。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/56795.html

相关文章

  • SegmentFault 技术周刊 Vol.35 - WebGL:打开网页看大片

    摘要:在文末,我会附上一个可加载的模型方便学习中文艺术字渲染用原生可以很容易地绘制文字,但是原生提供的文字效果美化功能十分有限。 showImg(https://segmentfault.com/img/bVWYnb?w=900&h=385); WebGL 可以说是 HTML5 技术生态链中最为令人振奋的标准之一,它把 Web 带入了 3D 的时代。 初识 WebGL 先通过几个使用 Web...

    objc94 评论0 收藏0
  • 阿里云前端周刊 - 第 29 期

    摘要:前端魔法堂异常不仅仅是在学习时我们会被告知异常和错误是不一样的,异常是不会导致进程终止从而可以被修复,但错误将会导致进程终止因此不能被修复。 推荐 1. RESTful API 设计最佳实践 https://blog.philipphauer.de/... 项目资源的URL应该如何设计?用名词复数还是用名词单数?一个资源需要多少个URL?用哪种HTTP方法来创建一个新的资源?可选参数应...

    Jaden 评论0 收藏0
  • 阿里云前端周刊 - 第 29 期

    摘要:前端魔法堂异常不仅仅是在学习时我们会被告知异常和错误是不一样的,异常是不会导致进程终止从而可以被修复,但错误将会导致进程终止因此不能被修复。 推荐 1. RESTful API 设计最佳实践 https://blog.philipphauer.de/... 项目资源的URL应该如何设计?用名词复数还是用名词单数?一个资源需要多少个URL?用哪种HTTP方法来创建一个新的资源?可选参数应...

    leeon 评论0 收藏0
  • vue-threeJS数据驱动三维图形可视

    摘要:数据驱动的三维图形可视化在信息暴涨的年间,冷暴力的扁平化确实有效降低用户的信息焦虑感,使有限的精力更高效处理过多的信息流。 数据驱动的三维图形可视化 在信息暴涨的2010-2016年间,冷暴力的扁平化确实有效降低用户的信息焦虑感,使有限的精力更高效处理过多的信息流。二维平面化扁平化在苹果等大头引领下,成为大众用户机器交流默认的语言。然后,随着PC、平板、手机、智能家居等用户持有终端的性...

    SegmentFault 评论0 收藏0
  • 分享数百个 HT 工业互联网 2D 3D 可视化应用案例

    摘要:研华科技工业物联网云平台,基于图形组件技术,集成边缘计算和云平台,提供从边缘感知及设备到云的数据采集分析可视化软件服务,协助系统集成商和制造商快速开发各垂直产业的应用,形成新形态的云端商务模式。这也是峰会结束后,该系统首次对外展出。 过去的 2018 年,我们认为是国内工业互联网可视化的元年,图扑软件作为在工业可视化领域的重度参与者,一线见证了众多 HTML5/Web 化、2D/3D ...

    anyway 评论0 收藏0

发表评论

0条评论

printempw

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<