<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <style> polyline { opacity: .3; stroke: black; stroke-width: 2px; fill: none; } .tooltip{ position: absolute; width: 120px; min-height: 50px; font-size: 14px; text-align: center; border-style: solid; border-width: 1px; border-color: #666; background:rgba(0,0,0,.5); color:white; border-radius: 5px; } </style></head><body><svg width="100%" height="500"></svg><script src="d3.min.js" charset="utf-8"></script><script> const json = [ {name: '購物', value: 983}, {name: '平常', value: 300}, {name: '醫藥', value: 140}, {name: '交通', value: 402}, {name: '雜費', value: 134} ]; class ReactPie { constructor(props){ this.width = 600; this.height = 300; this.radius = 100; this.pie = d3.layout.pie().value(d =>d.value); this.svg = d3.select('svg').append('g').attr("transform",`translate(${this.width/2} ${this.height/2})`); this.arc = d3.svg.arc().innerRadius(this.radius).outerRadius('10'); this.outerArc = d3.svg.arc().innerRadius(1.2 * this.radius).outerRadius(1.2 * this.radius); this.oArc = d3.svg.arc().innerRadius(1.1 * this.radius).outerRadius(1.1 * this.radius); this.color = d3.scale.category10(); this.tooltip = d3.select('body').append('div').attr('class','tooltip').style('opacity','0') }; init(){ this.drawMap(json); this.drawLine(json); this.drawText(json); } drawMap(json){ const pieData = this.pie(json); const _this = this; this.svg.append('g').attr('class', 'slices').selectAll("g") .data(pieData) .enter() .append('path') .attr('fill',(d,i)=>this.color(i)) .attr('d',d=>this.arc(d)) .on('mouseover',function(d){ _this.tooltip.html(`${d.data.name}:${d.data.value}`) .style('left',`${d3.event.pageX}px`) .style('top',`${d3.event.pageY-60}px`) .style('opacity',1); d3.select(this) .transition() .duration(300) .ease('bounce') .style('box-shadow','0 0 5px #000') .attr('transform',()=>`scale(1.1)`) }) .on('mousemove',d =>{ _this.tooltip.html(`${d.data.name}:${d.data.value}`) .style('left',`${d3.event.pageX}px`) .style('top',`${d3.event.pageY-60}px`) .style('opacity',1); }) .on('mouseout',function (){ _this.tooltip.style('opacity',0); d3.select(this) .transition() .duration(300) .ease('bounce') .attr('transform',()=>`scale(1)`) }); } drawLine(json){ const pieData = this.pie(json); this.svg.append('g') .attr('class','line') .selectAll('polyline') .data(pieData) .enter() .append('polyline') .attr('points', d =>[this.arc.centroid(d), this.arc.centroid(d), this.arc.centroid(d)]) .attr('points',(d) =>{ const pos = this.outerArc.centroid(d); pos[0] = this.radius * (this.midAngel(d)<Math.PI ? 1.5 : -1.5); return [this.oArc.centroid(d), this.outerArc.centroid(d), pos]; }) } drawText(json){ const pieData = this.pie(json); this.svg.append('g') .attr('class','text') .selectAll('text') .data(pieData) .enter() .append('text') .attr('dy','0.35em') .attr('fill', (d, i)=>this.color(i)) .text(d =>d.data.name) .style('text-anchor', d =>this.midAngel(d)<Math.PI ? 'start' : 'end') .attr('transform', (d) => { // 找出外弧形的中心點 const pos = this.outerArc.centroid(d); // 改變文字標識的x座標 pos[0] = this.radius * (this.midAngel(d)<Math.PI ? 1.6 : -1.6); return 'translate(' + pos + ')'; }) .style('opacity', 1); } midAngel(d){ return d.startAngle + (d.endAngle - d.startAngle)/2; } } const a = new ReactPie().init(json);</script></body></html>