Zhihao's Studio.

为SVG中大小可变的元素加上边框和背景色

Word count: 760 / Reading time: 3 min
2017/10/11 Share

问题的出现

我在使用d3js开发项目过程中,遇到了这样的需求,将鼠标hover到某些元素上时,将该元素的detail信息逐条展示出来,而且为了将detail框与其他元素区别开来,需要给detail展示区域添加边框或增加底色,想要的效果大概是下面的图那样的。



预期效果图

问题解决思路

普通的HTML元素有很多方法来获得其宽高等其他位置属性的方法。如果是一组元素,可以用div标签将他们组合起来,指定div的border颜色和宽度,div的边框会根据元素的宽高动态改变大小。

SVG中,将元素成组的标签是g元素,之前使用它是将元素进行分组,然后统一进行平移和旋转之类的功能变换。毫无意外的,普通DOM元素获取宽高和位置属性的方法offsetWidth、ClientWidth全部失效,返回值均为undefined。我的初步想法是获得这些元素,然后绘制矩形,这样就有了类似div的边框效果。

在Stack Overflow中,找到了问题的答案,有两个原生的JS方法可以作为候选。

getBBox

document.getElementById(“g1”).getBBox()的返回结果如下:



getBBox返回值

getBoundingClientRect()

而document.getElementById(“g1”).getBoundingClientRect()的返回结果如下:



getBoundingClientRect返回值

区别

发现两个方法的返回结果是不一样的,主要是因为getBBox()获得的是元素在当前SVG坐标中的数据,而getBoundingClientRect()则是浏览器的坐标。
显然,getBBox()方法更贴合我们的需求。

添加背景色

getBBox方法可以让我们知道矩形该如何绘制,如果让矩形fill none的话,是可以看到底层的文字的,但设计师比较执拗,死磕我需要背景色。而正确获取矩形位置大小是需要先将detail信息绘制到svg画板上的,因此后面再绘制矩形的时候,矩形就盖住了底层的detail信息文字。

对于这个先有鸡还是先有蛋的问题,我采取了一个比较tricky的方法,即在绘制完矩形之后,再绘制一遍deatil信息,这样后绘制的detail信息就处在了矩形的上层,从而可以被用户看到,而底层的detail信息实际上是存在的,只不过是被矩形这遮挡住了。

给设计师看过效果之后,他露出了满意的微笑☺。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function drawDeatilBorder(data){
vis.selectAll('.detailBorder').remove();
var position = document.getElementById("detailInfoBorder").getBBox();
vis.append('rect').attr('class','detailBorder').style('fill','#f0f0f0').attr('x',position.x-20).attr('y',position.y-20).attr('width',position.width+40)
.attr('height',position.height+40).attr('fill','red').attr('rx','3px').attr('ry','3px').attr('stroke','none');
var i = 0;
for(var key in data) {
if (typeof(data[key]) !== "object") {
i++;
vis.append("text").attr('class', 'detailInfo').attr("x", function () {
return 850
}).attr("y", function () {
return i * 20 + 30;
}).attr("dy", ".35em").attr("text-anchor", function () {
return "start";
}).text(function (d) {
return key + " : " + data[key];
})
.style("fill-opacity", 1);
}
}
}

项目开源地址,供参考。

CATALOG
  1. 1. 问题的出现
  2. 2. 问题解决思路
    1. 2.1. getBBox
    2. 2.2. getBoundingClientRect()
    3. 2.3. 区别
  3. 3. 添加背景色