const height = 500; const width = 1000; const margin = 2; const gap = 5; // 矩形之间的间隔 const data = [8, 56, 76, 21, 46, 98, 40, 72, 54, 68, 100]; // 需要绘制的数据,初始时随机给一些值 const my_svg = d3.select('#my_svg') .style('margin-top', '10px') .attr('width', width) .attr('height', height); // 添加一个背景矩形以显示svg覆盖的范围 my_svg.append('rect') .attr('width', width) .attr('height', height) .attr('fill', 'grey') .attr('opacity', 0.2); // 在svg中添加一个元素,所有的矩形都画在该元素中 const g = my_svg.append('g'); const width_scale = d3.scaleLinear() .domain([0,100]) .range([0,width-2*margin]);// 设置一个线性比例尺,用来将数据值转变为矩形的宽度; update_bar_chart(data); function update_bar_chart(data) { const rect_height = (height-gap*(data.length-1))/data.length// 计算矩形在当前数据下的高度,注意考虑矩形间的缝隙gap; g.selectAll('rect') .data(data, d => d) .join( // 按照d3的enter、update、exit的模式复刻动图中的效果,为关键代码添加详细注释 enter => enter.append('rect') .attr('x', margin) .attr('y', height) .attr('stroke','none') .attr('width', d => width_scale(d)) .attr('height', rect_height) .attr('fill', 'blue') .attr('fill-opacity', 0.3) .on('mouseover',function(){ d3.select(this) .attr('stroke-width',2) .attr('stroke','red'); }).on('mouseout',function(){ d3.select(this).attr('stroke','none') }) .call(enter => enter.transition() .duration(750) .attr('fill', 'red') .attr("y", (d, i) => i * (rect_height+gap))), update => update .call(update => update.transition() .attr('height',rect_height) .attr('fill', 'green') .duration(750) .attr('y', (d, i) => i * (rect_height+gap))), exit => exit .call(exit => exit.transition() .duration(750) .attr('y', height) .remove()) ); } function sort_by_ascending() { data.sort(d3.ascending); update_bar_chart(data); } function shuffle() { d3.shuffle(data); update_bar_chart(data); } function delete_last_one() { data.pop(); update_bar_chart(data); } function add_one() { // 随机生成一个不在当前data数组中的数值 let random_num = Math.floor(d3.randomUniform(20, 100)()); while(data.indexOf(random_num) !== -1) { random_num = Math.floor(d3.randomUniform(20, 100)()); } data.push(random_num); update_bar_chart(data); }