// 此处利用闭环(Closure)定义一个可以重复利用的图表, // 使该类型图表模块化。 function TurncatingChart(){ // Default Value var margin = {top: 50, bottom: 50, left: 50, right: 50}, radius = 6, axisPadding = 0.5 * radius, svgWidth = 600, svgHeight = 200, xLabelName = '[undefined]', yLabelName = '[undefined]', xLabelToBottom = 10, yLabelToLeft = 20, xAxisTicks = 10, yAxisTicks = 10, brushHeight = 30, brushMargin = 25, brushColor = { background: '#ccc', extent: 'red', resize: 'yellow' }; // define a closure function // so it can access the previes properties function chart(selection){ selection.each(function(data){ // data extent var maxPosition = d3.max(data, function(d){ return d[1]; }), maxMutation = d3.max(data, function(d){ return d[2]; }); // updata data, such as axises and scale var graphWidth = svgWidth - (margin.left + margin.right), graphHeight = svgHeight - (margin.top + margin.bottom); var xScale = d3.scale.linear().nice() .domain([0, maxPosition]) .range([0, graphWidth]), yScale = d3.scale.linear().nice() .domain([0, maxMutation]) .range([graphHeight, 0]), dynamicScale = xScale.copy(), dynamicAxis = d3.svg.axis().orient('bottom') .ticks(xAxisTicks), yAxis = d3.svg.axis().orient('left') .scale(yScale) .ticks(yAxisTicks); // Tip block,position: absolute var tipsBox = d3.select(this).append('div') .attr('class', 'tooltip') .style('display', 'none'); // Start to draw var svgBody = d3.select(this) .append('svg') .attr('width', svgWidth) .attr('height', svgHeight); // divide the svg canvas into pices as we need var leftGroup = svgBody.append('g') .attr('class', 'y axis') .attr('transform', 'translate(' + [margin.left - axisPadding, margin.top] + ')'), bottomGroup = svgBody.append('g') .attr('class', 'x axis') .attr('transform', 'translate(' + [margin.left, margin.top + graphHeight + axisPadding] + ')'), // bottom and left axis data labels leftLabelGroup = svgBody.append('g') .attr('class', 'yLabel') .attr('transform', 'translate(' + [yLabelToLeft, margin.top + 0.5 * graphHeight] + ')'), bottomLabelGroup = svgBody.append('g') .attr('class', 'xLabel') .attr('transform', 'translate(' + [margin.left + 0.5 * graphWidth, svgHeight - xLabelToBottom] + ')'), // brush area brushGroup = svgBody.append('g') .attr('class', 'brush') .attr('transform', 'translate(' + [margin.left, margin.top + graphHeight + brushMargin] + ')'), // main drawing area mainGraph = svgBody.append('g') .attr('class', 'mainGraph') .attr('transform', 'translate(' + [margin.left, margin.top] + ')'); // generate yAxis leftGroup.call(yAxis).call(styleAxis); // styleAxis(leftGroup); // adding axis label leftLabelGroup.append('text') .style('text-anchor', 'middle') .attr('transform', 'rotate(-90)') .html(yLabelName); bottomLabelGroup.append('text') .style('text-anchor', 'middle') .html(xLabelName); // Main Drawing area // --------------------redraw------------------- function redraw(data){ // data range var xExtent = d3.extent(data, function(d){ return d[1]; }); dynamicScale.domain(xExtent); dynamicAxis.scale(dynamicScale); // clear data then rejection mainGraph.html(null) mainGraph.selectAll('g').data(data) .enter() .append('g') .each(function(d,i){ var sample = d3.select(this); sample.attr('transform', 'translate(' + [dynamicScale(d[1]), yScale(d[2])] + ')'); sample.append('line') .attr({ y2: graphHeight - yScale(d[2]), fill: null, stroke: 'steelblue', 'stroke-width': 1 }) .style('transition', 'all 0.2s'); sample.append('circle') .attr({ r: radius, fill: 'steelblue', 'stroke-width': 0 }) .style('transition', 'all 0.2s'); }); // dynamic scale and axis bottomGroup.call(dynamicAxis).call(styleAxis); // re-attach events mainGraph.selectAll('circle, line') .on('mouseover', mouseover) .on('mouseout', mouseout); } // -------------------redraw--------------------- // *******************brush code********************* var brush = d3.svg.brush() .x(xScale) .extent([maxPosition * 0.3, maxPosition * 0.8]) .on('brush', onBrush); var arcPath = d3.svg.arc() .outerRadius(brushHeight / 2) .startAngle(0) .endAngle(function(d,i){ return i ? -Math.PI : Math.PI; }); brushGroup .call(brush) .call(brush.event) .selectAll('rect') .attr('height', brushHeight); brushGroup.select('rect.background') .attr('fill', brushColor.background) .style('visibility', 'visible') .attr({rx: 0.5 * brushHeight, ry: 0.5 * brushHeight}); brushGroup.select('rect.extent') .attr('fill', brushColor.extent) brushGroup.selectAll('.resize') .append('path') .attr('d', arcPath) .attr({ fill: brushColor.resize, stroke: 'black', 'stroke-width': 1, transform: 'translate(0,' + brushHeight / 2 + ')' }); function onBrush(){ if(brush.empty()){ redraw(data); }else{ var ext = brush.extent(); var extData = data.filter(function(d){ return (d[1] <= ext[1]) && (d[1] >= ext[0]) }); redraw(extData); } }; // *******************brush code********************* // Event handlers for tip-div function mouseover(){ d3.select(this.parentNode).classed('active', true); var data = d3.select(this).data()[0]; var msg = 'Sample Name: ' + data[0] + '\n' + 'Position : ' + data[1] + '\n' + 'Turncating : ' + data[2] + '\n'; tipsBox .html('
'+ msg + '
') .style('left', (d3.event.pageX + 5) + 'px') .style('top', (d3.event.pageY - 15) + 'px') .style('display', 'inline'); }; function mouseout(){ d3.select(this.parentNode).classed('active', false); tipsBox.style('display', 'none'); }; function styleAxis(selection){ selection.selectAll('path, line') .attr({ stroke: '#555', 'stroke-width': 1, fill: 'none' }); selection.selectAll('text') .attr('fill', '#555'); } }); } // Setter-Getter function chart.margin = function(_){ if(!arguments.length) return margin; margin = _; return chart; }; chart.radius = function(_){ if(!arguments.length) return radius; radius = _; return chart; }; chart.axisPadding = function(_){ if(!arguments.length) return axisPadding; // min gap between main area and axis group is half radius if(_ < 0.5 * radius){ axisPadding = 0.5 * radius; return chart; } axisPadding = _; return chart; }; chart.svgWidth = function(_){ if(!arguments.length) return svgWidth; svgWidth = _; return chart; }; chart.svgHeight = function(_){ if(!arguments.length) return svgHeight; svgHeight = _; return chart; }; chart.xLabelName = function(_){ if(!arguments.length) return xLabelName; xLabelName = _; return chart; }; chart.yLabelName = function(_){ if(!arguments.length) return yLabelName; yLabelName = _; return chart; }; chart.xLabelToBottom = function(_){ if(!arguments.length) return xLabelToBottom; xLabelToBottom = _; return chart; }; chart.yLabelToLeft = function(_){ if(!arguments.length) return yLabelToLeft; yLabelToLeft = _; return chart; }; chart.xAxisTicks = function(_){ if(!arguments.length) return xAxisTicks; xAxisTicks = _; return chart; }; chart.yAxisTicks = function(_){ if(!arguments.length) return yAxisTicks; yAxisTicks = _; return chart; }; chart.brushHeight = function(_){ if(!arguments.length) return brushHeight; brushHeight = _; return chart; }; chart.brushMargin = function(_){ if(!arguments.length) return brushMargin; brushMargin = _; return chart; }; chart.brushColor = function(_){ if(!arguments.length) return brushColor; brushColor = _; return chart; }; // return my chart return chart; }