使用 D3 向静态 SVG 动态添加工具提示 [英] Dynamically Adding a tooltip to static SVG Using D3

查看:34
本文介绍了使用 D3 向静态 SVG 动态添加工具提示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个静态 SVG 图像,我们正在尝试动态添加工具提示,例如在 AngularJS 应用程序上下文中使用 d3.js 在 SVG 图像中的对象上的悬停事件.

SVG 图像是一个平面图并且相当复杂,但是我们在 POC 过程中的起步非常小.这是一个部分的一小部分代表性片段:

<g><rect x="75.2" y="92.4" pointer-events="visible" fill="none"宽度="64.7" 高度="57.8"/><polyline fill="none" stroke="#CDDDED" stroke-width="0.5"stroke-miterlimit="10" points="118.4,149.9 140.3,149.9 140.3,92.475.2,92.4 75.2,128.7"/><g><text transform="matrix(1 0 0 1 87.8719 144.8836)" fill="#010101"font-family="arial, sans-serif" font-size="13.4182">362.12</text>

D3.js 对我们来说是新的,但是从我们的研究来看,它似乎能够做我们需要它做的事情,因为它似乎被设计为与 SVG 一起工作并在 SVG 中表示数据,但是所有的我们发现的示例是动态创建 SVG(主要是图表)但不操作现有 SVG 图像.

简而言之,我们需要做的是:

  1. 查找 id 以f3"开头的 g 标签,如上面的 g id="f3s362c12"
  2. 对于与 g 标签关联的每个矩形,添加工具提示悬停事件(可能是标签?)
  3. 当用户将鼠标悬停在说 g id="f3s362c12" 上时,选择矩形并获取 f3s362c12 的相应数据记录,这是我从 .csv 文件 IE 加载的:

<块引用>

({"floor":"3","location":"f3s362c12","name":"DavidByrne","occ":"歌手","img":"img/davidAvatar.jpg\r"})

  1. 将此信息添加到工具提示/标签中,这样当您将鼠标悬停在 g id=f3s362c12 上时,您会看到 David Byrne 的工具提示,他的职业是歌手和他的头像.

我创建了一个 Plunk :

  1. 在 HTML 中加载 SVG
  2. 加载 .csv 文件.

