【问题标题】:Resetting drag with multiple elements使用多个元素重置拖动
【发布时间】:2019-08-17 12:06:50
【问题描述】:

我有多个 svg 组(每个都包含一个圆圈和文本),我通过 d3-drag 从初始位置拖动它们。我有一个矩形点击区域,我一次只想要其中一个可拖动组。因此,每当有两组处于命中区域时,我希望处于命中区域的第一组消失并重新出现在其初始位置。

我尝试通过一个函数来执行此操作,该函数通过查找圆形的当前位置并翻译如下:将组翻译回其初始位置:

translate(${-current_x}, ${-current_y})

这确实将组转换回 (0,0) 位置,所以我必须偏移它的初始位置。我通过将圆形的初始 x 和 y 值设置为圆形元素中的属性并将它们合并到翻译中来做到这一点:

translate(${-current_x + initial_x}, ${-current_y + initial_y})

这是我尝试的一部分:

https://bl.ocks.org/interwebjill/fb9b0d648df769ed72aeb2755d3ff7d5

这里是sn-p形式:

    const circleRadius = 40;
    const variables = ['one', 'two', 'three', 'four'];
    const inZone = [];
    
    // DOM elements
    const svg = d3.select("body").append("svg")
      .attr("width", 960)
      .attr("height", 500)
    
    const dragDockGroup = svg.append('g')
      .attr('id', 'draggables-dock');
    
    const dock = dragDockGroup.selectAll('g')
      .data(variables)
      .enter().append("g")
      .attr("id", (d, i) => `dock-${variables[i]}`);

    dock.append("circle")
      .attr("cx", (d, i) => circleRadius * (2 * i + 1))
      .attr("cy", circleRadius)
      .attr("r", circleRadius)
      .style("stroke", "none")
      .style("fill", "palegoldenrod");

    dock.append("text")
      .attr("x", (d, i) => circleRadius * (2 * i + 1))
      .attr("y", circleRadius)
      .attr("text-anchor", "middle")
      .style("fill", "white")
      .text((d, i) => variables[i]);
    
    const draggablesGroup = svg.append('g')
    	.attr('id', 'draggables');
    
    const draggables = draggablesGroup.selectAll('g')
      .data(variables)
      .enter().append("g")
      .attr("id", (d, i) => variables[i])
      .call(d3.drag()
          .on("start", dragStarted)
          .on("drag", dragged)
          .on("end", dragEnded));

    draggables.append('circle')
      .attr("cx", (d, i) => circleRadius * (2 * i + 1))
      .attr("cy", circleRadius)
      .attr("initial_x", (d, i) => circleRadius * (2 * i + 1))
      .attr("initial_y", circleRadius)
      .attr("r", circleRadius)
      .style("stroke", "orange")
      .style("fill", "yellowgreen");

    draggables.append("text")
       .attr("x", (d, i) => circleRadius * (2 * i + 1))
       .attr("y", circleRadius)
       .attr("text-anchor", "middle")
       .style("fill", "white")
       .text((d, i) => variables[i]);
    
    svg.append('rect')
      .attr("x", 960/2)
      .attr("y", 0)
      .attr("width", 100)
      .attr("height", 500/2)
      .attr("fill-opacity", 0)
      .style("stroke", "#848276")
      .attr("id", "hitZone");
    
    // functions
    function dragStarted() {
      d3.select(this).raise().classed("active", true);
  	}

  	function dragged() {
      d3.select(this).select("text").attr("x", d3.event.x).attr("y", d3.event.y);
      d3.select(this).select("circle").attr("cx", d3.event.x).attr("cy", d3.event.y);
  	}

  function dragEnded() {
    d3.select(this).classed("active", false);
    d3.select(this).lower();
    let hit = d3.select(document.elementFromPoint(d3.event.sourceEvent.clientX, d3.event.sourceEvent.clientY)).attr("id");

    if (hit == "hitZone") {
      inZone.push(this.id);
      if (inZone.length > 1) {
        let resetVar = inZone.shift();
        resetCircle(resetVar);
      }
    }
    
    d3.select(this).raise();
  }

  function resetCircle(resetVar) {
    let current_x = d3.select(`#${resetVar}`)
      .select('circle')
      .attr('cx');

    let current_y = d3.select(`#${resetVar}`)
      .select('circle')
      .attr('cy');

    let initial_x = d3.select(`#${resetVar}`)
      .select('circle')
      .attr('initial_x');

    let initial_y = d3.select(`#${resetVar}`)
      .select('circle')
      .attr('initial_y');

    d3.select(`#${resetVar}`)
      .transition()
        .duration(2000)
        .style('opacity', 0)
      .transition()
        .duration(2000)
        .attr('transform', `translate(${-current_x}, ${-current_y})`)
      .transition()
        .duration(2000)
        .style('opacity', 1);
  }
 body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
 <script src="https://d3js.org/d3.v5.min.js"></script>

以下是问题:

  1. 虽然使用translate(${-current_x}, ${-current_y}) 有效,但当我尝试使用translate(${-current_x + initial_x}, ${-current_y + initial_y}) 时,转换使用非常大的负数(例如,translate(-52640, -4640))。

  2. 虽然使用translate(${-current_x}, ${-current_y}) 有效,但当我再次尝试拖动此翻译组时,该组立即重复之前的translate(${-current_x}, ${-current_y})

