问题
I have the following arc diagram:
I would like to add a gradient to each of the individual arcs, that flows from the outer radius to the inner radius of each individual arc.
I am guessing that I will need to create an individual gradient for each arc?
let radius = 100;
for(let i = 0; i < 5; i ++) {
let grad = defs.append('radialGradient')
.attr('id', 'mygrad' + i)
.attr('gradientUnits', 'userSpaceOnUse')
.attr('cx', '0')
.attr('cy', '0')
.attr('r', radius)
grad.append('stop')
.attr('offset', '0%')
.attr('stop-color', 'white');
grad.append('stop')
.attr('offset', '100%')
.attr('stop-color', 'blue');
let arc = d3.arc()
.outerRadius( radius )
.innerRadius( radius - 50)
.startAngle( degToRad(-90) )
.endAngle( degToRad(90) );
g.append('path')
.attr('d', arc)
.attr('fill', `url(#${'mygrad' + i })`)
.attr('stroke', 'lightgrey');
radius += 50;
}
let grad = defs.append('radialGradient')
.attr('id', 'mygrad' + i)
.attr('r', '80%')
grad.append('stop')
.attr('offset', '0%')
.attr('stop-color', 'white');
grad.append('stop')
.attr('offset', '100%')
.attr('stop-color', 'blue');
Codepen
回答1:
SVG 2 adds a fr attribute. That makes it possible to define an inner start radius for a gradient. In conjunction with the possibility to quote gradient definitions within other definitiions, your code can be rewritten as
let width = 800;
let height = 800;
let svg = d3.select('svg')
.attr('width', width)
.attr('height', height);
let defs = svg.append('defs');
let g = svg.append('g')
.attr('transform', `translate(${width/2}, ${height/2})`);
let radius = 100;
let gradcolors = defs.append('radialGradient')
.attr('id', 'gradcolors')
.attr('gradientUnits', 'userSpaceOnUse')
.attr('cx', '0')
.attr('cy', '0')
gradcolors.append('stop')
.attr('offset', '0%')
.attr('stop-color', 'white');
gradcolors.append('stop')
.attr('offset', '100%')
.attr('stop-color', 'blue');
for(let i = 0; i < 5; i++) {
let grad = defs.append('radialGradient')
.attr('id', 'mygrad' + i)
.attr('href', '#gradcolors')
.attr('fr', radius - 50)
.attr('r', radius)
let arc = d3.arc()
.outerRadius( radius )
.innerRadius( radius - 50)
.startAngle( -90 * (Math.PI / 180) )
.endAngle( 90 * (Math.PI / 180) );
g.append('path')
.attr('d', arc)
.attr('fill', `url(#${'mygrad' + i })`)
.attr('stroke', 'lightgrey');
radius += 50;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg> </svg>
Or you use the spreadMethod="repeat" attribute to define only one gradient with infinitely repeating colors.
let width = 800;
let height = 800;
let svg = d3.select('svg')
.attr('width', width)
.attr('height', height);
let defs = svg.append('defs');
let g = svg.append('g')
.attr('transform', `translate(${width/2}, ${height/2})`);
let radius = 100;
let radiusWidth = 50;
let grad = defs.append('radialGradient')
.attr('id', 'mygrad')
.attr('gradientUnits', 'userSpaceOnUse')
.attr('spreadMethod', 'repeat')
.attr('cx', '0')
.attr('cy', '0')
.attr('r', radiusWidth)
grad.append('stop')
.attr('offset', '0%')
.attr('stop-color', 'white');
grad.append('stop')
.attr('offset', '100%')
.attr('stop-color', 'blue');
for(let i = 0; i < 5; i++) {
let arc = d3.arc()
.outerRadius( radius )
.innerRadius( radius - 50)
.startAngle( -90 * (Math.PI / 180) )
.endAngle( 90 * (Math.PI / 180) );
g.append('path')
.attr('d', arc)
.attr('fill', 'url(#mygrad')
.attr('stroke', 'lightgrey');
radius += radiusWidth;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg> </svg>
回答2:
You need to work in UserSpace Coordinates because the object bounding box is not a nice square, you will get a distorted gradient. Specify the end-radius
of the gradient (r
).
Specify the white stop in percent relative to the end-radius
of the gradient (parameter r
)
grad.append('stop')
.attr('offset', `${100*(radius-50)/radius}%`)
.attr('stop-color', 'white');
let width = 800;
let height = 800;
let svg = d3.select('svg')
.attr('width', width)
.attr('height', height);
let defs = svg.append('defs');
let g = svg.append('g')
.attr('transform', `translate(${width/2}, ${height/2})`);
let radius = 100;
for(let i = 0; i < 5; i ++) {
let grad = defs.append('radialGradient')
.attr('id', 'mygrad' + i)
.attr('gradientUnits', 'userSpaceOnUse')
.attr('cx', 0)
.attr('cy', 0)
.attr('r', radius);
grad.append('stop')
.attr('offset', `${100*(radius-50)/radius}%`)
.attr('stop-color', 'white');
grad.append('stop')
.attr('offset', '100%')
.attr('stop-color', 'blue');
let arc = d3.arc()
.outerRadius( radius )
.innerRadius( radius - 50)
.startAngle( degToRad(-90) )
.endAngle( degToRad(90) );
g.append('path')
.attr('d', arc)
.attr('fill', `url(#${'mygrad' + i })`)
.attr('stroke', 'lightgrey');
radius += 50;
}
//helpers
function degToRad( deg ) {
return deg * (Math.PI / 180);
}
<svg></svg>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
来源:https://stackoverflow.com/questions/53851904/radial-gradients-for-multiple-arcs-in-d3