D3线图鼠标移动点位置错误

时间:2018-06-06 06:45:16

标签: d3.js

我想创建折线图,当我移动鼠标时,它会显示点和此点数据。这是我的代码。但我发现点位不正确。我不知道。谢谢你的帮助。

这是我的代码。我用的是d3 v4。

 (function lineChart() {

      elementId = 'real_monitor_platform_getBetAmount';
      xMax = 1528170430000;
      yMax = 8;
      xMin = 1528170360000;
      yMin = 0;
      x = 'unixtime';
      y = 'betAmount';
      dataset = [{unixtime:1528170360000,betAmount:0},
        {unixtime:1528170370000,betAmount:1},
        {unixtime:1528170380000,betAmount:2},
        {unixtime:1528170390000,betAmount:3},
        {unixtime:1528170400000,betAmount:5},
        {unixtime:1528170410000,betAmount:6},
        {unixtime:1528170420000,betAmount:7},
        {unixtime:1528170430000,betAmount:8}];

      dataset.sort((a, b) => a[x] - b[x]);

      const margin = {
        top: 30, right: 40, bottom: 120, left: 60,
      };
      const w = 700; 
      const h = 300;
      const width = w + margin.left + margin.right;
      const height = h + margin.top + margin.bottom;
      const formatTime = d3.timeFormat('%Y-%m-%d %H:%M:%S');

      const svg = d3.select(`#${elementId}`).append('svg')
        .attr('width', width) 
        .attr('height', height), 
      areaWidth = width - margin.left - margin.right,
      areaHeight = svg.attr('height') - margin.top - margin.bottom,
      g = svg.append('g')
        .attr('id', 'group')
        .attr('transform', `translate(${margin.left},${margin.top})`)
        .attr('width', areaWidth)
        .attr('height', areaHeight);

      const xScale = d3.scaleTime()
        .domain([xMin, xMax])
        .range([0, areaWidth]);

      const yScale = d3.scaleLinear().domain([yMin, yMax]).range([areaHeight, 0]);

      // create axis objects
      const xAxis = d3.axisBottom(xScale).ticks(width / 100);
      const yAxis = d3.axisLeft(yScale);

      const line = d3.line()
        .x(d =>
          xScale(d[x]),
        ).y(d =>
          yScale(d[y]),
        );

      const t = d3.transition()
        .duration(500)
        .ease(d3.easeLinear);

      const xGrooup = g.append('g')
        .attr('transform', `translate(0,${areaHeight})`)
        .call(xAxis.tickFormat(formatTime).ticks(3)); 

      const yGroup = g.append('g')
        .attr('transform', 'translate(0,0)')
        .call(yAxis);

      g.append('clipPath')
        .attr('id', 'clip')
        .append('rect')
        .attr('width', areaWidth)
        .attr('height', areaHeight);

      const bisectDate = d3.bisector(d => d.unixtime).left;

      const focus = g.append('g')
        .attr('class', 'focus')
        .style('display', 'none');

      const circle = focus.append('circle')
        .attr('r', 4)
        .style('fill', '#F1F3F3')
        .style('stroke', '#6F257F')
        .style('stroke-width', '1px');

      const updateLine = g.append('g')
        .attr('class', 'chart')
        .selectAll('line')
        .data([dataset]);

      const enterLine = updateLine.enter();

      const exitLine = updateLine.exit();

      const path = enterLine.append('path')
        .attr('clip-path', 'url(#clip)')
        .attr('class', 'line')
        .attr('d', line)
        .attr('fill', 'none')
        .attr('stroke', 0)
        .transition(t)
        .attr('stroke-width', 1)
        .attr('stroke', 'DodgerBlue');

      exitLine.remove();

      const zoom = d3.zoom()
        .scaleExtent([1, 80])
        .translateExtent([[0, 0], [areaWidth, areaHeight]])
        .on('zoom', zoomed);

      const zoomRect = svg.append('rect')
        .attr('width', width)
        .attr('height', height)
        .attr('transform', 'translate(0,0)')
        .style('fill', 'transparent')
        .attr('fill', 'none')
        .attr('pointer-events', 'all')
        .call(zoom)
        .on("mouseover", function() { focus.style("display", null); })
        .on("mouseout", function() { focus.style("display", "none"); })
        .on("mousemove", mousemove);

      function zoomed() {
        const new_xScale = d3.event.transform.rescaleX(xScale);

        xGrooup.call(xAxis.scale(new_xScale));

        d3.select(`#${elementId}`).select('svg').select('#group').select('.chart')
          .select('path.line')
          .attr('d', line.x(d => new_xScale(d.unixtime)));
      }

      function mousemove() {
        var transform = d3.zoomTransform(this);
        var xt = transform.rescaleX(xScale);
        var yt = transform.rescaleY(yScale);
        let g = d3.select("#group")._groups[0][0]
        var mouse = d3.mouse(g);
        var x0 = xt.invert(mouse[0]);
        var i = bisectDate(dataset, x0, 1);
        var d0 = dataset[i - 1];
        var d1 = dataset[i];
        var d = x0 - d0[x] > d1[x] - x0 ? d1 : d0;


        circle.attr("transform", `translate(${transform.applyX(xScale(d[x]))},${transform.applyY(yScale(d[y]))})`);

      }
    })();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="real_monitor_platform_getBetAmount"></div>

