简体   繁体   English

如何执行d3拖放

[英]how to perform d3 drag and drop

Having Issue with d3.drag for angular 4. whenever I drag the rectangle object , it is moving fine for first time. 问题是d3.drag for angular 4.每当我拖动矩形对象时,它首次正常运行。 After release of mousepress and again trying to drag the rectangle, it is going back to previous event and not able to make mouse control on draggable object. 释放mousepress并再次尝试拖动矩形后,它将返回上一个事件,无法对可拖动对象进行鼠标控制。 Please give solution to my problem. 请解决我的问题。

import { Component,Input, ElementRef, OnInit } from '@angular/core'; 
import * as d3 from 'd3';  
interface LineData{
  xVal: number,
  yVal:number
}

@Component({
  selector: 'app-line-chart',
  template:'<svg height="500" width="500" ></svg>',
  styleUrl: [] 
})

export class LineChartComponent implements OnInit {
  @Input() data : LineData[];
  private parentNativeElement : any;
  constructor(private element:ElementRef) { 
      this.parentNativeElement = element.nativeElement;
  }

  ngOnInit() {
    var width = 300;
    var height = 300;
    var margin = {top: 10, right: 10, bottom: 30, left: 10}

    var x = d3.scaleLinear().range([0, width]);
    var y = d3.scaleLinear().range([height, 0]);

    var xAxis = d3.axisBottom(x)
        .scale(x)
        .ticks(5);

    var yAxis = d3.axisLeft(y)
        .scale(y)
        .ticks(5);

    var valueline:any = d3.line()
        .x(function (d) {
            return x(d['xVal']);
        })
        .y(function (d) {
            return y(d['yVal']);
        });
        console.log(valueline);

    var svg = d3.select("svg");
    d3.select(this.parentNativeElement)
        .append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g");

    // Get the data
    var data = [
                  {
                    "xVal": 1,
                    "yVal": 2
                  },
                  {
                    "xVal": 2,
                    "yVal": 4
                  },
                  {
                    "xVal": 3,
                    "yVal": 1
                  },
                  {
                    "xVal": 4,
                    "yVal": 5
                  },
                  {
                    "xVal": 5,
                    "yVal": 3
                  }
              ];

    // Scale the range of the data
    x.domain(d3.extent(data,
        function (d) {
            return d.xVal;
        }));
    y.domain([
        0, d3.max(data,
            function (d) {
                return d.yVal;
            })
    ]);
     let color = d3.scaleOrdinal(d3.schemeCategory10);

     svg.append("path").datum(data).attr("class","path")// Add the valueline path.
    .attr("fill", "none")
    .attr("stroke", "red")
    .attr("stroke-width", 1.5)
    .attr("d", valueline(data)).attr("class", "line");


    let rectangle:any = d3.range(1).map(function(){
        return{
            x: Math.floor(Math.random()*width),
            y: Math.floor(Math.random()*height)
        };
    });

    console.log(rectangle);

   let dragRect = svg.selectAll('g').data(rectangle).enter().append("g")

    dragRect.append("rect")
    .attr("x",function(d){return d['x'];})
    .attr("y",function(d){return d['y'];})
    .attr("height",50)
    .attr("width",50).style("fill", "steelblue");

   svg.selectAll('g').attr("transform", 
    "translate(" + margin.left + "," + margin.top + ")").data(rectangle)
    .call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));


    function dragstarted(d){
        d3.select(this).raise().classed("active",true);
    }



    function dragged(d){
        d.xVal = x.invert(d3.event.x);
        d.yVal = y.invert(d3.event.y);
        d3.select(this).select("rect")
        .attr("x", x(d.xVal))
        .attr("y", y(d.yVal))
        .attr("transform","translate("+d.xVal+","+d.yVal+")")
        console.log(d); 
    }


    function dragended(d){
        d3.select(this).raise().classed("active",false);
        //d3.select('rect#no-drag').on('mousedown.drag',null);
    }



    svg.append("g") // Add the X Axis
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis);

    svg.append("g") // Add the Y Axis
        .attr("class", "y axis"`enter code here`)
        .call(yAxis);
  }

}

The basic problem seems to be that dragged function is not remembering the x & y between successive drag events. 基本问题似乎是dragged函数不记住连续拖动事件之间的x和y。

To do this, you need 要做到这一点,你需要

d3.select(this)
  .attr("x", d.x = x(d.xVal))
  .attr("y", d.y = y(d.yVal))

instead of 代替

d3.select(this)
  .attr("x", x(d.xVal))
  .attr("y", y(d.yVal))

