资讯专栏INFORMATION COLUMN

D3js之入门

guqiu / 1990人阅读

摘要:子选集直接通过返回,和子选集分别通过和返回。截止上面也并不是非得用不可,就是一些插入操作,原生也是可以实现的。

相对于echart, highchart等其他图表库算是一个比较底层的可视化工具,简单来讲他不提供任何一种现成的图表,所有的图表都是我们在它的库里挑选合适的方法构建而成。

基于上面的理解,d3无疑会复杂很多但是也强大自由的多,另外因为d3基于svg所以修改图表的样式和结构也会方便很多,但是同样是这个原因,d3的性能比canvas类库差了不少,dom毕竟是拖累浏览器性能的罪魁祸首。顺口提一句,d3也是可以基于canvas构建图表的。但是这篇文章就不提了。

基本概念

对于d3我们可以简单的将其分个类:数据处理, dom处理,事件以及其他。 其实dom和事件其实可以合到一起。

前端做可视化的时候肯定需要对数据进行处理,d3提供了一下常用的方法。

因为d3是基于svg所以跟dom打交道肯定是必须的,这里一定程度替代了jQuery之类的功能。

事件的话其实就是一些交互比如滚轮,拖拽等等都是基础功能可以进行一系列组合排序

请求就是ajax请求数据源了。

数据处理

数据处理就很简单了,就是对于数组和集合以及时间的一些处理方法, 比如数组求中位数方差等等,和lodash的一些方法有重合,但是还是偏向数学方面,方法有点多这里不一一列出了:

// array的方法
d3.min([1, 2, 3, 4]) // 1 不同于Math,min忽略NaN undefined等
d3.range(1, 10) // [1, 2 ... 10]

// collection的方法
d3.entries({foo: 42, bar: true}); // [{key: "foo", value: 42}, {key: "bar", value: true}]
var map = d3.map([{name: "foo"}, {name: "bar"}], function(d) { return d.name; });
map.get("foo"); // {"name": "foo"}
map.get("bar"); // {"name": "bar"}
map.get("baz"); // undefined

// time的方法
d3.timeDays(new Date("2014-01-11"), new Date("2014-02-12")) // 获取2014-01-11 到2014-02-12的日期数组

上面是单纯的数据处理也就是工具类,但是d3的强大不仅仅在于此,d3提供了一个强大算法库,比如力导向图的碰撞检测以及tick等等,这里的功能也属于数据处理但是又跟插入dom密不可分。

d3的数据不仅仅是这些有些跟dom耦合极深没办法完全拎出来说, 而且d3的api极多, 这些东西很多时候也只能边看文档边做。好在d3的示例很多,基本需求都能满足。

dom处理

关于dom操作d3也提供了一系列方便的接口,比如d3.selectd3.append等等, 这部分的接口相当多,个人也没法一一说明, 只能说用法都是一样的,和jQuery相当类似:

svg.selectAll("circle")
  .data(data)
  .enter().append("circle")
  .attr("cx", function(d) { return d.x; })
  .attr("cy", function(d) { return d.y; })
  .attr("r", 2.5);

上面的代码是把circledata进行数据绑定并插入对应的dom节点(引用自链接):

首先,svg.selectAll("circle") 返回一个空选集,因为当前 SVG 还没有任何子元素,该选集的父节点是这个 SVG 容器。

然后将该选集与数据绑定,产生三个新的子选集,分别代表三种可能的状态:enter、update 和 exit。由于当前选集为空,所以 update 和 exit 子选集也为空,enter 子选集就包含了每条数据对应的元素的占位符。

update 子选集直接通过 selection.data 返回,enter 和 exit 子选集分别通过 selection.enterselection.exit 返回。

那些缺少的元素通过对 enter 子选集调用 selection.append 方法来添加到 SVG 中,这样就为每条数据添加了一个新的圆点到 SVG 中。

如上都是链式操作

事件

不同于canvas这里可以直接触发原生事件,让人亲切很多。

事件是指基于dom的一些交互操作,包括但不限于click等原生事件,类似jQuery,事件是通过on进行绑定的:

selection.on("click", function (d) {}) // this指向事件元素, d是绑定的数据可以直接使用

同时,d3提供了很多自定义事件诸如drag, zoom,brush等等,这时候就是通过call调用了:

const brush = d3.brushX()
  .extent([[50, 50], [1100, 150]])
  .on("start brush", brushed)
  .on("end", brushended)

svg.append("g")
  .call(brush)

上面是调用brush事件,同时调用相应的回调, 都是字面意思,至于还有很多有意思的事件,都隐藏在文档中。

其他

这个其他就包含了很多东西, 比如异步请求,解析excel,动画等等,这里不一一说明了, 但是如果发现有需求没法实现不妨看看文档,说不定就内置了呢。

完整示例

下面给个示例,简单力导向图示例jsfiddle:

核心代码如下:

const height = 200
const width = 200

const svg = d3.select("body").append("svg")

const graph = {
    nodes: [
      { id: 1, name: "test1" },
    { id: 2, name: "test2" }
  ],
  links: [
      { source: 1, target: 2 }
  ]
}

const simulation = d3.forceSimulation() 
  .force("charge", d3.forceManyBody().strength(-700).distanceMin(100).distanceMax(1000)) 
  .force("link", d3.forceLink().id(d => d.id)) 
  .force("center", d3.forceCenter(width / 2, height / 2))
  
const link = svg.selectAll("link")
  .data(graph.links)
  .enter()
  .append("line")
  .attr("class", "link")  

const node = svg.selectAll("node")
  .data(graph.nodes)
  .enter().append("g")
  .attr("class", "node")
  
