陈建华的博客
专注web开发
D3.js 面积(area)图与缩放查看(序5)
2014-09-16 17:41:09   阅读7007次

这一次我们继续完成带缩略图拖拽查看的面积图。我们要完成的是拖拽查看的部分。

面积图强调数量随时间而变化的程度,是非常常用的可视化图表

最终demo  http://www.d3js.cn/demo/brush.html 在开始学习时建议大家先熟悉下图表的交互方式

 

效果图:

brush2[1].png

 

要制作拖拽缩略查看的效果,我们首先要了解d3js的brush对象。可以把这个叫做拖拽刷子

https://github.com/mbostock/d3/wiki/SVG-Controls#wiki-brush

 

# d3.svg.brush()

要使用brush对象的时候,必须先用这个来创建一个brush对象。

# brush.x([scale])

这个方法可以“设置”你所要拖拽的总范围。注意传入的是scale,也就是说类似

 x2 = d3.time.scale().range([0, width])


这样的scale对象,一般就是你用来代表某个坐标轴数据的那个对象。brush.y方法同理。

# brush.extent([values])

在我们使用拖拽刷子时都有个刷到的范围,extend方法可以获得或者设置这个范围。若我们在之前设置了x(),那extend会返回一个[x0,x1]这样的范围。若同时设置了x,y会返回一个二维数组范围。注意extend里面也是个scale,所以可以用domain匹配数据,这样就可以方便我们改变图表了。domain方法

# brush.on(type[, listener])

这个方法主要是来做事件监听

brushstart – 鼠标按下刷子时的事件

brush – 鼠标“刷”的时候,extend变化的事件

brushend – 鼠标抬起的事件

为了方便大家理解,我画了个图,大家对照着看就明白了。

bursh[1].png


ok,有了以上的工具,我们要理清制作的思路:

brush图表的原理是利用上下两个图表,两个图表x方向的数据一样则图表就一样。当刷动图表时,根据brush对象拿到第二个图表刷到的区域范围。

利用这个范围去匹配第一个图表,然后更新第一个图表即可

首先创造brush对象,并绑定要“刷”的那个坐标轴

给下面的图表预留出“刷”的部分。激活区域

给上面的图表预留出“刷”后的结果显示区

监听“刷”的事件,当刷的范围改变时,更新上面的图表。

 

第一步建立brush对象绑定好坐标轴


var x = d3.time.scale().range([0, width]),
    x2 = d3.time.scale().range([0, width]),
    y = d3.scale.linear().range([height, 0]),
    y2 = d3.scale.linear().range([height2, 0]);
 
//建立brush对象
var brush = d3.svg.brush()
    .x(x2)

因为我们是在下面的图表刷的,所以要绑定下面

 

第二步,在下面的图表中预留出刷的部分,并激活


context.append("g")
      .attr("class", "x brush")
      .call(brush)//绑定刷子
    .selectAll("rect")
      .attr("y", -6)
      .attr("height", height2 + 7);
});


这里使用call方法,可以把区域与刷子对象绑定好

第三步,给上面的图表绑定一个刷后结果区


svg.append("defs").append("clipPath")
    .attr("id", "clip")
  .append("rect")
    .attr("width", width)
    .attr("height", height);