我们遇到的问题是 d3.js.例如,在我们的 Plunk 中,在 script.js 中,我们做这样的事情找到我们的 g 标签:

 var svg = d3.select("#svgFP");var allG = svg.selectAll("g").each(function (d,i) {}

然而,正是在这一点上,我们遇到了困难,因为我们正试图使用​​this"关键字在 allG 上找到一个矩形.

 if (this.id.indexOf("f3") > -1){//1.添加标签/div/悬停//2.从数组对象中查找对应的记录.//3.将各自的名称、职业和图像与鼠标悬停/鼠标移出事件一起注入标签/div.}

我们一直在使用 Firebug 来尝试寻找要使用的属性,但至少可以说这是非常令人沮丧和徒劳的,所以我们认为 SO 中可能有一两个 d3/angular 大师可能能够为我们指明道路.

提前致谢.

解决方案

你的 plunker 遇到的第一个问题是你的 CSV:CSV 的第一行必须是 header,它定义了什么每列是.因此,我将您的 CSV 修改为:

楼层、位置、姓名、姓氏、角色、图像3,f3s362c12,David,Byrne,Singer,img/davidAvatar.jpg3,f3s362c11,Tina,Weymouth,Bassist,img/tinaAvatar.jpg3,f3s362c2,Jerry,Harrison,Keyboards,img/jerryAvatar.jpg3,f3s362c1,Chris,Frantz,Drums,img/chrisAvatar.jpg

现在是你的实际问题.

D3 代码的最佳解决方案是将数据绑定到 DOM 元素(在本例中为您的 SVG).但是,由于您已经有一个静态 SVG,不是用 D3 制作的,也没有任何绑定数据,这里有一个解决方案.

首先,选择所有相关的组元素:

var groups = d3.selectAll("g[id^='f3']");

然后将 mouseover 代码添加到该选择中.

mouseover 代码中是这个解决方案最重要的部分:我们得到光标所在元素的 ID...

var groupId = this.id

... 然后,根据这个 ID,我们过滤之前加载的 CSV:

var thisData = data.filter(function(d) {返回 d.location === groupId});

这就是上面代码所做的:我们将 CSV 加载到名为 data 的变量中.该变量是一个对象数组,每个对象都有一个 location 属性(查看我创建的 CSV 标头).然后,我们将每个对象的location(d.location)与组的ID(groupId)进行比较.

现在,您有一个名为 thisData 的变量,可用于填充工具提示.

这是一个带有 MCVE 版本代码的演示:

var tooltip = d3.select("body").append("div").style("位置", "绝对").style("背景色", "gainsboro").style("边框", "1px 纯黑色").style("padding", "20px").style("指针事件", "无").style("字体大小", "12px");var data = d3.csvParse(d3.select("#csv").text());var groups = d3.selectAll("g[id^='f3']");group.on("mousemove", function() {var groupId = this.idvar thisData = data.filter(function(d) {返回 d.location === groupId});tooltip.html("姓名:" + thisData[0].name + " " + thisData[0].surname + "
角色:" + thisData[0].role).style("top", (d3.event.pageY - 2) + "px").style("left", (d3.event.pageX + 2) + "px").style("可见性", "可见性");}).on("mouseout", function() {tooltip.style("可见性", "隐藏");});

pre {显示:无;}

<script src="https://d3js.org/d3.v4.min.js"></script><svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" height="100%"><g id="背景"><rect x="1.5" y="0.3" fill="#5A8CC9" width="298.7" height="300.4"/><g id="f3s362c12"><g><rect x="75.2" y="92.4" pointer-events="visible" fill="none" width="64.7" height="57.8"/><polyline fill="none" stroke="#CDDDED" stroke-width="0.5" stroke-miterlimit="10" points="118.4,149.9 140.3,149.9 140.3,92.475.2,92.4 75.2,128.7"/><g><text transform="matrix(1 0 0 1 87.8719 144.8836)" fill="#010101" font-family="arial, sans-serif" font-size="13.4182">362.12</text><g id="f3s362c11"><g><rect x="75.2" y="149.9" pointer-events="visible" fill="none" width="64.7" height="57.8"/><polyline fill="none" stroke="#CDDDED" stroke-width="0.5" stroke-miterlimit="10" points="118.4,207.8 140.3,207.8 140.3,149.975.2,149.9 75.2,186.2"/><g><text transform="matrix(1 0 0 1 87.8719 201.6532)" fill="#010101" font-family="arial, sans-serif" font-size="13.4182">362.11</text><g id="f3s362c2"><g><rect x="140.3" y="149.9" pointer-events="visible" fill="none" width="68.8" height="57.8"/><polyline fill="none" stroke="#CDDDED" stroke-width="0.5" stroke-miterlimit="10" points="208.7,183.5 208.7,149.9 140.3,149.9140.3,207.8 185.8,207.8"/><g><text transform="matrix(1 0 0 1 163.782 201.6532)" fill="#010101" font-family="arial, sans-serif" font-size="13.4182">362.2</text><g id="f3s362c1"><g><rect x="140.3" y="92.4" pointer-events="visible" fill="none" width="68.8" height="57.8"/><polyline fill="none" stroke="#CDDDED" stroke-width="0.5" stroke-miterlimit="10" points="208.7,126 208.7,92.4 140.3,92.4140.3,149.9 185.8,149.9"/><g><text transform="matrix(1 0 0 1 163.782 144.8836)" fill="#010101" font-family="arial, sans-serif" font-size="13.4182">362.1</text></svg><pre id="csv">楼层、位置、姓名、姓氏、角色、图像3,f3s362c12,David,Byrne,Singer,img/davidAvatar.jpg3,f3s362c11,Tina,Weymouth,Bassist,img/tinaAvatar.jpg3,f3s362c2,Jerry,Harrison,Keyboards,img/jerryAvatar.jpg3,f3s362c1,Chris,Frantz,Drums,img/chrisAvatar.jpg</pre>

总结一下,这些是步骤:

  1. 创建一个选择以获取鼠标事件;
  2. 加载并解析您的 CSV;
  3. 当用户将鼠标悬停在 上时,根据该 的 ID 过滤您的数据数组;
  4. 使用过滤后的数组创建工具提示的 html.

PS:在此代码段中,我使用 <pre> 元素来存储您的 CSV.我这样做只是因为,与 Plunker 不同,我无法使用 S.O.加载 CSV.片段.

We have a static SVG image that we are trying to dynamically add tooltips to say, a hover event on a object within the SVG image using d3.js in the context of an AngularJS application.

The SVG image is a floorplan and is fairly complex, however we've started very small in the POC process. Here is a small representative snippet of one section:

<g id="f3s362c12">
  <g>
    <rect x="75.2" y="92.4" pointer-events="visible" fill="none" 
    width="64.7" height="57.8" />
    <polyline fill="none" stroke="#CDDDED" stroke-width="0.5" 
     stroke-miterlimit="10" points="118.4,149.9 140.3,149.9 140.3,92.4
     75.2,92.4 75.2,128.7" />
  </g>
  <g>
    <text transform="matrix(1 0 0 1 87.8719 144.8836)" fill="#010101" 
     font-family="arial, sans-serif" font-size="13.4182">362.12</text>
  </g>
</g>

D3.js is new to us, however from our research, it seems to be capable of doing what we need it to do since it seems to be designed to work with SVG and represent data in the SVG, however all of the examples we have found are of dynamically creating SVG (mainly charts) but not manipulating existing SVG images.

In a nutshell what we need to do is:

  1. Find g tags that have id's starting with "f3" as in g id="f3s362c12" above
  2. For each rect associated with the g tag, add a tooltip hover event ( possibly a label?)
  3. When a user hovers over say g id="f3s362c12" select the rect and grab the corresponding data record for f3s362c12, that I have loaded from a .csv file, IE:

({"floor":"3","location":"f3s362c12","name":"David Byrne","occ":"Singer","img":"img/davidAvatar.jpg\r"})

  1. Add this info to the tooltip/label so when you hover over g id=f3s362c12, you see a tooltip with David Byrne, whose occupation is singer with his avatar image.

I've created a Plunk that:

  1. Loads the SVG in the HTML
  2. Loads the .csv file.

The problem we're having is with the d3.js. For example, in our Plunk, in script.js, we do something like this to find our g tags:

 var svg = d3.select("#svgFP");
 var allG = svg.selectAll("g").each(function (d,i) {}

However, it's at this point that we hit the wall, since we are trying to find a rect on allG using "this" keyword.

  if (this.id.indexOf("f3") > -1)
    {
        //1. Add label/div/hover
        //2. Find corresponding record from array object.
        //3. Inject respective name, occupation and image into label/div along with mouseover/mouseout event.
    }

We've been using Firebug to try to find properties to use, but it's been quite frustrating and fruitless to say the least, so we thought that there might be one or two d3/angular guru's in SO that might be able to show us the way.

Thanks in advance.

解决方案

The first problem you have in your plunker is your CSV: The first row a CSV has to be the header, which defines what each column is. Thus, I modified your CSV to this:

floor,location,name,surname,role,image
3,f3s362c12,David ,Byrne,Singer,img/davidAvatar.jpg
3,f3s362c11,Tina,Weymouth,Bassist,img/tinaAvatar.jpg
3,f3s362c2,Jerry,Harrison,Keyboards,img/jerryAvatar.jpg
3,f3s362c1,Chris,Frantz,Drums,img/chrisAvatar.jpg

Now comes your actual question.

The best solution for a D3 code would be binding the data to the DOM elements (in this case, your SVG). But, since you already have a static SVG, not made with D3 and not having any bound data, here is a solution.

First, select all relevant group elements:

var groups = d3.selectAll("g[id^='f3']");

And add the mouseover code to that selection.

In the mouseover code comes the most important part of this solution: we get the ID of the element which the cursor is over...

var groupId = this.id

... and then, based on this ID, we filter the previously loaded CSV:

var thisData = data.filter(function(d) {
    return d.location === groupId
});

This is what the above code does: we loaded the CSV in a variable named data. That variable is an array of objects, each object having a location property (look at the CSV header I created). Then, we compare the location of each object (d.location) with the ID of the group (groupId).

Now, you have a variable named thisData, which you can use to populate your tooltip.

Here is a demo with a MCVE version of your code:

var tooltip = d3.select("body")
            .append("div")
            .style("position", "absolute")
            .style("background-color", "gainsboro")
            .style("border", "1px solid black")
            .style("padding", "20px")
            .style("pointer-events", "none")
            .style("font-size", "12px");

        var data = d3.csvParse(d3.select("#csv").text());

        var groups = d3.selectAll("g[id^='f3']");

        groups.on("mousemove", function() {
            var groupId = this.id
            var thisData = data.filter(function(d) {
                return d.location === groupId
            });
            tooltip.html("Name: " + thisData[0].name + " " + thisData[0].surname + "<br>Role: " + thisData[0].role)
                .style("top", (d3.event.pageY - 2) + "px").style("left", (d3.event.pageX + 2) + "px")
                .style("visibility", "visible");
        }).on("mouseout", function() {
            tooltip.style("visibility", "hidden");
        });

pre {
	display: none;
}

<script src="https://d3js.org/d3.v4.min.js"></script>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 300" height="100%">
    <g id="background">
        <rect x="1.5" y="0.3" fill="#5A8CC9" width="298.7" height="300.4" />
    </g>
    <g id="f3s362c12">
        <g>
            <rect x="75.2" y="92.4" pointer-events="visible" fill="none" width="64.7" height="57.8" />
            <polyline fill="none" stroke="#CDDDED" stroke-width="0.5" stroke-miterlimit="10" points="118.4,149.9 140.3,149.9 140.3,92.4
                75.2,92.4 75.2,128.7        " />
        </g>
        <g>
            <text transform="matrix(1 0 0 1 87.8719 144.8836)" fill="#010101" font-family="arial, sans-serif" font-size="13.4182">362.12</text>
        </g>
    </g>
    <g id="f3s362c11">
        <g>
            <rect x="75.2" y="149.9" pointer-events="visible" fill="none" width="64.7" height="57.8" />
            <polyline fill="none" stroke="#CDDDED" stroke-width="0.5" stroke-miterlimit="10" points="118.4,207.8 140.3,207.8 140.3,149.9
                75.2,149.9 75.2,186.2       " />
        </g>
        <g>
            <text transform="matrix(1 0 0 1 87.8719 201.6532)" fill="#010101" font-family="arial, sans-serif" font-size="13.4182">362.11</text>
        </g>
    </g>
    <g id="f3s362c2">
        <g>
            <rect x="140.3" y="149.9" pointer-events="visible" fill="none" width="68.8" height="57.8" />
            <polyline fill="none" stroke="#CDDDED" stroke-width="0.5" stroke-miterlimit="10" points="208.7,183.5 208.7,149.9 140.3,149.9
                140.3,207.8 185.8,207.8         " />
        </g>
        <g>
            <text transform="matrix(1 0 0 1 163.782 201.6532)" fill="#010101" font-family="arial, sans-serif" font-size="13.4182">362.2</text>
        </g>
    </g>
    <g id="f3s362c1">
        <g>
            <rect x="140.3" y="92.4" pointer-events="visible" fill="none" width="68.8" height="57.8" />
            <polyline fill="none" stroke="#CDDDED" stroke-width="0.5" stroke-miterlimit="10" points="208.7,126 208.7,92.4 140.3,92.4
                140.3,149.9 185.8,149.9         " />
        </g>
        <g>
            <text transform="matrix(1 0 0 1 163.782 144.8836)" fill="#010101" font-family="arial, sans-serif" font-size="13.4182">362.1</text>
        </g>
    </g>
</svg>
<pre id="csv">floor,location,name,surname,role,image
3,f3s362c12,David ,Byrne,Singer,img/davidAvatar.jpg
3,f3s362c11,Tina,Weymouth,Bassist,img/tinaAvatar.jpg
3,f3s362c2,Jerry,Harrison,Keyboards,img/jerryAvatar.jpg
3,f3s362c1,Chris,Frantz,Drums,img/chrisAvatar.jpg</pre>

Summarising, these are the steps:

  1. Create a selection to get your mouse events;
  2. Load and parse your CSV;
  3. When the user hover over a <g>, filter your data array according to that <g>'s ID;
  4. Use the filtered array to create your tooltip's html.

PS: in this snippet I'm using a <pre> element to store your CSV. I'm only doing this because, unlike a Plunker, I cannot load a CSV using the S.O. snippet.

这篇关于使用 D3 向静态 SVG 动态添加工具提示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