首页 > 解决方案 > 如何在散点图中使用 d3.drag 处理比例

问题描述

我试图在 D3 中实现具有拖动行为的散点图,但我不明白如何处理具有拖动效果的缩放。

现在我这样做了:

https://bl.ocks.org/AlexisPister/c60d570d6739a28dfd8ba0c9eae76866/518dc76870582d2fb8d43609e320e91cb246c19d

据我了解,有两个坐标系:一个是我要绘制的对象(每个对象都由 x 和 y 坐标描述,两者都遵循正常规律),另一个是显示的画布。d3.scale 对象允许在两者之间进行映射。

因此,首先,要找到从 d3.event 对象的坐标拖动的对象,我遍历所有数据数组并找到其缩放坐标等于 d3.event 的 x 和 y 的对象。

然后我用 d3.event 坐标修改这个对象坐标,用比例对象进行逆变换以从画布坐标系映射到对象坐标系。

但是,这不起作用:当一个点被拖动时,它会移动,但不会移动光标所在的位置。我不明白这种行为。

标签: javascriptd3.jscanvas

解决方案


您将需要知道鼠标位置并将其应用于当前的 x 和 y。做了一些改变,看看mousePosition,你需要更多的工作来使它完美,但你会得到idee

    let width = 960,
        height = 500,
        radius = 3,
        transform = d3.zoomIdentity
    
    let canvas = d3.select("body").append("canvas")
      .attr("width", width)
      .attr("height", height)

    let ctx = canvas.node().getContext("2d");
    
   	let data = d3.range(200)
    							.map(() => { return {"x": d3.randomNormal()(),
                                       "y": d3.randomNormal()()}})
    
    let scaleX = d3.scaleLinear()
    								.domain(d3.extent(data, function(d){return d.x}))
    								.range([0, width])
    
    let scaleY = d3.scaleLinear()
    								.domain(d3.extent(data, function(d){return d.y}))
    								.range([height, 0])
    
    //canvas.call(d3.zoom()
    //            		.on("zoom", zoomed))
    
    canvas.call(d3.drag()
                		.subject(dragSubject)
               			.on("drag", dragged))
    
    function zoomed(){
      transform = d3.event.transform
      render()
    }
    
    function dragSubject(){
   
    	x = d3.event.x 
      y = d3.event.y
      

      for(let i = 0; i < data.length; i++){

        point = data[i]
        dx = x - scaleX(point.x)  
        dy = y - scaleY(point.y) 

        if (dx * dx + dy * dy < radius * radius){          
          return data[i];
        }
      }
    }
    
    
    // get the position of the mouse, you need to calculate scale to
    function mousePosition(event) {
            if (event == undefined || (!event.pageX && !event.offsetX))
             return {x:0, y:0}
            return { x: event.pageX || event.offsetX, y: event.pageY || event.offsetY };
        };
    
    function dragged(){
      
      var mouse = mousePosition(window.event)
      d3.event.subject.x = scaleX.invert(d3.event.x + (mouse.x)) 
      d3.event.subject.y = scaleY.invert(d3.event.y + mouse.y) 
   

      render()
    }
    
    function render(){
      ctx.save()
      ctx.clearRect(0, 0, width, height)
      
      ctx.beginPath()
      //ctx.translate(transform.x, transform.y)
      //ctx.scale(transform.k, transform.k)
      
      data.forEach(function(d){
        ctx.moveTo(scaleX(d.x) + radius, scaleY(d.y))
        ctx.arc(scaleX(d.x), scaleY(d.y), radius, 0, Math.PI * 2)
      })
      ctx.stroke()
      
      /*
      ctx.globalAlpha = 0.2
      ctx.fillStyle = "blue"
      ctx.fillRect(0,0,width,height) */
      
      ctx.restore()
    }
    
    render()
<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <style>
    body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
  </style>
</head>

<body>

</body>


推荐阅读