Run this code snippet to check it out 运行此代码段以将其签出

 console.clear() var width = 300; var height = 300; var margin = {top: 10, right: 10, bottom: 30, left: 10} var x = d3.scaleLinear().range([0, width]); var y = d3.scaleLinear().range([height, 0]); var xAxis = d3.axisBottom(x).scale(x).ticks(5); var yAxis = d3.axisLeft(y).scale(y).ticks(5); var valueline = d3.line() .x(function (d) { return x(d['xVal']); }) .y(function (d) { return y(d['yVal']); }); var svg = d3.select("svg"); d3.select(this.parentNativeElement) .append("svg") .attr("width", width) .attr("height", height) .append("g"); // Get the data var data = [ { "xVal": 1, "yVal": 2 }, { "xVal": 2, "yVal": 4 }, { "xVal": 3, "yVal": 1 }, { "xVal": 4, "yVal": 5 }, { "xVal": 5, "yVal": 3 } ]; // Scale the range of the data x.domain(d3.extent(data, function (d) { return d.xVal; })); y.domain([ 0, d3.max(data, function (d) { return d.yVal; }) ]); let color = d3.scaleOrdinal(d3.schemeCategory10); svg.append("path").datum(data).attr("class","path") .attr("fill", "none") .attr("stroke", "red") .attr("stroke-width", 1.5) .attr("d", valueline(data)).attr("class", "line"); let rectangle = d3.range(3).map(function() { return { x: Math.floor(Math.random()*width), y: Math.floor(Math.random()*height) }; }); let dragRect = svg.selectAll('g').data(rectangle).enter() .append("g") dragRect.append("rect") .attr("x",function(d){return d['x'];}) .attr("y",function(d){return d['y'];}) .attr("height", 50) .attr("width", 50) .style("fill", "steelblue") svg.selectAll('rect') .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .data(rectangle) .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended) ); const dragBounds = {} const tickHeight = 10; function setDragBounds(subject) { dragBounds.top = 0 - margin.top; dragBounds.left = 0 - margin.left; dragBounds.bottom = height - tickHeight - subject.attr('height'); dragBounds.right = width - margin.right - subject.attr('width'); } function dragstarted(d){ /* Calculate drag bounds at dragStart because it's one event vs many events if done in 'dragged()' */ setDragBounds(d3.select(this)) d3.select(this).raise().classed("active", true); } function dragged(d){ d3.select(this) .attr("x", getX(dx = d3.event.x) ) .attr("y", getY(dy = d3.event.y) ); } function getX(x) { return x < dragBounds.left ? dragBounds.left : x > dragBounds.right ? dragBounds.right : x } function getY(y) { return y < dragBounds.top ? dragBounds.top : y > dragBounds.bottom ? dragBounds.bottom : y } function dragended(d){ d3.select(this).classed("active", false); } svg.append("g") // Add the X Axis .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) svg.append("g") // Add the Y Axis .attr("class", "y axis") .call(yAxis); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <svg height="500" width="500" ></svg> 

Note I attached the drag events to rect instead of g , which I assume was a typo. 注意我将拖动事件附加到rect而不是g ,我认为这是一个错字。


Keeping within bounds 保持在界限内

To constrain the drag to bounds the way I found works best is a max/min function on the x & y values. 要限制拖动限制我找到最佳工作方式是x和y值的最大/最小函数。

function dragged(d){
  d3.select(this)
    .attr("x", getX(d.x = d3.event.x) )
    .attr("y", getY(d.y = d3.event.y) );
}

function getX(x) {
  return x < dragBounds.left ? dragBounds.left
    : x > dragBounds.right ? dragBounds.right 
    : x
}

function getY(y) {
  return y < dragBounds.top ? dragBounds.top
    : y > dragBounds.bottom ? dragBounds.bottom 
    : y
}

Bounds are set at drag start to avoid repetition of any calculation. 在拖动开始时设置界限以避免重复任何计算。

const dragBounds = {}
const tickHeight = 10;

function setDragBounds(subject) {
  dragBounds.top = 0 - margin.top;
  dragBounds.left = 0 - margin.left;
  dragBounds.bottom = height - tickHeight - subject.attr('height');
  dragBounds.right = width - margin.right - subject.attr('width');
}

function dragstarted(d){
  /* 
    Calculate drag bounds at dragStart because it's one event vs many 
    events if done in 'dragged()'
  */    
  setDragBounds(d3.select(this)) 
  d3.select(this).raise().classed("active", true);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM