【问题标题】:Using d3.js svg clipPath not clipping in Angular使用 d3.js svg clipPath 不在 Angular 中剪辑
【发布时间】:2013-12-12 01:28:44
【问题描述】:

我正在开发一个与以下示例非常相似的图表: http://bl.ocks.org/mbostock/1667367 它使用 clipPath 来裁剪区域,因此区域图不会溢出轴。

在 Angular 指令中使用相同的示例,它几乎可以正常工作,但剪辑除外。使用焦点功能时,图表中的线条会超出剪辑矩形的范围。

但是 jsFiddle 中的相同代码可以正常工作http://jsfiddle.net/gserra/TXYGH/。指令中提到的代码如下:

angular.module('casApp.directives', []).
  directive('lineChart', ['d3', '_', 'moment', function (d3, _, moment) {
    var margin = {top: 10, right: 10, bottom: 100, left: 40};
    var margin2 = {top: 430, right: 10, bottom: 20, left: 40};

    return {
      restrict: 'E',
      replace: true,
      scope: {
        data: '=',
        stream: '=',
        start: '=',
        end: '=',
        period: '@',
        unit: '@',
        sensor: '@',
        width: '@',
        height: '@'
      },
      template: '<div class="line-chart"></div>',
      controller:'DataCtrl',
      link: function (scope, element) {
        var height = scope.height - margin.top - margin.bottom,
          height2 = scope.height - margin2.top - margin2.bottom,
          width = scope.width - margin.left - margin.right;

        scope.updateTime();

        var x = d3.time.scale()
          .domain([scope.start, scope.end])
          .range([0, width]);

        var x2 = d3.time.scale()
          .domain(x.domain())
          .range([0, width]);

        var min = d3.min(scope.data, function (d) {return d.value; })
        var max = d3.max(scope.data, function (d) {return d.value; })
        var thres = Math.abs(max-min);

        var y = d3.scale.linear()
          .domain([min - thres, max + thres])
          .range([height, 0]);

        var y2 = d3.scale.linear()
          .domain(y.domain())
          .range([height2, 0]);

        var line = d3.svg.line()
          .x(function (d) {
            return x(moment(d.at));
          })
          .y(function (d) {
            return y(d.value);
          });

        var line2 = d3.svg.line()
          .x(function (d) {
            return x2(moment(d.at));
          })
          .y(function (d) {
            return y2(d.value);
          });

        var graph = d3.select(element[0])
          .append('svg')
          .attr('width', scope.width)
          .attr('height', scope.height);

        var xAxis = d3.svg.axis().scale(x).orient('bottom'),
          xAxis2 = d3.svg.axis().scale(x2).orient('bottom'),
          yAxis = d3.svg.axis().scale(y).orient('left');

        var brush = d3.svg.brush()
          .x(x2)
          .on('brush', brushed);

        var focus = graph.append('g')
          .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

        var context = graph.append('g')
          .attr('transform', 'translate(' + margin2.left + ',' + margin2.top + ')');

        focus.append('defs').append('clipPath')
          .attr('id', 'clip')
          .append('rect')
          .attr('width', width)
          .attr('height', height);

        focus.append('g')
          .data([scope.data])
          .attr('clip-path', 'url(#clip)')
          .append('path')
          .attr('d', line)
          .attr('class', 'line');

        focus.append('g')
          .attr('class', 'x axis')
          .attr('transform', 'translate(0,' + height + ')')
          .call(xAxis);

        focus.append('g')
          .attr('class', 'y axis')
          .call(yAxis);

        context.append('path')
          .data([scope.data])
          .attr('d', line2)
          .attr('class', 'line');

        context.append('g')
          .attr('class', 'x axis')
          .attr('transform', 'translate(0,' + height2 + ')')
          .call(xAxis2);

        context.append('g')
          .attr('class', 'x brush')
          .call(brush)
          .selectAll('rect')
          .attr('y', -6)
          .attr('height', height2 + 7);

        function brushed() {
          x.domain(brush.empty() ? x2.domain() : brush.extent());
          focus.select('path.line').attr('d', line);
          focus.select('.x.axis').call(xAxis);
        }

        function updateGraph () {
           // update x axis
          x.domain([scope.start, scope.end]);
          x2.domain([scope.start, scope.end]);
          xAxis.scale(x);
          xAxis2.scale(x2);

          focus.selectAll('g.x.axis').call(xAxis);
          context.selectAll('g.x.axis').call(xAxis2);

          // update y axis
          var min = d3.min(scope.data, function (d) {return d.value; })
          var max = d3.max(scope.data, function (d) {return d.value; })
          var thres = Math.abs(max-min);

          y.domain([min - thres, max + thres]);
          y2.domain([min - thres, max + thres]);

          yAxis.scale(y);

          focus.selectAll('g.y.axis').call(yAxis);

          //update line
          focus.selectAll('path.line')
            .data([scope.data])
            .attr('d', line);

          context.selectAll('path.line')
            .data([scope.data])
            .attr('d', line2);
        }

        scope.$watch('data', function () {
          var last = scope.data.length > 0 ? moment(_.last(scope.data).at) : null;

          if (last && last > scope.end) {
            scope.updateTime(last, moment(last).add(scope.unit, scope.period));
            scope.data = [_.last(scope.data)];
          }
          updateGraph();
        });
      }
    };
  }]).

  directive('paginator', function () {
    return {
      controller: 'DataCtrl',
      restrict: 'E',
      template: '<button class="forward" ng-disabled="stream" ng-click="forward()">Page up</button>' +
                '<button class="back" ng-click="back()">Page down</button>'
    };
  });

图表会在控制器中使用 socket.io 自动更新。我尝试将defs附加到“svg”而不是“g”,但没有成功。有什么想法吗?

我正在使用 Angular 1.2.2

【问题讨论】:

  • 你验证过angular生成的SVG包含defs吗?
  • 是的,jsfiddle 中的代码是从角度输出中复制粘贴的。

标签: angularjs svg d3.js


【解决方案1】:

最后在另一个文件中分离代码并简化后,我发现了这种奇怪行为的原因:

&lt;head&gt;我有:

<head>
  <meta charset="utf8">
  <title>Real time context aware graphics display</title>
  <link rel="stylesheet" href="/stylesheets/app.css">
  <link rel="stylesheet" href="/stylesheets/graph.css"><base href="/">
  <base href="/">
  <style type="text/css"></style>
</head>

当我删除 &lt;base href="/"&gt; 时的广告一切正常。具体原因我也不知道。

【讨论】:

  • 您指定了一个相对 URL - url(#clip) 这被扩展为一个绝对 URL,并且基本指令改变了它的工作方式。
  • @RobertLongson 我的项目中需要&lt;base href="/"&gt;。我也想使用clipPaths。你会怎么解决这个问题?
  • 好的 :-) 问题是 here
  • 我使用的解决方法是在#clip 之前通过'url(' + $location.path() + '#clip)' 将相对URL 添加到控制器,或者如果您需要在html 中添加$scope.path = $location.path() 到控制器和clip-path="url({{path}}#clip)"到元素。
  • 对@morloch 的回答还有一点补充:如果你有一个“?”并在 url 中查询参数(也称为搜索值),您需要使用 $locaiton.url() 而不是 $location.path()。它看起来像这样: url($location.url() + #clip)
猜你喜欢
  • 1970-01-01
  • 2014-09-28
  • 2014-02-07
  • 2016-07-30
  • 2022-01-20
  • 2011-06-16
  • 1970-01-01
  • 2018-05-08
相关资源
最近更新 更多