修改

这对我有用。

 (function lineChart() {

      elementId = 'real_monitor_platform_getBetAmount';
      xMax = 1528170430000;
      yMax = 8;
      xMin = 1528170360000;
      yMin = 0;
      x = 'unixtime';
      y = 'betAmount';
      dataset = [{unixtime:1528170360000,betAmount:0},
        {unixtime:1528170370000,betAmount:1},
        {unixtime:1528170380000,betAmount:2},
        {unixtime:1528170390000,betAmount:3},
        {unixtime:1528170400000,betAmount:5},
        {unixtime:1528170410000,betAmount:6},
        {unixtime:1528170420000,betAmount:7},
        {unixtime:1528170430000,betAmount:8}];

      dataset.sort((a, b) => a[x] - b[x]);

      const margin = {
        top: 30, right: 40, bottom: 120, left: 60,
      };
      const w = 700; 
      const h = 300;
      const width = w + margin.left + margin.right;
      const height = h + margin.top + margin.bottom;
      const formatTime = d3.timeFormat('%Y-%m-%d %H:%M:%S');

      const svg = d3.select(`#${elementId}`).append('svg')
        .attr('width', width) 
        .attr('height', height), 
      areaWidth = width - margin.left - margin.right,
      areaHeight = svg.attr('height') - margin.top - margin.bottom,
      g = svg.append('g')
        .attr('id', 'group')
        .attr('transform', `translate(${margin.left},${margin.top})`)
        .attr('width', areaWidth)
        .attr('height', areaHeight);

      const xScale = d3.scaleTime()
        .domain([xMin, xMax])
        .range([0, areaWidth]);

      const yScale = d3.scaleLinear().domain([yMin, yMax]).range([areaHeight, 0]);

      // create axis objects
      const xAxis = d3.axisBottom(xScale).ticks(width / 100);
      const yAxis = d3.axisLeft(yScale);

      const line = d3.line()
        .x(d =>
          xScale(d[x]),
        ).y(d =>
          yScale(d[y]),
        );

      const t = d3.transition()
        .duration(500)
        .ease(d3.easeLinear);

      const xGrooup = g.append('g')
        .attr('transform', `translate(0,${areaHeight})`)
        .call(xAxis.tickFormat(formatTime).ticks(3)); 

      const yGroup = g.append('g')
        .attr('transform', 'translate(0,0)')
        .call(yAxis);

      g.append('clipPath')
        .attr('id', 'clip')
        .append('rect')
        .attr('width', areaWidth)
        .attr('height', areaHeight);

      const bisectDate = d3.bisector(d => d.unixtime).left;

      const focus = g.append('g')
        .attr('class', 'focus')
        .style('display', 'none');

      const circle = focus.append('circle')
        .attr('r', 4)
        .style('fill', '#F1F3F3')
        .style('stroke', '#6F257F')
        .style('stroke-width', '1px');

      const updateLine = g.append('g')
        .attr('class', 'chart')
        .selectAll('line')
        .data([dataset]);

      const enterLine = updateLine.enter();

      const exitLine = updateLine.exit();

      const path = enterLine.append('path')
        .attr('clip-path', 'url(#clip)')
        .attr('class', 'line')
        .attr('d', line)
        .attr('fill', 'none')
        .attr('stroke', 0)
        .transition(t)
        .attr('stroke-width', 1)
        .attr('stroke', 'DodgerBlue');

      exitLine.remove();

      const zoom = d3.zoom()
        .scaleExtent([1, 80])
        .translateExtent([[0, 0], [areaWidth, areaHeight]])
        .on('zoom', zoomed);

      const zoomRect = svg.append('rect')
        .attr('width', width)
        .attr('height', height)
        .attr('transform', 'translate(0,0)')
        .style('fill', 'transparent')
        .attr('fill', 'none')
        .attr('pointer-events', 'all')
        .call(zoom)
        .on("mouseover", function() { focus.style("display", null); })
        .on("mouseout", function() { focus.style("display", "none"); })
        .on("mousemove", mousemove);

      function zoomed() {
        const new_xScale = d3.event.transform.rescaleX(xScale);

        xGrooup.call(xAxis.scale(new_xScale));

        d3.select(`#${elementId}`).select('svg').select('#group').select('.chart')
          .select('path.line')
          .attr('d', line.x(d => new_xScale(d.unixtime)));
      }

      function mouseDate(scale) {
        var g = d3.select("#group")._groups[0][0]
        var x0 = scale.invert(d3.mouse(g)[0]),
            i = bisectDate(dataset, x0, 1),
            d0 = dataset[i - 1],
            d1 = dataset[i],
            d = x0 - d0[x] > d1[x] - x0 ? d1 : d0;
        return d;
      }

      function mousemove() {
        var transform = d3.zoomTransform(this);
        var xt = transform.rescaleX(xScale);
        var yt = transform.rescaleY(yScale);
				d = mouseDate(xt);

        console.log(transform.applyX(xScale(d[x])));
        console.log(transform.applyY(yScale(d[y])));

        circle.attr("transform", `translate(${transform.applyX(xScale(d[x]))},${(yScale(d[y]))})`);

      }
    })();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="real_monitor_platform_getBetAmount"></div>

