Skip to content

Instantly share code, notes, and snippets.

@powersparks
Last active March 22, 2017 23:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save powersparks/146f356ecf8df48616542e45a1c8d132 to your computer and use it in GitHub Desktop.
Save powersparks/146f356ecf8df48616542e45a1c8d132 to your computer and use it in GitHub Desktop.
This corrects the problem with d3.js axis clipping
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.axis--grid .domain { fill: #ddd; stroke: none; }
.axis--grid .tick--minor line { stroke-opacity: .5; }
.axis--x .domain, .axis--grid .tick line { stroke: #333; }
rect.zoom{ cursor: move;; }
.axis--x, .axis--y, .tick, .domain, .axis--grid .tick { shape-rendering: crispEdges; border: thin; border-color:black; }
.brush .selection{ cursor: ew-resize; fill:rgba(0, 0, 0, 0.75); pointer-events: all;shape-rendering: crispEdges; border: thin; stroke:none; }
.brush .handle{ cursor: col-resize; fill: none; }
.line{ border: solid 1px steelblue; margin: 4px; padding: 4px; background-color: #eeeeec; fill: none;}
.timeline-container{ margin: 0px; padding: 0px ; border:1px solid blue ; }
.zoom { fill: none; pointer-events: all; }
</style>
<div id="timelineContainerDivId" width="100%" height="90px" class="timeline-container">
<svg id="timeFilterId" viewBox="0 0 800 80" preserveAspectRatio="xMidYMid"></svg>
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var divWidth = document.getElementById('timelineContainerDivId').clientWidth-10;
// 1) ESTABLISH LAYOUT SETTINGS
var svg = d3.select("svg").attr("viewBox","0 0 " + divWidth +" 80"),
h = 80, w = divWidth,
rwidth = svg.attr("width") ? svg.attr("width") : w,
rheight = svg.attr("height") ? svg.attr("height") : h,
margin = {top: 11, right: 20, bottom: 30, left: 40},
margin2 = {top: 60, right: 20, bottom: 20, left: 40},
width = w - margin.left - margin.right - 10,
height = h - margin.top - margin.bottom,
height2 = (svg.attr("height") ? + svg.attr("height") : h) - margin2.top - margin2.bottom;
//https://github.com/d3/d3-drag
//https://github.com/d3/d3-zoom
// 2) SCALE TIME RANGE on x and y
var x = d3.scaleTime().range([0, width]),
x2 = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
// 3) AXIS ORIENTATION and TICKS
var xAxis = d3.axisTop(x).tickSizeInner(height-5).tickSizeOuter( height).tickPadding(-height *0.2),
yAxis = d3.axisRight(y).ticks(0).tickSize(width);
// 4) INSTANTIATE d3 ZOOM
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
// 5) APPEND FOCUS g element as the TIMELINE
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// 5.1) APPEND rect to use as a zoom interface. (see 7.8) moved out of data block.
focus.append("rect")
.attr("class", "zoom ")
.attr("width", width)
.attr("height", height * 2)
.attr("transform", "translate(" + 0 + "," + 0 + ")")
// .call(drag)
// .call(brush_sensor)
//.call(brush_sensor.move, [width - Math.floor(width / 2) - ( width / 5), width - Math.floor( width / 2) + (width / 5)])
//on("drag", function() { d3.event.stopImmediatePropagation(); });
.call(zoom)
.on("wheel",function() { d3.event.preventDefault(); });
// 5.2) Setup Brush for time filter
var brush_sensor = d3.brushX()
.extent([[0, 0], [width, (height/2 + 5)]])
.on("brush end", focus_brush_feedback );
// 5.3) Append the brush; (see 7.9) moved out of data block
focus.append("g")
.attr("class", "brush")
.attr("height", height/2)
.attr("transform", "translate(" + 2+ "," + Math.floor( margin.top + 5) + ")")
// .attr("cursor", "default !important")
.call(brush_sensor)
.call(brush_sensor.move, [width - Math.floor(width / 2) - ( width / 5), width - Math.floor( width / 2) + (width / 5)])
.on("wheel",function() { d3.event.preventDefault(); })
.selectAll('.overlay')
.attr('pointer-events','none');
var clip_orig_x = 0, clip_orig_y = 0;
// 6.1) append culling to be used for a clip area.
var defs = focus.append("defs");
defs.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", 0)
.attr("y",0)
.attr("width", width )
.attr("height", height);
// 6) PARSE TIME FORMAT
var parseDate = d3.timeParse("%b %Y");
// 6.2) example of adding an svg as an object.
var xmlSvg;
d3.xml("blog.svg").mimeType("image/svg+xml").get(function(error, xml) {
if (error) throw error;
xmlSvg = xml.documentElement;
document.body.appendChild(xml.documentElement);
//});//xml
// 7) SETUP DATA WITH CALLBACK AND FORMAT DATE TYPE
d3.csv("sp500_2.csv", type, function(error, data) {
if (error) throw error;
// 7.2) STARTING DOMAIN of x and y axis
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.price; })]);
// 7.3) STARING DOMAIN FOR ALTERNATE axis
x2.domain(x.domain());
y2.domain(y.domain());
// 7.4) moved to 6.4, then moved back.
//revelation - if appending the axis before the domain, it will not be scaled to the new domain.
// 7.5) moved to 6.5, then moved back.
//revelation - if appending the axis before the domain, it will not be scaled to the new domain.
// 6.4) APPEND x-axis. (see 7.4) this was in the "data" block, but doesn't need to be.
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// 6.5) APPEND y-axis. (see 7.5) this was in the "data" block, but doesn't need to be
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
// 7.6) Select all the circle's within the Chart
focus.selectAll("circle")
.data(data).enter().append("circle")
.attr("clip-path", "url('#clip')")
.attr("class","dot")
.attr("r", 4.5)
.attr("opacity", 0.7)
.style("fill", "steelblue");
var xmlSvgB = d3.select(xmlSvg).select('#b').node();
var gXml = focus.selectAll("g.icon-g")
.data(data ).enter().append("svg:g")
.attr("clip-path", function(){ return 'url("#clip")';})
.attr("class","icon-g")
.append(function(){return xmlSvgB.cloneNode(true);})
.attr('class', 'icon')
.attr("opacity", 0.7)
.attr('id',function(d){return 'i_' + d.date.getTime();})
.attr('transform', function(d){
var xdate = x(d.date) - 11;
return "translate(" + xdate + "," + 15 + ") scale(0.8)" ;});
// 7.7) Select class dot and apply anyomous function
focus.selectAll(".dot")
.attr('cx', function(d) { return x(d.date); })
.attr('cy', height * 0.5);
// 7.8) moved to 5.1
// 7.9) moved to 5.3
focus.selectAll('.tick line')
.attr("y1", function(d){
return -height;
});
//move the brush infront of everything.
d3.selectAll(".brush").raise();
}); //end of data call
}); //end of xml call
function focus_brush_feedback(){
if(!d3.event.select)return;
console.info("focus_brush_feedback");
console.info(d3.event.select);
}
// 8) Method for zooming
function zoomed() {
if(d3.event.select)return;
// 8.1) confirm the right events are used
if (d3.event.sourceEvent == null || d3.event.sourceEvent.type == null || d3.event.sourceEvent.type === '') return;
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return;
// 8.2) INSTANTIATE d3 transform event
var t = d3.event.transform;
var ts = t.k, tx = t.x, ty = t.y;
//console.info("k = " + ts + "x = " + tx + "y = " + ty);
// 8.3) RESET the X DOMAIN using the Transform, Rescale X based on the x2 domain
x.domain(t.rescaleX(x2).domain());
// 8.4) Select the Chart's element to rescale.
focus.selectAll(".dot")
.attr('cx', function(d) { return x(d.date); })
.attr('cy', height * 0.5);
focus.selectAll('.icon')
.attr('transform', function(d){
var xdate = x(d.date) - 11;
return "translate(" + xdate + "," + 15 + ") scale(0.8)" ;})
;
// .attr('x', function(d){return x(d.date); });
/*focus.selectAll(".icon")
.attr("transform", function(d) {
return "translate(" + 0 + "," + d.x + ")"
});*/
// 8.5) Select the Chart's axis--x class, call xAxis to reset it(how?)
focus.select(".axis--x").call(xAxis);
focus.selectAll('.tick line')
.attr("y1", function(d){
return -height;
});
}
function type(d) {
d.date = parseDate(d.date);
//d.price = +d.close;
d.price = +d.price;
return d;
}
</script>
date price
Jan 2000 1394.46
Jul 2009 1140.45
Feb 2010 1140.45
Apr 2016 1140.45
Mar 2017 1240.45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment