我只是从javascript和d3.js v3开始。我制作了一个甜甜圈图,希望它使用来自Google API的JSON数据。
应该使用JSON响应中的“得分”数据更新HTML中的data-value
属性。
我知道可以使用d3.json(url, callback);
提取JSON数据,但是我不确定如何正确设置它。
以及如何更改代码,以便在初始化图表之前更新data-value
?
HTML:
<div class="ui">
<ul class="ui__downloadList">
<li class="ui__downloadList__item red" id="red" data-value=25 data-type=performance>
<div class="ui__downloadList__graphic" width=154 height=160></div>
<h2 class="ui__downloadList__headline">Performance</h2>
<h3 class="ui__downloadList__subHeadline">120 Files</h3>
<li class="ui__downloadList__item purple" id="purple" data-value=50 data-type=accessibility>
<div class="ui__downloadList__graphic" width=154 height=160></div>
<h2 class="ui__downloadList__headline">Accessibility</h2>
<h3 class="ui__downloadList__subHeadline">65 Files</h3>
<li class="ui__downloadList__item cyan" id="cyan" data-value=75 data-type=best-practices>
<div class="ui__downloadList__graphic" width=154 height=160></div>
<h2 class="ui__downloadList__headline">Best-Practices</h2>
<h3 class="ui__downloadList__subHeadline">98 Files</h3>
</ul>
</div>
API的JSON示例:
{"lighthouseResult":{"categories":{"performance":{"score":1.0},"accessibility":{"score":0.9},"best-practices":{"score":0.92}}}}
Javascript:
;( function( d3 ) {
/**
* Function to kick of all fancy download circles
*/
function visualizeDownloadCircles ( selector ) {
var d3items = d3.selectAll( selector ),
d3graphics = d3items.select( '.ui__downloadList__graphic' ),
svgElements;
// remove old svg for the case there was one
d3graphics.select( 'svg' ).remove();
d3items.each( function( svg, index ) {
var d3item = d3.select( this );
index++;
drawDownloadCircle( d3item, index );
} );
}
/**
* Function to draw one particular
* download circle
*/
function drawDownloadCircle( d3item, index ) {
var d3container = d3item.select( '.ui__downloadList__graphic' ),
value = d3item.attr( 'data-value' ),
type = d3item.attr( 'data-type' ),
width = d3container.attr( 'width' ),
height = d3container.attr( 'height' ),
// circle stuff
twoPi = 2 * Math.PI,
radius = Math.min( width, height ) / 2 - 5,
arcBackground = d3.svg.arc()
.startAngle( 0 )
.endAngle( function( d ) { return d.value * twoPi; } )
.outerRadius( radius - 10 )
.innerRadius( radius - 35 ),
arcForeground = d3.svg.arc()
.startAngle( 0 )
.endAngle( function( d ) { return d.value * twoPi; } )
.outerRadius( radius )
.innerRadius( radius - 27 ),
// value stuff
currentValue = 0,
progress = 0,
// animation stuff,
duration = 3000;
// string to number
value = +value;
// append new svg
var svg = d3container.append( 'svg' )
.attr( 'width', width )
.attr( 'height', height )
.append( 'g' )
.attr(
'transform',
'translate(' + width / 2 + ',' + height / 2 + ')'
);
// filter stuff
/* For the drop shadow filter... */
var defs = svg.append( 'defs' );
var filter = defs.append( 'filter' )
.attr( 'id', 'dropshadow' )
filter.append( 'feGaussianBlur' )
.attr( 'in', 'SourceAlpha' )
.attr( 'stdDeviation', 2 )
.attr( 'result', 'blur' );
filter.append( 'feOffset' )
.attr( 'in', 'blur' )
.attr( 'dx', 2 )
.attr( 'dy', 3 )
.attr( 'result', 'offsetBlur' );
var feMerge = filter.append( 'feMerge' );
feMerge.append( 'feMergeNode' )
.attr( 'in", "offsetBlur' )
feMerge.append( 'feMergeNode' )
.attr( 'in', 'SourceGraphic' );
// end filter stuff
// gradient stuff
var gradientBackgroundRed = defs.append( 'linearGradient' )
.attr( 'id', 'gradientBackgroundRed' )
.attr( 'x1', '0' )
.attr( 'x2', '0' )
.attr( 'y1', '0' )
.attr( 'y2', '1' );
gradientBackgroundRed.append( 'stop' )
.attr( 'class', 'redBackgroundStop1' )
.attr( 'offset', '0%' );
gradientBackgroundRed.append( 'stop' )
.attr( 'class', 'redBackgroundStop2' )
.attr( 'offset', '100%' );
var gradientBackgroundPurple = defs.append( 'linearGradient' )
.attr( 'id', 'gradientBackgroundPurple' )
.attr( 'x1', '0' )
.attr( 'x2', '0' )
.attr( 'y1', '0' )
.attr( 'y2', '1' );
gradientBackgroundPurple.append( 'stop' )
.attr( 'class', 'purpleBackgroundStop1' )
.attr( 'offset', '0%' );
gradientBackgroundPurple.append( 'stop' )
.attr( 'class', 'purpleBackgroundStop2' )
.attr( 'offset', '100%' );
var gradientBackgroundCyan = defs.append( 'linearGradient' )
.attr( 'id', 'gradientBackgroundCyan' )
.attr( 'x1', '0' )
.attr( 'x2', '0' )
.attr( 'y1', '0' )
.attr( 'y2', '1' );
gradientBackgroundCyan.append( 'stop' )
.attr( 'class', 'cyanBackgroundStop1' )
.attr( 'offset', '0%' );
gradientBackgroundCyan.append( 'stop' )
.attr( 'class', 'cyanBackgroundStop2' )
.attr( 'offset', '100%' );
var gradientForegroundRed = defs.append( 'linearGradient' )
.attr( 'id', 'gradientForegroundRed' )
.attr( 'x1', '0' )
.attr( 'x2', '0' )
.attr( 'y1', '0' )
.attr( 'y2', '1' );
gradientForegroundRed.append( 'stop' )
.attr( 'class', 'redForegroundStop1' )
.attr( 'offset', '0%' );
gradientForegroundRed.append( 'stop' )
.attr( 'class', 'redForegroundStop2' )
.attr( 'offset', '100%' );
var gradientForegroundPurple = defs.append( 'linearGradient' )
.attr( 'id', 'gradientForegroundPurple' )
.attr( 'x1', '0' )
.attr( 'x2', '0' )
.attr( 'y1', '0' )
.attr( 'y2', '1' );
gradientForegroundPurple.append( 'stop' )
.attr( 'class', 'purpleForegroundStop1' )
.attr( 'offset', '0%' );
gradientForegroundPurple.append( 'stop' )
.attr( 'class', 'purpleForegroundStop2' )
.attr( 'offset', '100%' );
var gradientForegroundCyan = defs.append( 'linearGradient' )
.attr( 'id', 'gradientForegroundCyan' )
.attr( 'x1', '0' )
.attr( 'x2', '0' )
.attr( 'y1', '0' )
.attr( 'y2', '1' );
gradientForegroundCyan.append( 'stop' )
.attr( 'class', 'cyanForegroundStop1' )
.attr( 'offset', '0%' );
gradientForegroundCyan.append( 'stop' )
.attr( 'class', 'cyanForegroundStop2' )
.attr( 'offset', '100%' );
// end gradient stuff
var meter = svg.append( 'g' )
.attr( 'class', 'progress-meter' );
meter.append( 'title' )
.text( 'Progress meter showing amount of space used for ' + type );
meter.data(
[
{ value : .0, index : .5 }
]
)
.append( 'path' )
.attr( 'class', 'ui__downloadList__backgroundCircle' )
.attr( 'd', arcBackground )
.attr( 'filter', 'url(#dropshadow)' )
.transition()
.duration( duration )
.attrTween( 'd', tweenArcBackground( { value : 1 } ) );
var foreground = meter.data(
[
{ value : .0, index: .5 }
]
)
.append( 'path' )
.attr( 'stroke', '#fff' )
.attr( 'class', 'ui__downloadList__foregroundCircle' )
.attr( 'd', arcForeground )
.attr( 'filter', 'url(#dropshadow)' )
.transition()
.attr( 'stroke', '#aaa' )
.delay( 100 * index )
.duration( duration )
.attrTween( 'd', tweenArcForeground({ value : value / 100 } ) );
meter.data( [ 0 ] )
.append( 'text' )
.text ( 0 )
.attr( 'font-size', '25px' )
.attr( 'x', 0 )
.attr( 'y', 0 )
.attr( 'fill', '#fff' )
.attr( 'text-anchor', 'middle' )
.attr( 'filter', 'url(#dropshadow)' )
.transition()
.delay( 100 * index )
.duration( duration )
.tween( 'text', tweenText( value ) );
meter.append( 'text' )
.attr( 'fill', '#fff' )
.attr( 'x', 0 )
.attr( 'y', 20 )
.attr( 'text-anchor', 'middle' )
.attr( 'filter', 'url(#dropshadow)' )
.text( '%' );
// Helper functions!!!
function tweenArcForeground( b ) {
return function( a ) {
var i = d3.interpolate( a, b );
return function( t ) {
return arcForeground( i ( t ) );
};
};
}
function tweenArcBackground( b ) {
return function( a ) {
var i = d3.interpolate( a, b );
return function( t ) {
return arcBackground( i ( t ) );
};
};
}
function tweenText( b ) {
return function( a ) {
var i = d3.interpolateRound( a, b );
return function(t) {
this.textContent = i(t);
};
}
}
}
function kickoff() {
visualizeDownloadCircles( '.ui__downloadList__item' );
}
// yeah, let's kick things off!!!
kickoff();
} )( d3 );