1 个答案:

答案 0 :(得分:2)

您必须考虑边距。

所以,而不是......

const focus = svg.append('g')

......应该是:

const focus = g.append('g')

以下是具有该更改的代码:

(function lineChart() {

      elementId = 'real_monitor_platform_getBetAmount';
      xMax = 1528170430000;
      yMax = 8;
      xMin = 1528170360000;
      yMin = 0;
      x = 'unixtime';
      y = 'betAmount';
      dataset = [{unixtime:1528170360000,betAmount:0},
        {unixtime:1528170370000,betAmount:1},
        {unixtime:1528170380000,betAmount:2},
        {unixtime:1528170390000,betAmount:3},
        {unixtime:1528170400000,betAmount:5},
        {unixtime:1528170410000,betAmount:6},
        {unixtime:1528170420000,betAmount:7},
        {unixtime:1528170430000,betAmount:8}];

      dataset.sort((a, b) => a[x] - b[x]);

      const margin = {
        top: 30, right: 40, bottom: 120, left: 60,
      };
      const w = 700; 
      const h = 300;
      const width = w + margin.left + margin.right;
      const height = h + margin.top + margin.bottom;
      const formatTime = d3.timeFormat('%Y-%m-%d %H:%M:%S');

      const svg = d3.select(`#${elementId}`).append('svg')
        .attr('width', width) 
        .attr('height', height), 
      areaWidth = width - margin.left - margin.right,
      areaHeight = svg.attr('height') - margin.top - margin.bottom,
      g = svg.append('g')
        .attr('id', 'group')
        .attr('transform', `translate(${margin.left},${margin.top})`)
        .attr('width', areaWidth)
        .attr('height', areaHeight);

      const xScale = d3.scaleTime()
        .domain([xMin, xMax])
        .range([0, areaWidth]);

      const yScale = d3.scaleLinear().domain([yMin, yMax]).range([areaHeight, 0]);

      // create axis objects
      const xAxis = d3.axisBottom(xScale).ticks(width / 100);
      const yAxis = d3.axisLeft(yScale);

      const line = d3.line()
        .x(d =>
          xScale(d[x]),
        ).y(d =>
          yScale(d[y]),
        );

      const t = d3.transition()
        .duration(500)
        .ease(d3.easeLinear);

      const xGrooup = g.append('g')
        .attr('transform', `translate(0,${areaHeight})`)
        .call(xAxis.tickFormat(formatTime).ticks(3)); 

      const yGroup = g.append('g')
        .attr('transform', 'translate(0,0)')
        .call(yAxis);

      g.append('clipPath')
        .attr('id', 'clip')
        .append('rect')
        .attr('width', areaWidth)
        .attr('height', areaHeight);

      const bisectDate = d3.bisector(d => d.unixtime).left;

      const focus = g.append('g')
        .attr('class', 'focus')
        .style('display', 'none');

      const circle = focus.append('circle')
        .attr('r', 4)
        .style('fill', '#F1F3F3')
        .style('stroke', '#6F257F')
        .style('stroke-width', '1px');

      const updateLine = g.append('g')
        .attr('class', 'chart')
        .selectAll('line')
        .data([dataset]);

      const enterLine = updateLine.enter();

      const exitLine = updateLine.exit();

      const path = enterLine.append('path')
        .attr('clip-path', 'url(#clip)')
        .attr('class', 'line')
        .attr('d', line)
        .attr('fill', 'none')
        .attr('stroke', 0)
        .transition(t)
        .attr('stroke-width', 1)
        .attr('stroke', 'DodgerBlue');

      exitLine.remove();

      const zoom = d3.zoom()
        .scaleExtent([1, 80])
        .translateExtent([[0, 0], [areaWidth, areaHeight]])
        .on('zoom', zoomed);

      const zoomRect = svg.append('rect')
        .attr('width', width)
        .attr('height', height)
        .attr('transform', 'translate(0,0)')
        .style('fill', 'transparent')
        .attr('fill', 'none')
        .attr('pointer-events', 'all')
        .call(zoom)
        .on("mouseover", function() { focus.style("display", null); })
        .on("mouseout", function() { focus.style("display", "none"); })
        .on("mousemove", mousemove);

      function zoomed() {
        const new_xScale = d3.event.transform.rescaleX(xScale);

        xGrooup.call(xAxis.scale(new_xScale));

        d3.select(`#${elementId}`).select('svg').select('#group').select('.chart')
          .select('path.line')
          .attr('d', line.x(d => new_xScale(d.unixtime)));
      }

      function mousemove() {
        var mouse = d3.mouse(this);
        var x0 = xScale.invert(mouse[0]);
        var i = bisectDate(dataset, x0);
        var d0 = dataset[i - 1];
        var d1 = dataset[i];
        var d = x0 - d0[x] > d1[x] - x0 ? d1 : d0;
        circle.attr("transform", `translate(${xScale(d[x])},${(yScale(d[y]))})`);

      }
    })();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<div id="real_monitor_platform_getBetAmount"></div>

顺便说一句,这不适用于缩放...但是,那是另一个问题。