node.append("circle")
    .attr("r", 13)
    .attr("fill", "#999")

node.append("text")
  .attr("dx", -18)
  .attr("dy", 8)
  .style("font-family", "overwatch")
  .style("font-size", "18px")
  .text(d => d.name)


const ticked = function () {
  link.attr("x1", d => d.source.x)
    .attr("y1", d => d.source.y)
    .attr("x2", d => d.target.x)
    .attr("y2",  d => d.target.y);
    
  node.attr("transform", d => `translate(${d.x}, ${d.y})`)
}


const { nodes, links } = graph

simulation.nodes(nodes).on("tick", ticked)
simulation.force("link").links(links)

下面简单解析一下代码部分,const svg = d3.select("body").append("svg") 就是上面提到的d3操作dom的部分,就是类似jQuery的插入操作, 总之我们获取到了svg画布, graph 是提供了数据关系模型,但是一般来讲后端不会这么提供严格的对应关系, 这时候就需要我们队数据进行处理以获取合理的数据格式, 一般来讲数据格式都是如上。

力导向图的核心是forceSimulation, 如字面上的意思就是来模拟力的,这是d3的内部算法我们基本干涉不了, 所以d3的力导向图怎么动最后停在哪都是我们没法精确控制的, forceSimulation 定义了力导向图的基本形态比如key值是否居中等等, 但是到这一步还没对数据进行任何处理。

const linkconst node, 简单讲就是把数据和dom进行绑定插入对应的dom节点, 一直到这一步, 我们完成了基本的步骤:根据关系模型绘制对应节点, 由于不是canvas, 每个数据节点都有一个对应的dom节点, 这里可以对样式进行精确的处理。

截止上面也并不是非得用d3不可,就是一些dom插入操作, 原生js也是可以实现的。 simulation.nodes(nodes).on("tick", ticked)simulation.force("link").links(links)才是d3真正的作用所在,它会修改原来的数据模型在上面挂载一些位置信息, 如图所示:

可以看到,nodes和link上面分别多了不少数据,暂时我们不需要了解那么多, 只要知道xy是节点的位置信息即可,另外力导向图会不停的tick(300次左右),每次tick,d3都会修改graph上的位置信息,它内部肯定做了很多事情, 比如碰撞检测等等。当每次tick触发的时候我们都已调用一个callback,在这个callback里更新所有节点的位置信息,也就是上面代码的ticked, 我们就是修改了node和link的位置信息也就是x1之类的, 这些都是svg提供的接口这里不多做说明了。 到这里, 一个完整的力导向图算是完成了,虽然数据少了点但是并不妨碍我们去理解其中的原理。

总结

通过上面一个完整示例, 我们发现,d3的核心并不在于绘制图形,这些都是dom操作,而是数据的处理,数据驱动dom,到这里是不是跟现代mvvm又挂上钩了,并且d3是基于dom的, 我们完全可以把d3当做一个算法库,处理数据,至于图像的绘制完全可以交由react等框架,这是canvas类库所做不到的。用上virtual dom性能可能还会更高一点。dom操作是昂贵的,virtual dom跟d3搭配味道可能更佳。如果把d3作为一个算法库我们还缺少最佳实践。还需要学习。

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

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

相关文章

  • d3js中级教程漂浮的卡牌效果(data的key函数进阶)

    摘要:效果如图所示链接地址源码链接地址其实这个例子也很简单用到的也只是的基础部分我们要把一定间隔请求过来的数据进行排序并生成元素同时重用原来和新数据相对应的元素删除不对应的说的有点绕表达能力不行其实说到这明白人都能看出来了这要用到选择器的以及方法 showImg(http://segmentfault.com/img/bVbZY5); 效果如图所示,demo链接地址源码链接地址 其实这个例...

    zhoutao 评论0 收藏0
  • d3js中级教程漂浮的卡牌效果(data的key函数进阶)

    摘要:效果如图所示链接地址源码链接地址其实这个例子也很简单用到的也只是的基础部分我们要把一定间隔请求过来的数据进行排序并生成元素同时重用原来和新数据相对应的元素删除不对应的说的有点绕表达能力不行其实说到这明白人都能看出来了这要用到选择器的以及方法 showImg(http://segmentfault.com/img/bVbZY5); 效果如图所示,demo链接地址源码链接地址 其实这个例...

    Lin_R 评论0 收藏0
  • 力导向算法从入门到放弃!

    摘要:通过力导向算法计算位置,绘制出对应的力导向图,这样的分配是最佳位置的分布图。力导向算法是根据自然界中电子直接互相作用的原理来实现的,自然界中。 前言 说到力导向可能很多小伙伴都只是会使用,不知道其中的实现原理,今天,我们一起来自己实现一套力导向算法,然后做一些技术相关的延伸。发散下思维。 什么是力导向算法? 根据百科的介绍:力导向算法是指通过对每个节点的计算,算出引力和排斥力综合的合力...

    levy9527 评论0 收藏0
  • web入门+书籍推荐

    摘要:这里我也给大家推荐一些数据可视化的图形库不过这些图形库一般需要一些的基础知识输入数据即可生成图形,自由发挥程度较低百度出品,与很像,个人觉得略丑推荐,但不适合新手,图表漂亮,灵活性高以上都可以先去阅读官方文档未完待续 如果你想建立一个自己的网站,你可以从网上搜到许多的教程:比如 wordpress gitpages 等等。 如果你想了解这个框架是怎么工作的,你可以了解以下下面的三个...

    suosuopuo 评论0 收藏0

发表评论

0条评论

guqiu

|高级讲师

TA的文章

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