Vue整合d3.v5.js制作--折线图(line)
先上效果图(x轴固定为时间轴):
图中出现的悬浮框是鼠标悬停效果
1、环境说明:
vue版本:"vue": "^2.5.2" d3版本:"d3": "^5.9.1"
2、Line.vue源码
1 <template> 2 <div class="d3line" :id="id"> 3 </div> 4 </template> 5 6 <script> 7 import * as d3 from 'd3' 8 export default { 9 name: 'd3line', 10 props: { 11 id: String, 12 width: Number, 13 height: Number, 14 dataset: Array 15 }, 16 mounted() { 17 this.init(); 18 }, 19 methods: { 20 init() { 21 d3.select("#svg" + this.id).remove(); 22 let width = this.width ? this.width : 600; 23 let height = this.height ? this.height : 600; 24 let padding = { 25 left: 80, 26 right: 50, 27 top: 50, 28 bottom: 50 29 } 30 let colorZ = d3.scaleOrdinal(d3.schemeDark2) 31 let parseTime = d3.timeParse("%Y-%m-%d") 32 let xScale = d3.scaleTime().range([0, width - padding.left - padding.right]) 33 let dates = this.dataset.flatMap((d) => d.value.map(v => parseTime(v.key))) 34 xScale.domain([d3.min(dates), d3.max(dates)]) 35 let yScale = d3.scaleLinear().range([height - padding.top - padding.bottom, 0]) 36 yScale.domain([0, d3.max(this.dataset.flatMap((d) => d.value.map( v => v.value) )) + 2]) 37 let xAxis = d3.axisBottom(xScale).tickFormat(d => d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate()) 38 let yAxis = d3.axisLeft(yScale) 39 let svg = d3.select("#" + this.id).append("svg").attr("width", width).attr("height", height).attr("id", "svg" + this.id); 40 svg.append('g') 41 .attr('transform', 'translate(' + padding.left + ',' + (height - padding.bottom) + ')') 42 .call(xAxis) 43 .selectAll('text') 44 .attr('dx', -20) 45 .attr('dy', 10) 46 .attr('transform', 'rotate(-20)') 47 .style('font-weight', 'bold') 48 svg.append('g') 49 .attr('transform', 'translate(' + padding.left + ',' + padding.top + ')') 50 .call(yAxis) 51 .selectAll('text') 52 .style('font-weight', 'bold') 53 let line = d3.line().x(d => xScale(parseTime(d.key))).y(d => yScale(d.value)) 54 this.dataset.forEach((v, vi) => { 55 let tp_x = 0, 56 tp_y =0; 57 svg.append("path") 58 .attr('d' , line(v.value)) 59 .attr('transform', 'translate(' + padding.left + ',' + padding.top + ')') 60 .attr('fill', 'none') 61 .attr('stroke', (d, i) => colorZ(vi)) 62 .attr("stroke-width", 2) 63 .style('stroke-dasharray', function(d, i) { 64 return d3.select(this).node().getTotalLength(); 65 }) 66 .style('stroke-dashoffset', function(d, i) { 67 return d3.select(this).node().getTotalLength(); 68 }) 69 .transition() 70 .duration(2000) 71 .ease(d3.easePolyOut) 72 .delay((d, i) => i * 200) 73 .style('stroke-dashoffset', 0) 74 svg.selectAll('circle1') 75 .data(v.value) 76 .enter() 77 .append('circle') 78 .attr('cx', (d, i) => { 79 let x = xScale(parseTime(d.key)) 80 if (i === v.value.length - 1) tp_x = x - 40 81 return x 82 }) 83 .attr('cy', (d, i) => { 84 let y = yScale(d.value) 85 if (i === v.value.length - 1) tp_y = y - 10 86 return y 87 }) 88 .attr('r', 2) 89 .attr('transform', 'translate(' + padding.left + ',' + padding.top + ')') 90 .style("fill", (d, i) => colorZ(vi)) 91 .on("mouseover", (d, i) => { 92 let g = svg.append('g') 93 .attr('id', `hoverg${vi}${d.key}${d.value}`) 94 .attr("transform", "translate(" + (xScale(parseTime(d.key)) - 20) + "," + (yScale(d.value) + 30) + ")" ) 95 g.append("rect") 96 .attr("x", function(d){ return this.parentNode.getBBox().x - 3;}) 97 .attr("y", function(d, i){ return this.parentNode.getBBox().y - 20}) 98 .attr("width", 110) 99 .attr("height", 25) 100 .style("fill", "#fffbf0") 101 g.append('text') 102 .text(`${d.key}:${d.value}`) 103 .style("fill", colorZ(vi)) 104 }) 105 .on("mouseout", (d) => d3.select(`#hoverg${vi}${d.key}${d.value}`).remove()) 106 .transition() 107 .duration(1500) 108 .ease(d3.easePolyIn) 109 .delay((d, i) => i * 200) 110 .attr('r', 5) 111 // svg.selectAll('text1') 112 // .data([v.name]) 113 // .enter() 114 // .append('text') 115 // .attr('dx', (d, i) => tp_x) 116 // .attr('dy', (d, i) => tp_y) 117 // .attr('transform', 'translate(' + padding.left + ',' + padding.top + ')') 118 // .text((d) => d) 119 // .style("fill", (d, i) => colorZ(vi)) 120 // .style('font-weight', 'bold') 121 svg.append('text') 122 .attr('dx', tp_x) 123 .attr('dy', tp_y) 124 .attr('transform', 'translate(' + padding.left + ',' + padding.top + ')') 125 .text(v.name) 126 .style("fill", colorZ(vi)) 127 .style('font-weight', 'bold') 128 }) 129 } 130 }, 131 watch: { 132 dataset() { 133 this.init(); 134 } 135 } 136 } 137 </script> 138 139 <style> 140 141 </style>
3、使用示例
1 <template> 2 <div id="test"> 3 <!-- 一定要传进去一个id,随便传一个 --> 4 <d3line id="line" :dataset="data1"></d3line> 5 </div> 6 </template> 7 8 <script> 9 import d3line from '@/components/d3/Line' 10 export default { 11 name: 'test', 12 data() { 13 return { 14 data1: [ 15 { 16 'name': '哈尔滨', 17 'value': [{key: '2015-1-1', value: 10}, {key: '2015-1-2', value: 12}, {key: '2015-1-3', value: 13}, {key: '2015-1-17', value: 17}] 18 }, 19 { 20 'name': '海南', 21 'value': [{key: '2015-1-1', value: 9}, {key: '2015-1-2', value: 48}, {key: '2015-1-3', value: 5}, {key: '2015-1-17', value: 49}] 22 }, 23 { 24 'name': '天津', 25 'value': [{key: '2015-1-2', value: 30}, {key: '2015-1-3', value: 1}, {key: '2015-1-4', value: 32}, {key: '2015-1-5', value: 10}] 26 } 27 ] 28 } 29 }, 30 components: { 31 d3line 32 }, 33 methods: { 34 35 } 36 } 37 </script> 38 39 <style scoped> 40 </style>
4、参考资料
https://github.com/d3/d3-scale-chromatic/blob/master/README.md#schemeCategory10
https://github.com/d3/d3-time-format#format
https://jakearchibald.com/2013/animated-line-drawing-svg/