我们这里使用了svg的clippath。简单讲解一下(引用自http://www.189works.com/article-68087-1.html)

对象的裁剪路径 – clipPath元素
      裁剪路径使用clipPath元素定义,然后使用clip-path属性引用。
clipPath可以包含path元素,text元素,基本的图形元素(circle等)和use元素。如果是use元素,则它必须是直接引用path,text或者基本图形元素,不能引用的是其他的元素。
注意裁剪路径只是一位的遮罩层,该路径是包含的所有的元素的并集。在这个集合中的对象就可以显示,不在这个范围内的对象就不显示。具体判定点在不在范围内的算法由”clip-rule”属性指定。

对于图形对象,裁剪路径等于自己clip-path设置的裁剪路径与所有外层元素的裁剪路径(包括clip-path和overflow设置的裁剪路径)的并集。注意几点:
1.clipPath元素自身并不会从外层节点继承clipPath定义的裁剪路径。
2.clipPath元素自身可以设置clip-path属性。效果是两个路径的交集。
3.clipPath元素的子元素可以设置clip-path属性:效果是两个路径的并集。
4.空裁剪路径会裁掉元素内所有的内容。
下面看看几种重要的属性:
clipPathUnits = “userSpaceOnUse(默认值) | objectBoundingBox”
      这个属性定义了clipPath元素使用的坐标系统,这两个值我们都很熟悉了,分别是采用引用当前裁剪路径的元素的用户坐标系统和包围盒比例值。
clipPath元素从来不直接渲染,都是通过clip-path被引用,所以设置clipPath元素的display属性没有作用。
clip-path = “<url(#裁剪路径名)> | none inherit”
      这个属性不用多说了,用于引用裁剪路径,这里需要注意的是,所有的容器元素,基本图形元素和clipPath元素都可以使用这个属性。
clip-rule = “nonzero(默认值) | evenodd | inherit”
      这个属性用于确定哪些点是属于裁剪路劲内部的点。对于简单的封闭图形,这个很好判定,但是对于复杂的内部有洞的图形,就有区别了。这个属性的取值与fill-rule的取值含义是一样的:
nonzero:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的处的走向;计算结果从0开始,每有一个交点处的线段是从左到右的,就加1;每有一个交点处的线段是从右到左的,就减1;这样计算完所有交点后,如果这个计算的结果不等于0,则该点在图形内,需要填充;如果该值等于0,则在图形外,不需要填充。看下面的示例:


evenodd:这个值采用的算法是:从需要判定的点向任意方向发射线,然后计算图形与线段交点的个数,个数为奇数则改点在图形内,需要填充;个数为偶数则点在图形外,不需要填充。看下图的示例:


clip-rule属性只能用于clipPath元素的内部图形元素。例如下面的设置是起作用的:


<g>
  <clipPath id="MyClip">
    <path d="..." clip-rule="evenodd" />
  clipPath>
  <rect clip-path="url(#MyClip)" ... />
g>


如果元素不在clipPath中是不起作用的。例如下面的设置是不起作用的:

<g clip-rule="nonzero">
  <clipPath id="MyClip">
    <path d="..." />
  clipPath>
  <rect clip-path="url(#MyClip)" clip-rule="evenodd" ... />
g>

最后看裁剪路径的一个小例子:

<svg width="100px" height="100px">
  <g>
    <clipPath id="MyClip">
      <path d="M 10,10 L 10,20 L 20,20 L 20,10 Z" clip-rule="evenodd" />
    <clipPath>
  <g>
  <rect clip-path="url(#MyClip)" x="10" y="10" width="80" height="80" fill="Red" />
<svg>

矩形只有左上角10*10的区域是可见的。

那么回到我们的图表。我们创建了一个clip-path后,还需要用url来引用他,因此我们修改以前的图表

focus.append("path")
      .datum(data)
      .attr("clip-path", "url(#clip)")//在这里增加一个裁剪层
      .attr("d", area);

第四步,做事件绑定。

var brush = d3.svg.brush()
    .x(x2)
    .on("brush", brushed);//链式操作接到前面创建的brush对象后面即可

当brush的extend范围变动时,触发brushed函数。brushed函数内容如下

function brushed() {
  x.domain(brush.empty() ? x2.domain() : brush.extent());//利用domain方法绑定数据。domain方法的讲解可以参考第一篇教程
//这里是一个三元操作符。当brush.empty(选定为空)时,x与x2的数值范围是一样的,当有brush时,x绑定brush对象刷到的区域所代表的数据范围。
  focus.select("path").attr("d", area); //利用新的数据更新上面图表
  focus.select(".x.axis").call(xAxis);//这里注意,上面图表的数据范围已经变化了,但坐标轴没变化,我们利用call方法来重新绑定一下
}



-----------------------------------------------------
转载请注明来源此处
原地址:#

-----网友评论----
暂无评论
-----发表评论----
微网聚博客乐园 ©2014 blog.mn886.net 鲁ICP备14012923号   网站导航