You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

D3.js缩放后Tooltip位置异常及图像拖拽问题求助

Hey there! Let's tackle your D3.js issues one by one. I'll focus on polishing your D3 v4 implementation (since it's the more modern, supported version) and fix all your core concerns: tooltip positioning after zoom, auto-sizing tooltips, smooth image dragging (with center alignment), and proper tooltip behavior during drag/zoom.


1. Fixing Tooltip Position After Zoom

The root issue here is that your tooltip was using the original d.x/d.y values without accounting for the current zoom and pan transform. In D3 v4, you can grab the active zoom state from the SVG element using d3.zoomTransform(svg.node()), then convert the image's local coordinates to screen coordinates to position the tooltip correctly.

Update your handleMouseOver function like this:

function handleMouseOver(d, i) {
  if(!IS_DRAGGING) {
    // Get current zoom/pan transform
    const transform = d3.zoomTransform(svg.node());
    // Convert image's local position to screen coordinates
    const screenX = transform.applyX(d.x + 32); // Offset right of the 32px image
    const screenY = transform.applyY(d.y - 28); // Offset above the image
    
    div.transition()
      .duration(100)
      .style("opacity", .9);
    div.html(d.url)
      .style("left", `${screenX}px`)
      .style("top", `${screenY}px`);
  }
}

2. Making Tooltip Width Auto-Adjust

Yep, this is just a CSS fix! Remove the fixed width and height from your .tooltip class, and add some horizontal padding for better spacing:

div.tooltip {
  position: absolute;
  text-align: center;
  padding: 2px 8px; /* Add horizontal padding */
  font: 12px sans-serif;
  background: lightsteelblue;
  border: 0px;
  border-radius: 8px;
  pointer-events: none;
  /* Removed fixed width/height */
}

Now the tooltip will automatically shrink or grow to fit its text content.

3. Fixing Smooth Image Dragging & Center Alignment

Your center drag fix (offsetting by 16px) was on the right track, but the "mouse moving faster than image" issue happens because the zoom transform was skewing the drag event's coordinates. We need to reverse the zoom transform to get accurate local SVG coordinates for the mouse position.

Update your drag handlers:

function drag_start(d) {
  d3.event.sourceEvent.stopPropagation(); // Prevent zoom from hijacking the drag
  d3.select(this).raise().classed("active", true);
  div.transition().duration(100).style("opacity", 0); // Hide tooltip during drag
}

function dragging(d) {
  const transform = d3.zoomTransform(svg.node());
  // Convert mouse screen position back to local SVG coordinates
  const invTransform = transform.invert(d3.event.sourceEvent);
  // Update position to keep image centered on mouse
  d.x = invTransform.x - 16;
  d.y = invTransform.y - 16;
  
  d3.select(this).select("image")
    .attr("x", d.x)
    .attr("y", d.y);
  IS_DRAGGING = true;
}

function drag_end(d) {
  d3.select(this).classed("active", false);
  IS_DRAGGING = false;
}

4. Extra: Hide Tooltip During Zoom

To prevent the tooltip from sticking around while zooming, add a line to your zoom handler:

.call(d3.zoom().on("zoom", function () {
  svg.attr("transform", d3.event.transform);
  div.style("opacity", 0); // Hide tooltip when zooming
}))

Full Working D3 v4 Code

Here's the complete, fixed code with all improvements:

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript" src="data.json"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
div.tooltip { 
  position: absolute; 
  text-align: center; 
  padding: 2px 8px;
  font: 12px sans-serif; 
  background: lightsteelblue; 
  border: 0px; 
  border-radius: 8px; 
  pointer-events: none; 
}
</style>
</head>
<body>
<script>
let IS_DRAGGING = false;
const svg = d3.select("body")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.style("background-color", "#E59400") // orange
.call(d3.zoom().on("zoom", function () {
  svg.attr("transform", d3.event.transform);
  div.style("opacity", 0); // Hide tooltip during zoom
}))
.append("g");

function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}

const div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);

function handleMouseOver(d, i) {
if(!IS_DRAGGING) {
  const transform = d3.zoomTransform(svg.node());
  const screenX = transform.applyX(d.x + 32);
  const screenY = transform.applyY(d.y - 28);
  
  div.transition()
    .duration(100)
    .style("opacity", .9);
  div.html(d.url)
    .style("left", `${screenX}px`)
    .style("top", `${screenY}px`);
}
}

function handleMouseOut(d, i) {
div.transition()
  .duration(100)
  .style("opacity", 0);
}

function drag_start(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).raise().classed("active", true);
div.transition().duration(100).style("opacity", 0);
}

function dragging(d) {
const transform = d3.zoomTransform(svg.node());
const invTransform = transform.invert(d3.event.sourceEvent);
d.x = invTransform.x - 16;
d.y = invTransform.y - 16;

d3.select(this).select("image")
  .attr("x", d.x)
  .attr("y", d.y);
IS_DRAGGING = true;
}

function drag_end(d) {
d3.select(this).classed("active", false);
IS_DRAGGING = false;
}

const arr = JSON.parse(data);
arr.forEach(function(d) {
d.x = getRandom(0, document.body.clientWidth);
d.y = getRandom(0, document.body.clientHeight);
})

const group = svg.selectAll('g')
.data(arr)
.enter().append("g")
.call(d3.drag()
  .on("start", drag_start)
  .on("drag", dragging)
  .on("end", drag_end))
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut)

group.append("image")
.attr("xlink:href", function(d) { return d.img_path; })
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", 32)
.attr("height", 32)
</script>
</body>

Key Improvements:

  • Tooltip positions correctly no matter how you zoom or pan the background
  • Tooltip auto-sizes to fit its text content
  • Image dragging is smooth, stays centered on your mouse, and doesn't interfere with zoom
  • Tooltip hides automatically during drag and zoom to avoid weird behavior

内容的提问来源于stack exchange,提问作者mrgloom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:27:50