猿问

如何用D3.js创建一个圆组一个组织圆图像并用曲线连接?

我正在尝试使用 d3.js 创建一个径向(圆形)分组圆,例如:

我写了一些如下代码。


但我不知道如何将每个圆圈与曲线连接起来,当鼠标悬停圆圈时,上面的圆圈会显示一个工具提示,怎么做?帮助将不胜感激。谢谢。


我更新了我的代码,现在我可以在一个大圆圈中绘制圆圈或图像元素。


    const mockedData = {

    "nodes": [

      {

        "name": "Node1one",

        "label": "Node1",

        "id": 1,

        "x": 120,

        "y": 120,

      },

      {

        "name": "Node2",

        "label": "Node2",

        "id": 2,

        "x": 350,

        "y": 180,

      },

    ]

  }


  const imgList = {

    "images": [

      {

        "image": 'https://via.placeholder.com/30x30',

        "x": -50,

      },

      {

        "image": 'https://via.placeholder.com/30',

        "x": 20

      }

    ]

  }



const svg = d3.select("svg");

const width = +svg.attr("width");

const height = +svg.attr("height");


let { links, nodes } = mockedData;


let { images } = imgList;


const ticked = ( node) => {

  node.attr("transform", 

  function (d) {return "translate(" + d.x + ", " + d.y + ")";});

}


const tickedImg = (nodeImg) => {

  nodeImg.attr("x", function (d) {return  d.x })

}


const node = svg.selectAll(".node")

        .data(nodes)

        .enter()

        .append("g")

        .attr("class", "node")


    node.append('circle').attr("r", 86); //radius

    svg.selectAll('circle')

    .on('click', function () { // arrow function will produce this = undefined

       d3.selectAll('circle')

       .style("fill", "lightgray");

       d3.select(this)

        .style("fill", "aliceblue");

     })

     .on('mouseover', function () {

        d3.selectAll('circle')

        .style("stroke", "black");

        

        d3.select(this)

        .style("stroke", "green");

    })

    ticked( node )





const nodeText = node.append("text")

.attr("y", -70);


      nodeText.selectAll("tspan.text")

      .data((d) =>  d.name.split(" "))

      .enter()

      .append("tspan")

      .attr("class", "text")

      .text(d => d)

      .attr("x", -30)

      .attr("y", -60)


      node.append("title")

          .text(function (d) {return d.id;});

跃然一笑
浏览 109回答 1
1回答

慕妹3242003

好的,如果我们只想在图像之间绘制弯曲的弧线,我当然可以提供帮助:)我在这里(非常详细地)回答了一个类似的问题:https ://stackoverflow.com/a/59784798/9792594 - 这应该有助于解释数学等。我将只使用该答案中的最终功能,如果您需要有关其工作原理的背景知识,请查看该答案。注意:您可以通过更改“绘制顺序”来更改 svg 元素的“z 顺序”,即在 DOM 中呈现它们的顺序。所以在d3中,使用.append(),这只是意味着首先在底层调用你想要的代码,然后在下一层调用你想要的代码,依此类推。const mockedData = {&nbsp; &nbsp; "nodes": [&nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; "name": "Node1",&nbsp; &nbsp; &nbsp; &nbsp; "label": "Node1",&nbsp; &nbsp; &nbsp; &nbsp; "id": 1,&nbsp; &nbsp; &nbsp; &nbsp; "x": 120,&nbsp; &nbsp; &nbsp; &nbsp; "y": 120,&nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; "name": "Node2",&nbsp; &nbsp; &nbsp; &nbsp; "label": "Node2",&nbsp; &nbsp; &nbsp; &nbsp; "id": 2,&nbsp; &nbsp; &nbsp; &nbsp; "x": 350,&nbsp; &nbsp; &nbsp; &nbsp; "y": 180,&nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; ]&nbsp; }&nbsp; const imgList = {&nbsp; &nbsp; "images": [&nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; "image": 'https://via.placeholder.com/30x30',&nbsp; &nbsp; &nbsp; &nbsp; "width": 30,&nbsp; &nbsp; &nbsp; &nbsp; "height": 30,&nbsp; &nbsp; &nbsp; &nbsp; "x": -50,&nbsp; &nbsp; &nbsp; &nbsp; "y": -20&nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; "image": 'https://via.placeholder.com/30',&nbsp; &nbsp; &nbsp; &nbsp; "width": 30,&nbsp; &nbsp; &nbsp; &nbsp; "height": 30,&nbsp; &nbsp; &nbsp; &nbsp; "x": 20,&nbsp; &nbsp; &nbsp; &nbsp; "y": -20&nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; "image": 'https://via.placeholder.com/30',&nbsp; &nbsp; &nbsp; &nbsp; "width": 30,&nbsp; &nbsp; &nbsp; &nbsp; "height": 30,&nbsp; &nbsp; &nbsp; &nbsp; "x": -15,&nbsp; &nbsp; &nbsp; &nbsp; "y": 20&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; ]&nbsp; }const svg = d3.select("svg");const width = +svg.attr("width");const height = +svg.attr("height");let { links, nodes } = mockedData;let { images } = imgList;const ticked = ( node) => {&nbsp; node.attr("transform",&nbsp;&nbsp; function (d) {return "translate(" + d.x + ", " + d.y + ")";});}const tickedImg = (nodeImg) => {&nbsp; nodeImg.attr("x", function (d) {return&nbsp; d.x })}const node = svg.selectAll(".node")&nbsp; &nbsp; &nbsp; &nbsp; .data(nodes);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; node&nbsp; &nbsp; &nbsp; &nbsp; .enter()&nbsp; &nbsp; &nbsp; &nbsp; .append("path")&nbsp; &nbsp; &nbsp; &nbsp; .attr("d", (d,i) => {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //console.log(d,i,images)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (nodes.length > 1){&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const j = i == (nodes.length - 1) ? 0 : i + 1;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const invertArc = (i+1) < (nodes.length/2);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const gtr2 = nodes.length > 2 ? invertArc : !invertArc;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //console.log(i,invertArc)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return pointsToPath(d, nodes[j], gtr2);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return "";&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; .attr("stroke", "black")&nbsp; &nbsp; &nbsp; &nbsp; .attr("stroke-dasharray", "4")&nbsp; &nbsp; &nbsp; &nbsp; .attr("fill", "transparent");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;const nodeGroup = node&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; .enter()&nbsp; &nbsp; &nbsp; &nbsp; .append("g")&nbsp; &nbsp; &nbsp; &nbsp; .attr("class", "node");&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;const circle = nodeGroup.append('circle').attr("r", 86); //radiuscircle&nbsp; &nbsp; .style("fill", "darkgray")&nbsp; &nbsp; .on('click', function () { // arrow function will produce this = undefined&nbsp; &nbsp; &nbsp; &nbsp;d3.selectAll('circle')&nbsp; &nbsp; &nbsp; &nbsp;.style("fill", "darkgray");&nbsp; &nbsp; &nbsp; &nbsp;d3.select(this)&nbsp; &nbsp; &nbsp; &nbsp; .style("fill", "aliceblue");&nbsp; &nbsp; &nbsp;})&nbsp; &nbsp; &nbsp;.on('mouseover', function () {&nbsp; &nbsp; &nbsp; &nbsp; d3.selectAll('circle')&nbsp; &nbsp; &nbsp; &nbsp; .style("stroke", "black");&nbsp; &nbsp; &nbsp; &nbsp; d3.select(this)&nbsp; &nbsp; &nbsp; &nbsp; .style("stroke", "green");&nbsp; &nbsp; })&nbsp; &nbsp; ticked( nodeGroup );const nodeText = nodeGroup.append("text")&nbsp; &nbsp; &nbsp; .attr("y", -70);nodeText.selectAll("tspan.text")&nbsp; &nbsp; &nbsp; .data((d) =>&nbsp; d.name.split(" "))&nbsp; &nbsp; &nbsp; .enter()&nbsp; &nbsp; &nbsp; .append("tspan")&nbsp; &nbsp; &nbsp; .attr("class", "text")&nbsp; &nbsp; &nbsp; .text(d => d)&nbsp; &nbsp; &nbsp; .attr("fill", "black")&nbsp; &nbsp; &nbsp; .attr("x", -30)&nbsp; &nbsp; &nbsp; .attr("y", -60)nodeGroup.append("title")&nbsp; &nbsp; &nbsp; .text(function (d) {return d.id;});const nodeImg = nodeGroup.selectAll("image")&nbsp; &nbsp; &nbsp; .data(images);nodeImg&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; .enter()&nbsp; &nbsp; &nbsp; .append("image")&nbsp; &nbsp; &nbsp; .attr("xlink:href", d => d.image)&nbsp; &nbsp; &nbsp; .attr("width", d => d.width)&nbsp; &nbsp; &nbsp; .attr("height", d => d.height)&nbsp; &nbsp; &nbsp; .attr("x", d => d.x)&nbsp; &nbsp; &nbsp; .attr("y", d => d.y)&nbsp; &nbsp; &nbsp; //tickedImg (nodeImg)&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp;nodeImg&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; .enter()&nbsp; &nbsp; &nbsp; .append("path")&nbsp; &nbsp; &nbsp; .attr("d", (d,i) => {&nbsp; &nbsp; &nbsp; &nbsp; //console.log(d,i,images)&nbsp; &nbsp; &nbsp; &nbsp; if (images.length > 1){&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const j = i == (images.length - 1) ? 0 : i + 1;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; const invertArc = (i+1) < (images.length/2);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //console.log(i,invertArc)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return pointsToPath(d, images[j], invertArc);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return "";&nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; .attr("stroke", "black")&nbsp; &nbsp; &nbsp; .attr("stroke-dasharray", "4")&nbsp; &nbsp; &nbsp; .attr("fill", "transparent")&nbsp; &nbsp; &nbsp; .attr("transform", d => "translate(" + d.width/2 + ", " + d.height/2 + ")");&nbsp; &nbsp; &nbsp; //tickedImg (nodeImg)&nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &nbsp;&nbsp;function pointsToPath(from, to, invertArc) {&nbsp; const centerPoint = [ (from.x + to.x) / 2, (from.y + to.y) / 2];&nbsp; const slope = (to.y - from.y) / (to.x - from.x);&nbsp; const invSlope = -1 / slope;&nbsp; const distance = Math.sqrt( Math.pow((to.x - from.x), 2) + Math.pow((to.y - from.y), 2) );&nbsp; const offset = (invertArc ? -1 : 1) * 2 * Math.sqrt(distance);&nbsp; const angle = Math.atan(slope);&nbsp; const offsetY = Math.cos(angle)*offset;&nbsp; const offsetX = Math.sin(angle)*offset;&nbsp; const offsetCenter = [centerPoint[0] - offsetX, centerPoint[1] + offsetY];&nbsp; const arcPointX = offsetCenter[0]&nbsp; const arcPointY = offsetCenter[1]&nbsp; &nbsp;&nbsp; return 'M' + from.x + ' ' + from.y + 'Q' + arcPointX + ' ' + arcPointY +&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ' ' + to.x + ' ' + to.y;&nbsp;}<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script><svg width="100%" viewbox="0 0 1000 500">&nbsp; <path d="M20 20 50 50" fill="transparent" stroke-width="5" stroke="black"></path></svg><script>//the following d3 code would insert the same path as that manually inserted in the HTML above:d3.select("svg")&nbsp; .append("path")&nbsp; //we can set it directly as below, or via a function:&nbsp; .attr("d", "M20 20 50 50")&nbsp; .attr("fill", "transparent")&nbsp; .attr("stroke-width", "5")&nbsp; .attr("stroke", "black");</script>输出(20 年 6 月 18 日更新):
随时随地看视频慕课网APP

相关分类

JavaScript
我要回答