【问题标题】:d3 drag only works when axis ticks are highlightedd3 拖动仅在突出显示轴刻度时有效
【发布时间】:2019-01-02 07:37:39
【问题描述】:

我正在开发一个合并 Angular 和 D3 (v3) 的包,特别是尝试将 D3 的拖动功能合并到包中。我非常接近成功,但是当我的绘图第一次渲染时,我无法拖动元素(在本例中为矩形),除非我使用光标选择 x 轴上的文本。文本突出显示后,用户可以拖动矩形,该功能似乎可以正常工作,但我真的很想修复最初的错误。

我已经在Github.io project found here 中复制了这个问题。我在库 ivml.0.0.0(包括 in the repo)的第 903-966 行中定义了 d3 的拖动行为,我将在下面包括:

    if (Bars.events.dragE) {

        // Note: xs(0) refers to pixel value at 0 on the x-scale. Must be reset for generalizability

        // function returns width (in pixels) of bar. Used to ensure drag doesn't exceed plot area

        function returnw(d, i){
            if (values_function(d, i) <= 0) {
                Bars.elements.barController.offset(position_function(d, i), xs(0) - xs(values_function(d, i)), values_function(d, i));
                return xs(0) - xs(values_function(d, i));
            }
            else {
                Bars.elements.barController.offset(position_function(d, i), xs(values_function(d, i)) - xs(0), values_function(d, i))
                return Math.abs(xs(0)-xs(values_function(d, i)))
            }
        }

        var drag = d3.behavior.drag()
        .on('dragstart', draginit)
        .on('drag', dragmove)
        .on('dragend', dragfin);

        function draginit(d){
            d3.event.sourceEvent.stopPropagation();
            // replace fill color with variable for user input function
            Bars.events.dragE(d, i, d3.select(this).attr('style', 'fill: yellow; stroke: black; stroke-width: 3px;'))
        }

        function dragmove(d){
            var svgwidth = Bars.elements.chartController.plotObject.attributes.width //margin of plotObject
            var svginterval = svgwidth/(Bars.elements.chartController.plotObject.attributes.xmax-Bars.elements.chartController.plotObject.attributes.xmin) //number of pixels between whole numbers in coordinate plot
            Bars.events.dragE(d, i, 
            d3.select(this)
                .attr("x", function(d, i){
                    if (d3.event.x < 0) {return 0}
                    else if (d3.event.x + returnw(d,i) > svgwidth){ return (svgwidth - returnw(d,i))}
                    else {return xs(Math.floor(d3.event.x/svginterval)) } })); //this function ensures value will be a whole number         

            var valuechange = d3.event.x < 0 ? 0 : (d3.event.x + returnw(d,i) > svgwidth ? svgwidth - returnw(d,i) : d3.event.x);

            console.log("Start value changed to: ", Math.floor(valuechange/svginterval));
        }

        function dragfin(d){
            Bars.events.dragE(d, i, d3.select(this).attr('style', 'stroke: black; stroke-width: 0px;')
            .attr('fill', Bars.attributes.fill))

        }


        rects.attr("cursor", "ew-resize")
        .on('dragstart', function(d, i){
            Bars.events.dragE(d, i, d3.select(this).call(drag))
        })
        .on('drag', function (d, i) {
            window.dragThis = this;
            Bars.events.dragE(d, i, 
                d3.select(this).call(drag)) 
        })
        .on("dragend", function(d, i){
            Bars.events.dragE(d, i, d3.select(this).call(drag))

        })
    }

我对 Angular 和 Javascript/D3 还是有点陌生​​,所以我可能会遗漏一些非常基本的东西,但任何见解都将不胜感激!

