最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can I ensure a y-scale zoom is centered on my cursor in spite of marginpadding? - Stack Overflow

programmeradmin1浏览0评论

I have this d3 snippet here:

/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Order Book Visualization</title>
    <script src=".v7.min.js"></script>
    <style>
        .axis path,
        .axis line {
            fill: none;
            shape-rendering: crispEdges;
        }
    </style>
</head>
<body>
    <svg width="800" height="400"></svg>
    <script>
        let orderBookStateVis = {
            MinY: 100,  // Example value
            MaxY: 300,  // Example value
            MinX: -100,   // Example value
            MaxX: 100     // Example value
        };

        // Define the chart dimensions
        const svg = d3.select("svg"),
            margin = { top: 100, right: 30, bottom: 40, left: 40 },
            width = +svg.attr("width") - margin.left - margin.right,
            height = +svg.attr("height") - margin.top - margin.bottom;

        const g = svg.append("g")
            .attr("transform", `translate(${margin.left},${margin.top})`);

        const y = d3.scaleLinear()
            .domain([orderBookStateVis.MinY, orderBookStateVis.MaxY])
            .range([height, 0]);

        const x = d3.scaleLinear()
            .domain([orderBookStateVis.MinX, orderBookStateVis.MaxX])
            .range([0, width]);

        const yAxis = g.append("g")
            .attr("class", "y-axis")
            .call(d3.axisLeft(y));

        const xAxis = g.append("g")
            .attr("class", "x-axis")
            .call(d3.axisBottom(x).tickSizeOuter(0))
            .attr("transform", `translate(0,${height})`);

        // Zoom behavior
        const zoom = d3.zoom()
            .scaleExtent([0.5, 5])
            .translateExtent([[0, 0], [width, height]])
            .extent([[0, 0], [width, height]])
            .on("zoom", zoomed);

        svg.call(zoom);

        // Reset view function
        function resetZoom() {
            svg.transition().duration(750).call(zoom.transform, d3.zoomIdentity);
        }

        svg.on("contextmenu", (event) => {
            event.preventDefault();
            resetZoom();
        });

        // Zoom event handler
        function zoomed(event) {
        
            const transform = event.transform;
                            console.log(transform.y);


            // Rescale axes
            const zx = transform.rescaleX(x);
            const zy = transform.rescaleY(y);

            xAxis.call(d3.axisBottom(zx).tickSizeOuter(0));
            yAxis.call(d3.axisLeft(zy));
        }
    </script>
</body>
</html>

My one problem with it is that when I zoom in on the y-axis, my zoom is not centered where my cursor is. I'd expect that if I'm hovering on y=200, then after zoom I should still be hovering on that point - instead my y position constantly becomes more disjointed from where my cursor is. I've discovered that the issue only arises when I add in a top margin. How can I correct for this top margin?

I have this d3 snippet here:

https://jsfiddle/h8b6gq7d/

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Order Book Visualization</title>
    <script src="https://d3js./d3.v7.min.js"></script>
    <style>
        .axis path,
        .axis line {
            fill: none;
            shape-rendering: crispEdges;
        }
    </style>
</head>
<body>
    <svg width="800" height="400"></svg>
    <script>
        let orderBookStateVis = {
            MinY: 100,  // Example value
            MaxY: 300,  // Example value
            MinX: -100,   // Example value
            MaxX: 100     // Example value
        };

        // Define the chart dimensions
        const svg = d3.select("svg"),
            margin = { top: 100, right: 30, bottom: 40, left: 40 },
            width = +svg.attr("width") - margin.left - margin.right,
            height = +svg.attr("height") - margin.top - margin.bottom;

        const g = svg.append("g")
            .attr("transform", `translate(${margin.left},${margin.top})`);

        const y = d3.scaleLinear()
            .domain([orderBookStateVis.MinY, orderBookStateVis.MaxY])
            .range([height, 0]);

        const x = d3.scaleLinear()
            .domain([orderBookStateVis.MinX, orderBookStateVis.MaxX])
            .range([0, width]);

        const yAxis = g.append("g")
            .attr("class", "y-axis")
            .call(d3.axisLeft(y));

        const xAxis = g.append("g")
            .attr("class", "x-axis")
            .call(d3.axisBottom(x).tickSizeOuter(0))
            .attr("transform", `translate(0,${height})`);

        // Zoom behavior
        const zoom = d3.zoom()
            .scaleExtent([0.5, 5])
            .translateExtent([[0, 0], [width, height]])
            .extent([[0, 0], [width, height]])
            .on("zoom", zoomed);

        svg.call(zoom);

        // Reset view function
        function resetZoom() {
            svg.transition().duration(750).call(zoom.transform, d3.zoomIdentity);
        }

        svg.on("contextmenu", (event) => {
            event.preventDefault();
            resetZoom();
        });

        // Zoom event handler
        function zoomed(event) {
        
            const transform = event.transform;
                            console.log(transform.y);


            // Rescale axes
            const zx = transform.rescaleX(x);
            const zy = transform.rescaleY(y);

            xAxis.call(d3.axisBottom(zx).tickSizeOuter(0));
            yAxis.call(d3.axisLeft(zy));
        }
    </script>
</body>
</html>

My one problem with it is that when I zoom in on the y-axis, my zoom is not centered where my cursor is. I'd expect that if I'm hovering on y=200, then after zoom I should still be hovering on that point - instead my y position constantly becomes more disjointed from where my cursor is. I've discovered that the issue only arises when I add in a top margin. How can I correct for this top margin?

Share Improve this question edited Mar 21 at 13:56 Mark 109k20 gold badges179 silver badges236 bronze badges asked Mar 17 at 16:18 Rory McDonaldRory McDonald 2952 silver badges9 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 3

You can add a rect of the same dimensions of your "canvas" g and put the zoom events on it:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Order Book Visualization</title>
    <script src="https://d3js./d3.v7.min.js"></script>
    <style>
      .axis path,
      .axis line {
        fill: none;
        shape-rendering: crispEdges;
      }
    </style>
  </head>
  <body>
    <svg width="800" height="400"></svg>
    <script>
      let orderBookStateVis = {
        MinY: 100, // Example value
        MaxY: 300, // Example value
        MinX: -100, // Example value
        MaxX: 100, // Example value
      }

      // Define the chart dimensions
      const svg = d3.select("svg"),
        margin = { top: 100, right: 30, bottom: 40, left: 40 },
        width = +svg.attr("width") - margin.left - margin.right,
        height = +svg.attr("height") - margin.top - margin.bottom

      const rect = svg
        .append("rect")
        .attr("width", width)
        .attr("height", height)
        .attr("transform", `translate(${margin.left},${margin.top})`)
        .style("fill", "none")
        .style("pointer-events", "all")

      const g = svg
        .append("g")
        .attr("transform", `translate(${margin.left},${margin.top})`)

      const y = d3
        .scaleLinear()
        .domain([orderBookStateVis.MinY, orderBookStateVis.MaxY])
        .range([height, 0])

      const x = d3
        .scaleLinear()
        .domain([orderBookStateVis.MinX, orderBookStateVis.MaxX])
        .range([0, width])

      const yAxis = g.append("g").attr("class", "y-axis").call(d3.axisLeft(y))

      const xAxis = g
        .append("g")
        .attr("class", "x-axis")
        .call(d3.axisBottom(x).tickSizeOuter(0))
        .attr("transform", `translate(0,${height})`)

      // Zoom behavior
      const zoom = d3
        .zoom()
        .scaleExtent([0.5, 5])
        .translateExtent([
          [0, 0],
          [width, height],
        ])
        .extent([
          [0, 0],
          [width, height],
        ])
        .on("zoom", zoomed)

      rect.call(zoom)

      // Reset view function
      function resetZoom() {
        svg.transition().duration(750).call(zoom.transform, d3.zoomIdentity)
      }

      svg.on("contextmenu", (event) => {
        event.preventDefault()
        resetZoom()
      })

      // Zoom event handler
      function zoomed(event) {
        const transform = event.transform

        // Rescale axes
        const zx = transform.rescaleX(x)
        const zy = transform.rescaleY(y)

        xAxis.call(d3.axisBottom(zx).tickSizeOuter(0))
        yAxis.call(d3.axisLeft(zy))
      }
    </script>
  </body>
</html>

发布评论

评论列表(0)

  1. 暂无评论