动态更新Snap.svg仪表图表

时间:2016-04-25 21:10:14

标签: javascript charts snap.svg

我在Codepen上找到了一些代码表(代码:Henry Poydar),它使用snap.svg创建动画规范图表。



angular.module('app', []);

angular.module('app')
  .controller('metricsCtrl', function($scope) {


    $scope.percentage = .8;


    var polar_to_cartesian, svg_circle_arc_path, animate_arc;

    polar_to_cartesian = function(cx, cy, radius, angle) {
      var radians;
      radians = (angle - 90) * Math.PI / 180.0;
      return [Math.round((cx + (radius * Math.cos(radians))) * 100) / 100, Math.round((cy + (radius * Math.sin(radians))) * 100) / 100];
    };

    svg_circle_arc_path = function(x, y, radius, start_angle, end_angle) {
      var end_xy, start_xy;
      start_xy = polar_to_cartesian(x, y, radius, end_angle);
      end_xy = polar_to_cartesian(x, y, radius, start_angle);

      return "M " + start_xy[0] + " " + start_xy[1] + " A " + radius + " " + radius + " 0 0 0 " + end_xy[0] + " " + end_xy[1];
    };

    animate_arc = function(ratio, svg, perc) {
      var arc;
      arc = svg.path('');

      return Snap.animate(0, ratio, (function(val) {
        var path;
        arc.remove();

        path = svg_circle_arc_path(500, 500, 450, -90, val * 180.0 - 90);
        arc = svg.path(path);
        arc.attr({
          class: 'data-arc'
        });
        perc.text(Math.round(val * 100) + '%');
      }), Math.round(2000 * ratio), mina.easeinout);
    };

    $scope.$watch('percentage', function() {

      $('.metric').each(function() {
        var ratio, svg, perc;
        //ratio = $(this).data('ratio');
        ratio = $scope.percentage;

        svg = Snap($(this).find('svg')[0]);
        perc = $(this).find('text.percentage');
        animate_arc(ratio, svg, perc);
      });
    });

  });

.metric {
  padding: 10%;
}
.metric svg {
  max-width: 100%;
}
.metric path {
  stroke-width: 75;
  stroke: #ecf0f1;
  fill: none;
}
.metric path.data-arc {
  stroke: #3498db;
}
.metric text {
  fill: #3498db;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script>


<div ng-app="app" ng-controller="metricsCtrl">
  <div class="metric">
    <svg viewBox="0 0 1000 500">
      <path d="M 950 500 A 450 450 0 0 0 50 500"></path>
      <text class='percentage' text-anchor="middle" alignment-baseline="middle" x="500" y="300" font-size="140" font-weight="bold">0%
      </text>
      <text class='title' text-anchor="middle" alignment-baseline="middle" x="500" y="450" font-size="90" font-weight="normal">Empty
      </text>
    </svg>
  </div>

  <input ng-model="percentage">
</div>
&#13;
&#13;
&#13;

我希望能够动态更新图表数据并相应地进行SVG渲染。我能够让图表显示增加值,但价值下降不起作用。这是一个重现我的问题http://codepen.io/EvanWieland/pen/bpxqpV的演示。在演示中,如果您在图表下方的输入中增加值然后减少它,您将能够观察到我的困境。请注意,演示使用Angularjs,这不是必需的。先谢谢!

1 个答案:

答案 0 :(得分:1)

这是由于“svg.path(path)”每次创建一个新的弧,因此减少的值会绘制一个前一个隐藏的弧。解决方案是在每次重绘时删除先前的弧。

angular.module('app', []);

angular.module('app')
  .controller('metricsCtrl', function($scope) {


    $scope.percentage = .8;


    var polar_to_cartesian, svg_circle_arc_path, animate_arc;

    polar_to_cartesian = function(cx, cy, radius, angle) {
      var radians;
      radians = (angle - 90) * Math.PI / 180.0;
      return [Math.round((cx + (radius * Math.cos(radians))) * 100) / 100, Math.round((cy + (radius * Math.sin(radians))) * 100) / 100];
    };

    svg_circle_arc_path = function(x, y, radius, start_angle, end_angle) {
      var end_xy, start_xy;
      start_xy = polar_to_cartesian(x, y, radius, end_angle);
      end_xy = polar_to_cartesian(x, y, radius, start_angle);

      return "M " + start_xy[0] + " " + start_xy[1] + " A " + radius + " " + radius + " 0 0 0 " + end_xy[0] + " " + end_xy[1];
    };

    animate_arc = function(ratio, svg, perc) {
      var arc;
      arc = svg.path('');

      return Snap.animate(0, ratio, (function(val) {
        var path;
        arc.remove();

        path = svg_circle_arc_path(500, 500, 450, -90, val * 180.0 - 90);
        var previousArc = svg.select('.data-arc')
        if (previousArc){
            previousArc.remove(); // REMOVES PREVIOUS ARC
        }
        arc = svg.path(path);
        arc.attr({
          class: 'data-arc'
        });
        perc.text(Math.round(val * 100) + '%');
      }), Math.round(2000 * ratio), mina.easeinout);
    };

    $scope.$watch('percentage', function() {

      $('.metric').each(function() {
        var ratio, svg, perc;
        //ratio = $(this).data('ratio');
        ratio = $scope.percentage;

        svg = Snap($(this).find('svg')[0]);
        perc = $(this).find('text.percentage');
        animate_arc(ratio, svg, perc);
      });
    });

  });
.metric {
  padding: 10%;
}
.metric svg {
  max-width: 100%;
}
.metric path {
  stroke-width: 75;
  stroke: #ecf0f1;
  fill: none;
}
.metric path.data-arc {
  stroke: #3498db;
}
.metric text {
  fill: #3498db;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js"></script>


<div ng-app="app" ng-controller="metricsCtrl">
  <div class="metric">
    <svg viewBox="0 0 1000 500">
      <path d="M 950 500 A 450 450 0 0 0 50 500"></path>
      <text class='percentage' text-anchor="middle" alignment-baseline="middle" x="500" y="300" font-size="140" font-weight="bold">0%
      </text>
      <text class='title' text-anchor="middle" alignment-baseline="middle" x="500" y="450" font-size="90" font-weight="normal">Empty
      </text>
    </svg>
  </div>

  <input ng-model="percentage">
</div>