Built with blockbuilder.org
forked from cdagli's block: fresh block
forked from cdagli's block: Scroll Bar Chart (Using Zoom)
forked from cdagli's block: Smaller Version
forked from cdagli's block: Smaller Version (Less Data)
| license: mit |
Built with blockbuilder.org
forked from cdagli's block: fresh block
forked from cdagli's block: Scroll Bar Chart (Using Zoom)
forked from cdagli's block: Smaller Version
forked from cdagli's block: Smaller Version (Less Data)
| <!DOCTYPE html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <script src="http://d3js.org/d3.v3.min.js"></script> | |
| <style> | |
| .bar { | |
| fill: steelblue; | |
| } | |
| .subBar{ | |
| fill: steelblue; | |
| } | |
| .axis text { | |
| font: 10px sans-serif; | |
| user-select: none; | |
| } | |
| .axis path, | |
| .axis line { | |
| fill: none; | |
| stroke: #000; | |
| shape-rendering: crispEdges; | |
| } | |
| .x.axis path { | |
| display: none; | |
| } | |
| rect.mover { | |
| fill: lightSteelBlue; | |
| fill-opacity: .5; | |
| } | |
| .brush .extent { | |
| stroke: #fff; | |
| fill-opacity: .125; | |
| shape-rendering: crispEdges; | |
| } | |
| .tooltip { | |
| position: absolute; | |
| pointer-events: none; | |
| padding: 12px; | |
| background: white; | |
| border: 1px solid gray; | |
| border-radius: 4px; | |
| } | |
| .tooltip-name | |
| { | |
| text-align: center; | |
| color: steelblue; | |
| } | |
| .tooltip-value | |
| { | |
| text-align: left; | |
| margin-top: 5px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!DOCTYPE html> | |
| <script> | |
| var DATA_COUNT = 66; | |
| var MAX_LABEL_LENGTH = 50; | |
| var MIN_LABEL_LENGTH = 50; | |
| var MAX_LABEL_LENGTH_ALLOWED = 27; | |
| var MARGIN_MODIFIER_CONSTANT = 4; | |
| var BAR_WIDTH = 20; | |
| var BAR_PADDING = 5; | |
| var data = []; | |
| for (var i = 0; i < DATA_COUNT; i++) { | |
| var datum = {}; | |
| var plusOrMinus = Math.random() < 0.5 ? -1 : 1; | |
| datum.name = stringGen(MIN_LABEL_LENGTH, MAX_LABEL_LENGTH); | |
| datum.value = Math.floor(Math.random() * 600 * plusOrMinus); | |
| data.push(datum); | |
| } | |
| function stringGen(minLength, maxLength) { | |
| var text = ""; | |
| var charset = "abcdefghijklmnopqrstuvwxyz0123456789"; | |
| for (var i = 0; i < getRandomArbitrary(minLength, maxLength); i++) { | |
| text += charset.charAt(Math.floor(Math.random() * charset.length)); | |
| } | |
| return text; | |
| } | |
| function getRandomArbitrary(min, max) { | |
| return Math.round(Math.random() * (max - min) + min); | |
| } | |
| var maxLabelLength =d3.max(data.map(function(d){ return d.name.length})); | |
| var marginModifier = maxLabelLength < MAX_LABEL_LENGTH_ALLOWED ? maxLabelLength : MAX_LABEL_LENGTH_ALLOWED; | |
| var margin = { | |
| top: 50, | |
| right: 30, | |
| bottom: 40 + marginModifier * MARGIN_MODIFIER_CONSTANT, | |
| left: 80 | |
| }; | |
| var marginOverview = { | |
| top: 56 + marginModifier * MARGIN_MODIFIER_CONSTANT, | |
| right: 30, | |
| bottom: 0, | |
| left: 30 | |
| }; | |
| var width = 400 - margin.left - margin.right; | |
| var heightOverview = 50; | |
| var height = 350 - margin.top - margin.bottom; | |
| var barWidth = width / data.length; | |
| var overviewVisible = true; | |
| if (barWidth > BAR_WIDTH) { | |
| BAR_WIDTH = barWidth * 90 / 100; | |
| BAR_PADDING = barWidth * 10 / 100; | |
| overviewVisible = false; | |
| } | |
| var x = d3.scale.ordinal() | |
| .domain(data.map(function(d) { | |
| return d.name; | |
| })) | |
| .range(data.map(function(d, i) { | |
| return i * (BAR_WIDTH + BAR_PADDING); | |
| })); | |
| var y = d3.scale.linear() | |
| .domain([d3.min(data, function(d) { | |
| return d.value; | |
| }), Math.abs(d3.max(data, function(d) { | |
| return d.value; | |
| }))]) | |
| .range([height, 0]).nice(); | |
| var xAxis = d3.svg.axis() | |
| .scale(x) | |
| .orient("bottom") | |
| var yAxis = d3.svg.axis() | |
| .scale(y) | |
| .orient("left"); | |
| var svg = d3.select("body") | |
| .append("svg") | |
| .attr("width", width + margin.left + margin.right) | |
| .attr("height", height + margin.top + margin.bottom + heightOverview + marginOverview.top + marginOverview.bottom); | |
| var chart = svg.append("g") | |
| .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
| var defs = chart.append("defs"); | |
| defs.append("clipPath").attr('id', 'chart-clip-path').append('rect') | |
| .attr('width', width) //Set the width of the clipping area | |
| .attr('height', height); // set the height of the clipping area | |
| defs.append("clipPath").attr('id', 'x-axis-clip-path').append('rect') | |
| .attr('width', width) //Set the width of the clipping area | |
| .attr('height', height + margin.bottom); // set the height of the clipping area | |
| var barsGroup = chart.append('g'); | |
| barsGroup.attr('clip-path', 'url(#chart-clip-path)'); | |
| var xAxisGroup = chart.append("g").attr('class', 'x-axis') | |
| xAxisGroup.append('g') | |
| .attr("class", "x axis") | |
| .attr("transform", "translate(" + (BAR_WIDTH + BAR_PADDING) / 2 + "," + height + ")") | |
| .call(xAxis) | |
| .selectAll("text") | |
| .attr("y", 10) | |
| .attr("x", -10) | |
| .attr("dy", 0) | |
| .attr("transform", "rotate(-60)") | |
| .style("text-anchor", "end") | |
| .text(function (d) { | |
| if(d.length > MAX_LABEL_LENGTH_ALLOWED) | |
| return d.substring(0,MAX_LABEL_LENGTH_ALLOWED)+'...'; | |
| else | |
| return d; | |
| }); | |
| xAxisGroup.attr('clip-path', 'url(#x-axis-clip-path)'); | |
| var yAxisGroup = chart.append("g").attr("class", "y axis") | |
| yAxisGroup.call(yAxis); | |
| var div = d3.select("body") | |
| .append("div") | |
| .attr("class", "tooltip") | |
| .style("opacity", 0); | |
| var bars = barsGroup.selectAll(".bar") | |
| .data(data) | |
| .enter().append("rect") | |
| .attr("class", "bar") | |
| .attr("x", function(d) { | |
| return x(d.name); | |
| }) | |
| .attr("y", function(d) { | |
| return d.value > 0 ? y(d.value) : y(0); | |
| }) | |
| .attr("height", function(d) { | |
| return Math.abs(y(d.value) - y(0)); | |
| }) | |
| .attr("width", BAR_WIDTH) | |
| .on("mouseenter", showTooltip) | |
| .on("touchstart", showTooltip) | |
| .on("mouseleave", hideTooltip) | |
| .on("touchend", hideTooltip); | |
| var xAxisLabel = chart.append("text") | |
| .attr("text-anchor", "middle") // this makes it easy to centre the text as the transform is applied to the anchor | |
| .attr("transform", "translate("+ (width/2) +","+(height + marginOverview.top )+")") // centre below axis | |
| .text("Name"); | |
| var yAxisLabel = chart.append("text") | |
| .attr("text-anchor", "middle") // this makes it easy to centre the text as the transform is applied to the anchor | |
| .attr("transform", "translate("+ -margin.left / 2 +","+(height/2)+")rotate(-90)") // text is drawn off the screen top left, move down and out and rotate | |
| .text("Value"); | |
| if (overviewVisible) { | |
| var zoom = d3.behavior.zoom().scaleExtent([1, 1]); | |
| // var zoomRect = chart.append("rect") | |
| // .attr("width", width) | |
| // .attr("height", height) | |
| // .attr("fill", "none") | |
| // .attr("pointer-events", "all") | |
| // .call(zoom); | |
| var xOverview = d3.scale.ordinal() | |
| .domain(data.map(function(d) { | |
| return d.name; | |
| })) | |
| .rangeBands([0, width], BAR_PADDING / BAR_WIDTH, 0); | |
| var yOverview = d3.scale.linear().range([heightOverview, 0]); | |
| yOverview.domain(y.domain()); | |
| var overviewGroup = chart.append('g') | |
| .attr('width', width) | |
| .attr('height', heightOverview); | |
| var subBars = overviewGroup.append('g').selectAll('.subBar') | |
| .data(data) | |
| subBars.enter().append("rect") | |
| .classed('subBar', true) | |
| .attr({ | |
| height: function(d) { | |
| return Math.abs(yOverview(d.value) - yOverview(0)); | |
| }, | |
| width: function(d) { | |
| return xOverview.rangeBand() | |
| }, | |
| x: function(d) { | |
| return xOverview(d.name); | |
| }, | |
| y: function(d) { | |
| return height + marginOverview.top + (d.value > 0 ? yOverview(d.value) : yOverview(0)); | |
| } | |
| }); | |
| var overviewRect = overviewGroup.append('rect') | |
| .attr('y', height + marginOverview.top) | |
| .attr('width', width) | |
| .attr('height', heightOverview) | |
| .style("opacity", "0") | |
| .style("cursor", "pointer").on("click", click); | |
| var selectorWidth = (width / (BAR_WIDTH) * (xOverview.rangeBand())); | |
| var selector = chart.append("rect") | |
| .attr("class", "mover") | |
| .attr("x", 0) | |
| .attr("y", height + marginOverview.top) | |
| .attr("height", heightOverview) | |
| .attr("width", selectorWidth) | |
| .attr("pointer-events", "all") | |
| .attr("cursor", "ew-resize") | |
| .call(d3.behavior.drag().on("drag", drag)); | |
| } | |
| function showTooltip(data) | |
| { | |
| div.style("opacity", .9); | |
| div.html('<div class="tooltip-name">' + data.name + '</div><div class="tooltip-value">Value:' + data.value + '</div>') | |
| .style("left", (d3.event.pageX + 25) + "px") | |
| .style("top", (d3.event.pageY - 25) + "px"); | |
| } | |
| function hideTooltip(data) | |
| { | |
| div.style("opacity", 0); | |
| } | |
| function click() { | |
| var newX = null; | |
| var selectorX = null; | |
| var customScale = d3.scale.linear().domain([0, width]).range([0, ((BAR_WIDTH + BAR_PADDING) * data.length)]) | |
| selectorX = (d3.event.x - marginOverview.left) - selectorWidth / 2; | |
| newX = customScale(selectorX); | |
| if (selectorX > width - selectorWidth) { | |
| newX = customScale(width - selectorWidth); | |
| selectorX = width - selectorWidth; | |
| } else if (selectorX - (selectorWidth / 2) < 0) { | |
| newX = 0; | |
| selectorX = 0 | |
| } | |
| selector.transition().attr("x", selectorX) | |
| bars.transition().duration(300).attr("transform", "translate(" + (-newX) + ",0)"); | |
| chart.transition().duration(300).select(".x.axis").attr("transform", "translate(" + -(newX - (BAR_WIDTH + BAR_PADDING) / 2) + "," + (height) + ")"); | |
| chart.select(".y.axis").call(yAxis); | |
| var transformX = (-(d3.event.x - selectorWidth) * ((BAR_WIDTH + BAR_PADDING) * data.length) / width); | |
| zoom.translate([-newX, 0]) | |
| } | |
| // function zoomed() { | |
| // var t = zoom.translate(), | |
| // tx = t[0], | |
| // ty = t[1]; | |
| // var xEndValue = x(data[data.length - 1].name) - (width) + (BAR_WIDTH + BAR_PADDING) + BAR_WIDTH / 2; | |
| // if (tx <= 0 && tx > -xEndValue) { | |
| // bars.attr("transform", "translate(" + d3.event.translate[0] + ",0)"); | |
| // chart.select(".x.axis").attr("transform", "translate(" + (d3.event.translate[0] + (BAR_WIDTH + BAR_PADDING) / 2) + "," + (height) + ")"); | |
| // chart.select(".y.axis").call(yAxis); | |
| // var selectorX = Math.abs(width * tx / ((BAR_WIDTH + BAR_PADDING) * data.length)) | |
| // selector.attr("x", selectorX) | |
| // } | |
| // if (tx < -xEndValue) { | |
| // zoom.translate([-xEndValue, ty]) | |
| // } | |
| // if (tx >= 0) { | |
| // zoom.translate([0, ty]) | |
| // } | |
| // } | |
| function drag() { | |
| var nx = d3.event.dx; | |
| var t = zoom.translate(), | |
| tx = t[0], | |
| ty = t[1]; | |
| var selectorX = parseFloat(selector.attr("x")) + nx | |
| var customScale = d3.scale.linear().domain([0, width]).range([0, ((BAR_WIDTH + BAR_PADDING) * data.length)]) | |
| var transformX = customScale(selectorX) | |
| var xEndValue = customScale(xOverview(data[data.length - 1].name)) - customScale(selectorWidth) + BAR_WIDTH | |
| if (transformX < xEndValue && transformX >= 0) { | |
| selector.attr("x", selectorX) | |
| bars.attr("transform", "translate(" + -transformX + ",0)"); | |
| chart.select(".x.axis").attr("transform", "translate(" + (-transformX + (BAR_WIDTH + BAR_PADDING) / 2) + "," + (height) + ")"); | |
| chart.select(".y.axis").call(yAxis); | |
| zoom.translate([transformX, 0]) | |
| } | |
| } | |
| </script> | |
| </body> |