• svg操纵方案 基于 D3 还是 angular?


    之前还是想简单了, 现在重新写这篇。把逻辑拆分粒度的辨析,放到外面去。

    问题提出:svg控制方案 基于 D3 还是 angular

    根据这个,html 4种展现样式:普通的html,svg,2D canvas,webgl 3d canvas. 

    Angular和D3都有各自的数据绑定方式来操作dom,需要合适的方式让它们不打架,代码尽量优雅。

    1个dom元素只让1个框架去操作。

    尤其是svg中各元素,最好不要ng绑定了一些,d3又绑定另一些。

      

    我需求是:用svg做图形化输入和图文混合的dom展现。不止是单向的输出展示visualization问题,还有图文混合输入的问题

    1数据源用ng的service,数据结构为聚合根下辖若干种/若干数量的领域对象

    2多个图层叠加,各自管理各自的显示效果。

    3输出显示:当数据源发生改变时,图表刷新显示

    4输入响应:当图表中元素被拖拽等情况,要保存更新位置等参数,数据源数据相应被更新。

      

    用谁创建dom元素?

    2种方案:

    1 用D3。包括selectAll选择集/data()数据绑定/添加属性,添加drag等行为

    this.tiles
    .attr('transform', function (node) {
    const piece = node.piece;
    // console.log(piece);
    return 'translate(' + piece.transform.x + ',' + piece.transform.y + ')';
    });

    this.images
    .attr('xlink:href', function (node) {
    const piece = node.piece;
    return piece.image;
    })
    .attr('width', scale)
    .attr('height', scale);

    2  angular的template:

    <svg viewBox="0 0 250 250"
         (mousemove)="setCircleLocation($event)">
      <svg:defs>
        <svg:clipPath id="clip">
          <svg:circle [attr.cx]="circle[0]" [attr.cy]="circle[1]" r="100" />
        </svg:clipPath>
      </svg:defs>
      <svg:g clip-path="url(#clip)">
        <svg:polygon points="125,30 125,30 125,30 31.9,63.2 46.1,186.3 125,230 125,230 125,230 203.9,186.3 218.1,63.2" />
        <svg:polygon points="125,30 125,52.2 125,52.1 125,153.4 125,153.4 125,230 125,230 203.9,186.3 218.1,63.2 125,30" />
        <svg:path d="M125,52.1L66.8,182.6h0h21.7h0l11.7-29.2h49.4l11.7,29.2h0h21.7h0L125,52.1L125,52.1L125,52.1L125,52.1
          L125,52.1z M142,135.4H108l17-40.9L142,135.4z"/>
      </svg:g>
    </svg>

    其实背后都隐含了创建dom的过程。angular在编译后会产生JavaScript DOM API,在运行时创建dom元素

    对HTML与SVG采用不同的API

    document.createElement('div');

    document.createElementNS('http://www.w3.org/2000/svg', 'path');

    js有点相当于前端的汇编语言,不论用jquery,d3,还是angular,最终都是一样的js代码。

    区别在于,越往汇编层,越暴露底层步骤(每个div操作,类似于操作每个寄存器);而越往高层走,更强调的是目的和最终要的结果。

    高级程度最高的,当然是angular了。

    这也就澄清了一个自己的误区,

    D3≠svg。

    虽然通常看到的svg操作案例,都是用d3或者基于d3的扩展包. 但绝不是说svg只能用d3来操作。

    d3虽然名字是data driven document。 但其实里面的document指的是svg或canvas,d3更类似于openpyxl这样的操作xlsx的库,不等于xlsx格式标准。完全可以用xlwt来操作xlsx。

    d3主要是实现了很多现成的layout(比如力导向图什么的),方便显示地图,定义动画/拖拽/圈选(用brush)比较方便。

    从功能上说,angular 一样是data driven document,只不过是用模板+数据绑定的方式。

    d3的data只能是array型,而angular绑定的数据格式就灵活多了,单个的class也可以,array型的用*NgFor。

    用汇编还是脚本语言,都能写出同样功能程序来。在不考虑的性能的时候,无非是开发速度/复用难度。这些考虑。

    结论:

    还是老实一点,根据职责不同,读写分离,准备2种方案:

    读:用D3单纯展示数据模型,数据变化时用d3重画

    写:用ng双向绑定svg,加载数据模型,获取svg拖拽、圈选事件,修改数据模型。

    1 如果仅仅需要数据->svg,展示、特别是简单的图、各种D3有的layout,或者地图这种作为底图,数据不变的图,(可以拖拽,但不要拖拽后的结果),那么就直接用D3,不用ng,或者ng简单封装即可。

    2 如果需要把svg作为一种输入界面,工作图,如需要保存、分发拖拽后元素的位置,需要双向数据绑定(svg<->数据模型),不需要动画、过渡,那么就直接用ng,不用d3

    d3类似matplotlab,做visualization非常舒服,做编辑图片像素区域的界面就非常难受,还是乖乖用qt的QImage,Qpixmap舒服。

    ng的templates可以显式表达svg的结构,模板语法也类似jinja2。

    ——看文档、想、实做。。。好几天了,虽然略有纠结,磕磕绊绊的,但好在有读写分离的经验,对unity,qt,matplotlib都还算熟悉,得到现在的结论还算不纠结。

    附录A,NG的小坑

    SVG不是html,而是XML。ng在处理模板中自定义元素的时候,不能和html一样。

    应该这样做

    @Component({
      selector: '[app-logo-left]',
      templateUrl: './app-logo-left.component.html'
    })
    export class LogoLeftComponent {
    }

    把方括号写在selector里面,在模板里不写方括号

    <svg viewBox="0 0 250 250">
      <svg:g app-logo-left />
      <svg:g app-logo-right />
      <svg:g app-logo-a />
    </svg> 

    附录B,D3的小坑

    1

    In D3, data() accepts only 3 things: an array, a function or nothing

    2 data带主键函数之前,必须先不带主键函数已经绑定过一次,才可以,否则报错

    http://www.ourd3js.com/wordpress/811/

    //数据
    var persons = [ { id: 3 , name:"张三" },
    { id: 6 , name:"李四" },
    { id: 9 , name:"王五" }];
     
    //选择body中的所有的p元素
    var p = d3.select("body").selectAll("p");
     
    //绑定数据,并修改p元素的内容
    p.data(persons)
    .text(function(d){
        return d.id + " : " + d.name;
    });
     
    //更新persons里的数据
    persons = [     { id: 6 , name:"张三" },
                        { id: 9 , name:"李四" },
                        { id: 3 , name:"王五" }];
     
    //根据键函数的规则绑定数据,并修改内容
        p.data(persons, function(d){ return d.id; })
                .text(function(d){
                    return d.id + " : " + d.name;
                });
    </script>
    </body>

    3 要想使用d3.drag(),.必须首先用data()进行绑定

    不可避免在angular里同时用数据绑定和d3了

    4 d3.drag()的事件,v4里精简成了'start' 'drag','end'。书和搜索结果里很多都还是v3的,是'dragstart'  会报错。还是要多看最新的官方文档。

    5 确实底层。比用matplotlib还底层,有点不适应。

    坐标轴axis 都要自己手动调位置。

    用path画图每个点都要自己调用scale设置比例尺。

    边距留白,要自己考虑,设置在scale定义里

     ……

    如果不是为了定制特殊效果的图,或者看中力导向图这些特殊的图,只是普通显示一下,可以用别的封装得高级些的库了。

    另一篇Visualizing Data with Angular and D3的作者给出的数据绑定方案:

    写了一个d3组件,封装了d3.js 图类型比如ForceDirectedGraph,然后 图上node 等等,把这些d3里的东西定义在models里,然后定义了D3.service。

    然后,自己写的实际的图组件,就和d3隔离了,只调用自己写的d3组件

    从自己写的d3组件实例化ForceDirectedGraph,而不是直接import * as d3 from 'd3';

  • 相关阅读:
    硬件的那些事
    seaJS学习资料参考
    nodejs前端自动化构建
    移动端开发的坑【持续更新...】
    【retina】手机上 1PX 边框
    【面试季之三】IE6兼容问题
    【面试季二】前端性能优化
    【面试季一】若干前端面试题
    【面试的坑】行内元素是否可以设置宽高
    Bootstrap和IE何时能相亲相爱啊~
  • 原文地址:https://www.cnblogs.com/xuanmanstein/p/7686437.html
Copyright © 2020-2023  润新知