【问题讨论】:

    标签: javascript d3.js svg transform


    【解决方案1】:

    您的代码遇到了困难,因为您同时定位了g 元素和子textcircles。

    圆圈和文字最初是通过 x/y 属性定位的:

    draggables.append('circle')
      .attr("cx", (d, i) => circleRadius * (2 * i + 1))
      .attr("cy", circleRadius)
    
    draggables.append("text")
       .attr("x", (d, i) => circleRadius * (2 * i + 1))
       .attr("y", circleRadius)
    

    拖动事件将圆圈和文字移动到这里:

    d3.select(this).select("text").attr("x", d3.event.x).attr("y", d3.event.y);
    d3.select(this).select("circle").attr("cx", d3.event.x).attr("cy", d3.event.y);
    

    然后我们尝试通过变换偏移父 g 来重置圆圈和文本:

    d3.select(`#${resetVar}`).attr('transform', `translate(${-current_x}, ${-current_y})`)
    

    其中current_xcurrent_y 是圆圈和文本的当前x、y 值。我们还存储了文本的初始 x,y 值,但总的来说,这变得更加复杂,因为我们有两组相互竞争的定位坐标。

    这可以相当简化。无需同时定位文本和圆圈,只需对持有圆圈和文本的父 g 应用变换即可。然后,当我们拖动时,我们更新变换,当我们完成时,我们重置变换。

    现在我们没有修改 x,y/cx,cy 属性和用于相对于彼此定位元素的变换。没有偏移,父 g 的变换将始终代表圆和文本的位置。

    下面我使用基准(不是元素属性)跟踪原始转换 - 通常我会使用基准的属性,但是您有非对象数据,所以我只需将基准替换为原始变换

    const circleRadius = 40;
    const variables = ['one', 'two', 'three', 'four'];
    const inZone = [];
        
    // DOM elements
    const svg = d3.select("body").append("svg")
      .attr("width", 960)
      .attr("height", 500)
        
    const dragDockGroup = svg.append('g')
      .attr('id', 'draggables-dock');
    
    // Immovable placemarkers:
    const dock = dragDockGroup.selectAll('g')
      .data(variables)
      .enter().append("g")
      .attr("id", (d, i) => `dock-${variables[i]}`);
    
    dock.append("circle")
      .attr("cx", (d, i) => circleRadius * (2 * i + 1))
      .attr("cy", circleRadius)
      .attr("r", circleRadius)
      .style("stroke", "none")
      .style("fill", "palegoldenrod");
    
    dock.append("text")
      .attr("x", (d, i) => circleRadius * (2 * i + 1))
      .attr("y", circleRadius)
      .attr("text-anchor", "middle")
      .style("fill", "white")
      .text((d, i) => variables[i]);
    
    // Dragables
    const draggablesGroup = svg.append('g')
      .attr('id', 'draggables');
        
    const draggables = draggablesGroup.selectAll('g')
      .data(variables)
      .enter()
      .append("g")
      .datum(function(d,i) {
         return "translate("+[circleRadius * (2 * i + 1),circleRadius]+")";
      })
      .attr("transform", (d,i) => d)
      .attr("id", (d, i) => variables[i])
      .call(d3.drag()
         .on("start", dragStarted)
         .on("drag", dragged)
         .on("end", dragEnded));
    
    draggables.append('circle')
      .attr("r", circleRadius)
      .style("stroke", "orange")
      .style("fill", "yellowgreen");
    
    draggables.append("text")
      .attr("text-anchor", "middle")
      .style("fill", "white")
      .text((d, i) => variables[i]);
        
    svg.append('rect')
       .attr("x", 960/2)
       .attr("y", 0)
       .attr("width", 100)
       .attr("height", 500/2)
       .attr("fill-opacity", 0)
       .style("stroke", "#848276")
       .attr("id", "hitZone");
        
     // functions
    function dragStarted() {
        d3.select(this).raise();
     }
    
    function dragged() {
       d3.select(this).attr("transform","translate("+[d3.event.x,d3.event.y]+")")
    }
    
    function dragEnded() {
      d3.select(this).lower();
      let hit = d3.select(document.elementFromPoint(d3.event.sourceEvent.clientX, d3.event.sourceEvent.clientY)).attr("id");
    
      if (hit == "hitZone") {
        inZone.push(this.id);
        if (inZone.length > 1) {
        let resetVar = inZone.shift();
        resetCircle(resetVar);
       }
      }
        
      d3.select(this).raise();
    }
    
    function resetCircle(resetVar) {
      d3.select(`#${resetVar}`)
       .transition()
         .duration(500)
         .style('opacity', 0)
       .transition()
         .duration(500)
         .attr("transform", (d,i) => d)
       .transition()
         .duration(500)
         .style('opacity', 1);
    }
     
    body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
    <script src="https://d3js.org/d3.v5.min.js"></script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-20
      • 1970-01-01
      • 1970-01-01
      • 2022-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多