我在 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,这不是必需的。
这是由于“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>