动态更新Snap.svg仪表图表

问题描述 投票:0回答:1

我在 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>

我希望能够动态更新图表的数据并相应地渲染 SVG。我可以让图表显示增加的值,但减少值不起作用。

这是一个重现我的问题的演示:http://codepen.io/EvanWieland/pen/bpxqpV。在演示中,如果您增加图表下方输入的值然后减少它,您将能够观察到我的困境。请注意,该演示使用 Angularjs,这不是必需的。

javascript charts snap.svg
1个回答
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>

© www.soinside.com 2019 - 2024. All rights reserved.