【问题讨论】:

    标签: javascript angularjs d3.js


    【解决方案1】:

    我已经编写了您想要的条形图拖动行为的 d3v5,并且首先需要的任何选择都没有问题。我建议跳过 D3-Angular 接口并在 D3v5 中自己绘制 D3 的东西,方法是调用一个函数,该函数根据参数中提供的数据绘制图形。

    original bar graph 数据中,条形值是对象的frequency 字段。

    <!DOCTYPE html>
    <!-- based on bar chart https://bl.ocks.org/mbostock/3885304 -->
    <meta charset="utf-8">
    <style>
    .active {
      stroke: #000;
      stroke-width: 2px;
    }
    .axis--y path {
      display: none;
    }
    </style>
    <svg width="960" height="500"></svg>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <script>
    
    var svg = d3.select("svg"),
        margin = {top: 20, right: 20, bottom: 30, left: 40},
        width = +svg.attr("width") - margin.left - margin.right,
        height = +svg.attr("height") - margin.top - margin.bottom;
    
    var x = d3.scaleLinear().rangeRound([0, width]),
        y = d3.scaleBand().rangeRound([height, 0]).paddingInner(0.1);
    
    var g = svg.append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    var data = [
        {letter: 'A', x: 0, frequency: 1 },
        {letter: 'B', x: 0, frequency: 13 },
        {letter: 'C', x: 0, frequency: 6 },
        {letter: 'D', x: 0, frequency: 2 },
        {letter: 'E', x: 0, frequency: 5 },
        {letter: 'F', x: 0, frequency: 9 }
    ];
    
    var colorFx = function(d, i){
                      var colors = ['blue', 'green', 'yellow', 'purple', 'orange'];
                      return colors[Math.floor(Math.random()*colors.length)];
                  };
    
      x.domain([0, 7 + d3.max(data, function(d) { return d.frequency; })]);
      y.domain(data.map(function(d) { return d.letter; }));
    
    
      g.append("g")
          .attr("class", "axis axis--x")
          .attr("transform", "translate(0," + height + ")")
          .call(d3.axisBottom(x));
    
      g.append("g")
          .attr("class", "axis axis--y")
          .call(d3.axisLeft(y));
    
      g.selectAll(".bar")
        .data(data)
        .enter().append("rect")
          .attr("class", "bar")
          .attr("fill", colorFx)
          .attr("x", function(d) { return 0; })
          .attr("y", function(d) { return y(d.letter); })
          .attr("width", function(d) { return x(d.frequency); } )
          .attr("height", y.bandwidth() )
        .call(d3.drag()
          .on("start", dragstarted)
          .on("drag", dragged)
          .on("end", dragended));
    
    var prevDragPosition = { x: 0, y: 0};
    
    function dragstarted(d) {
      prevDragPosition.x = d3.event.x;
      prevDragPosition.y = d3.event.y;
      d3.select(this).classed("active", true);
    }
    
    function dragged(d) {
      // how much do we have to move the rect
      var deltaX = x.invert(d3.event.x) - x.invert(prevDragPosition.x);
      var node = d3.select(this);
      var dd = node.attr("x");
      var rectX = d.x + deltaX;
      // limit x position to the domain
      rectX = Math.max(x.domain()[0], rectX);
      rectX = Math.min(x.domain()[1]-d.frequency, rectX);
      d.x = rectX;
      node.attr("x", x(rectX));
      prevDragPosition.x = d3.event.x;
      prevDragPosition.y = d3.event.y;
    }
    
    function dragended(d, i) {
      d3.select(this).classed("active", false);
    }
    
    </script>

    【讨论】:

    • 通常我会同意你的看法,只使用 D3(并使用最新版本),但 Angular-D3 界面的主要目标是让构建交互式可视化对那些对 D3 不熟悉/不舒服。不过谢谢!
    • 我查看了您的 git 存储库,它非常难以阅读。我对 Angular-D3 的看法是,它是 D3 的某种包装器,只是语法不同,但并没有让它变得更容易。它只会混淆漂亮的 D3 API。在编写这些包装器调用之前,您需要了解 D3。
    猜你喜欢
    • 1970-01-01
    • 2022-08-04
    • 1970-01-01
    • 2015-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-13
    • 2013-02-27
    相关资源
    最近更新 更多