javascript - 无法在 D3 力定向图中渲染图像

标签 javascript html d3.js svg

我有一个共享边界的国家的力导向图。我想在每个节点上显示国家国旗。我可以在生成的 HTML 中看到图像,也可以看到它占用了窗口中的空间,但它不可见。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="flags/flags.css">
  <link rel="stylesheet" href="index.css">
  <title>National Contiguity Visualization</title>
</head>
<body>
  <div class="details">National Contiguity</div>
  <svg class="plot"></svg>
  <div class="tooltip hidden"></div>
  <svg width="100" height="100">
      <foreignobject class="node" >

          <img class="flag flag-cz" alt="Czech Republic" src="flags/blank.png"/>                    
      </foreignobject>
  </svg>

  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="index.js"></script>
</body>
</html>

这是 JS 文件。

const apiUrl = 'https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json';
const tooltip = document.getElementsByClassName('tooltip')[0];

const dragstarted = d => {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

const dragged = d => {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

const dragended = d => {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

const plot = (data) => {
  data.nodes = data.nodes.map((d, index) => {
    d['id'] = index;
    return d;
  });
  const margin = {
    top: 20,
    right: 20,
    bottom: 10,
    left: 100
  };
  const width = Math.max((((window.innerWidth / 100) * 80) - margin.right - margin.left), 700);
  const height = ((window.innerHeight / 100) * 80) - margin.bottom - margin.top;
  const svg = d3.select('svg')
    .attr('width', width + margin.left + margin.right + 100)
    .attr('height', height + margin.top + margin.bottom + 100)
    .append('g')
    .attr('transform',
      `translate(${margin.left}, ${margin.top})`);

  const simulation = d3.forceSimulation()
    .force('link', d3.forceLink().id(function (d) { return d.id; }).distance(100).strength(1))
    .force('charge', d3.forceManyBody())
    .force('center', d3.forceCenter(width / 2, height / 2));

  const dragstarted = d => {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  const dragged = d => {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  const dragended = d => {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }

  const ticked = () => {
    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("x", function (d) { return d.x = Math.max(5, Math.min(width - 5, d.x)); })
      .attr("y", function (d) { return d.y = Math.max(5, Math.min(height - 5, d.y)); });
  }

  const link = svg.append('g')
    .attr('class', 'links')
    .selectAll('line')
    .data(data.links)
    .enter().append('line')
    .attr('stroke-width', function (d) { return Math.sqrt(d.value); });

  const node = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('.flag')
    .data(data.nodes)
    .enter()
    .append('foreignobject')
    .append('img')
    .attr('src', 'flags/blank.png')
    .attr('class', d => `flag flag-cz`)
    .attr('width', '5px')
    .attr('height', '5px')
    .attr("x", -8)
    .attr("y", -8)
    .call(d3.drag()
      .on('start', dragstarted)
      .on('drag', dragged)
      .on('end', dragended));

  node.append("title")
    .text(function (d) { return d.country; })
    .exit();

  simulation
    .nodes(data.nodes)
    .on("tick", ticked);

  simulation.force("link")
    .links(data.links);

}

const fetchData = () => {
  return fetch(apiUrl)
    .then(response => {
      return response.json();
    });
};

const fetchAndPlot = async () => {
  try {
    const response = await fetchData();
    console.log(response);
    plot(response);
  } catch (e) {
    console.error(e);
  }
}

fetchAndPlot();

尝试了 svg 图像元素和 foreignobject 方法。直接用 HTML 编写的可以正确呈现。在过去的 3/4 天内无法找出与此相关的问题。

repo :National Contiguity Visualization

提前致谢。

最佳答案

  • 您需要在 foreignObject 标签上设置宽度和高度属性
  • foreignObject 拼写为大写 O,虽然 HTML 标记不区分大小写,但元素的 DOM 创建以及 d3 是区分大小写的。
  • d3 希望 HTML 标签以 xhtml 为前缀:

由于您没有提供标志,我使用了一个示例标志。请注意,目前您的所有节点都一个接一个地放置,但我想如果您无法解决这个问题,那将是另一个问题。

const apiUrl = 'https://raw.githubusercontent.com/DealPete/forceDirected/master/countries.json';
const tooltip = document.getElementsByClassName('tooltip')[0];

const dragstarted = d => {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

const dragged = d => {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

const dragended = d => {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

const plot = (data) => {
  data.nodes = data.nodes.map((d, index) => {
    d['id'] = index;
    return d;
  });
  const margin = {
    top: 20,
    right: 20,
    bottom: 10,
    left: 100
  };
  const width = Math.max((((window.innerWidth / 100) * 80) - margin.right - margin.left), 700);
  const height = ((window.innerHeight / 100) * 80) - margin.bottom - margin.top;
  const svg = d3.select('svg')
    .attr('width', width + margin.left + margin.right + 100)
    .attr('height', height + margin.top + margin.bottom + 100)
    .append('g')
    .attr('transform',
      `translate(${margin.left}, ${margin.top})`);

  const simulation = d3.forceSimulation()
    .force('link', d3.forceLink().id(function (d) { return d.id; }).distance(100).strength(1))
    .force('charge', d3.forceManyBody())
    .force('center', d3.forceCenter(width / 2, height / 2));

  const dragstarted = d => {
    if (!d3.event.active) simulation.alphaTarget(0.3).restart();
    d.fx = d.x;
    d.fy = d.y;
  }

  const dragged = d => {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
  }

  const dragended = d => {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
  }

  const ticked = () => {
    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("x", function (d) { return d.x = Math.max(5, Math.min(width - 5, d.x)); })
      .attr("y", function (d) { return d.y = Math.max(5, Math.min(height - 5, d.y)); });
  }

  const link = svg.append('g')
    .attr('class', 'links')
    .selectAll('line')
    .data(data.links)
    .enter().append('line')
    .attr('stroke-width', function (d) { return Math.sqrt(d.value); });

  const node = svg.append('g')
    .attr('class', 'nodes')
    .selectAll('.flag')
    .data(data.nodes)
    .enter()
    .append('foreignObject')
    .attr('width', '100')
    .attr('height', '100')
    .append('xhtml:img')
    .attr('src', 'https://flaglane.com/download/british-flag/british-flag-small.gif')
    .attr('class', d => `flag flag-cz`)
    .call(d3.drag()
      .on('start', dragstarted)
      .on('drag', dragged)
      .on('end', dragended));

  node.append("title")
    .text(function (d) { return d.country; })
    .exit();

  simulation
    .nodes(data.nodes)
    .on("tick", ticked);

  simulation.force("link")
    .links(data.links);

}

const fetchData = () => {
  return fetch(apiUrl)
    .then(response => {
      return response.json();
    });
};

const fetchAndPlot = async () => {
  try {
    const response = await fetchData();
    plot(response);
  } catch (e) {
    console.error(e);
  }
}

fetchAndPlot();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="flags/flags.css">
  <link rel="stylesheet" href="index.css">
  <title>National Contiguity Visualization</title>
</head>
<body>
  <div class="details">National Contiguity</div>
  <svg class="plot"></svg>
  <div class="tooltip hidden"></div>

  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="index.js"></script>
</body>
</html>

关于javascript - 无法在 D3 力定向图中渲染图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49474238/

相关文章:

javascript - 如何在reactjs中停止重新渲染?

javascript - 将选定的图标列表值放入输入框中

javascript - 无法将秒格式转换为分钟格式

javascript - 使用下拉菜单加载 iframe 的 src,然后更改

javascript - d3.js 将可拖动网络与力导向图相结合

javascript - 在点击时渲染一个计时器组件

javascript - Jquery - 如何在 html 元素之间来回转换 (fadeIn/fadeOut) 并保持位置?

javascript - HTML/CSS 如何关闭菜单然后使用类 ="active"

javascript - 在 d3 中加载多行数据的默认格式是什么?

d3.js - d3.layout.histogram() 和属性在 v4 中不起作用