Skip to content

Instantly share code, notes, and snippets.

@sachams
Last active August 29, 2015 14:14
Show Gist options
  • Save sachams/d8621093ebb8181d50fe to your computer and use it in GitHub Desktop.
Save sachams/d8621093ebb8181d50fe to your computer and use it in GitHub Desktop.
D3 panning issue - panning accelerates!
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<style>
body, html{
margin: auto;
width: 100%;
height: 100%;
color: white;
background-color: black;
font-family: helvetica;
}
chart{
display: block;
z-index: 0;
overflow: hidden;
font-size: 10px;
}
rect.pane { /*this transparent rectangle catches all zoom events*/
opacity:0;
/* cursor:move; */
pointer-events: all;
}
.x-axis path,
.x-axis line {
fill: none;
stroke: none;
shape-rendering: crispEdges;
stroke-width: 1.5px;
}
.x-axis text {
fill: white;
stroke: white;
font-family: sans-serif;
font-size: 11px;
}
.y-axis path,
.y-axis line {
fill: none;
stroke: none;
shape-rendering: crispEdges;
stroke-width: 1.5px;
}
.y-axis text {
fill: white;
stroke: white;
font-family: sans-serif;
font-size: 11px;
}
.line {
fill: none;
stroke: white;
stroke-width: 1.5px;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
.chart-1{
width: 100%;
height: 100%;
float: left;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://d3js.org/queue.v1.min.js"></script>
<body ng-app="myApp" ng-controller="MainCtrl">
<chart class="chart-1" data="chart_data" range="range" selected-point="selectEmployer"> </scatter>
</body>
<script>
var app = angular.module('myApp', []);
app.factory('dataService', ['$http', function ($http) {
var getStaticData = function(){
data = [
{ time: '2011-11-04 16:07:00', value: 11.9 },
{ time: '2011-11-05 16:12:00', value: 11.9 },
{ time: '2011-11-06 16:17:00', value: 11.8 },
{ time: '2011-11-07 16:22:00', value: 11.6 },
{ time: '2011-11-08 16:27:00', value: 11.5 },
{ time: '2011-11-09 16:32:00', value: 11.4 },
{ time: '2011-11-10 16:37:00', value: 11.2 },
{ time: '2011-11-11 16:42:00', value: 10.9 },
{ time: '2011-11-12 16:47:00', value: 10.8 },
{ time: '2011-11-13 16:52:00', value: 10.7 }
];
// Parse the data
var format = d3.time.format('%Y-%m-%d %H:%M:%S');
data.forEach(function(d) {
d.time = format.parse(d.time);
})
return data;
};
function addMinutes(date, minutes) {
return new Date(date.getTime() + minutes*60000);
}
var getRandomData = function(){
var start = new Date(2011,10,1,0,0,0,0); // month 10 is actuall Nov...
var end = new Date(2011,10,11,0,0,0,0);
var interval = 60; // interval in mins
var dt = start;
var value = 5;
var data = [];
while(dt < end) {
data.push({time: dt, value: value});
dt = addMinutes(dt, interval);
value = value + (Math.random()-0.5) * 1;
if(value > 15) { value = 15 } // cap and floor it at 15/3, respectively.
if(value < 3) { value = 3 }
}
return data;
};
var filterData = function(data, params) {
var filteredData = [];
data.forEach(function(d) {
if(d.time >= params.start && d.time <= params.end) {
filteredData.push(d)
}
})
return filteredData;
}
// Public API here
return {
getData: function(params){
var data = getRandomData();
var filteredData = filterData(data, params);
return filteredData;
}
};
}]);
app.controller('MainCtrl', function(dataService, $scope, $window){
$scope.range = [new Date(2011,10,1,0,0,0,0),
new Date(2011,10,11,0,0,0,0)];
$scope.loadData = function(range) {
console.log('Loading data form range ' + range)
var params = {start: range[0], end: range[1]};
$scope.chart_data = dataService.getData(params);
};
$scope.loadData($scope.range);
angular.element($window).on('resize', function(){ $scope.$apply() })
});
app.directive('chart', function(){
function link(scope, el, attr){
// Everything construction-related goes here
// We create objects and assign them to variables
// which we then reference later on
el = el[0];
var x = d3.time.scale();
var y = d3.scale.linear();
var xAxis = d3.svg.axis().scale(x).orient('bottom').tickFormat(d3.time.format('%d %b %y'));
var yAxis = d3.svg.axis().scale(y).orient('left');
var xGrid = d3.svg.axis().scale(x).orient("bottom").ticks(10).tickFormat("");
var yGrid = d3.svg.axis().scale(y).orient("left").ticks(10).tickFormat("");
var line = d3.svg.line().interpolate("cardinal").x(function(d) { return x(d.time); }).y(function(d) { return y(d.value); });
var Zoomer = d3.behavior.zoom()
.x(x)
.on("zoom", zoomHandler);
var svg = d3.select(el).append('svg');
var svgG = svg.append('g');
var svgX = svgG.append('g');
var xAxisG = svgG.append('g').attr('class', 'x-axis');
var yAxisG = svgG.append('g').attr('class', 'y-axis');
var xGridG = svgG.append('g').attr('class', 'grid');
var yGridG = svgG.append('g').attr('class', 'grid');
var path = svgG.append('path').attr('class', 'line');
var xAxisText = xAxisG.append("text").attr("y", 30).attr('dx', '.71em').style("text-anchor", "middle").text("Date");
var yAxisText = yAxisG.append('text').attr('transform', 'rotate(-90)').attr('y', -40).attr('dy', '.71em').style('text-anchor', 'middle').text('Value');
var margin = {top: 20, right: 20, bottom: 35, left: 50};
x.domain(scope.range);
scope.$watch(function(){
width = el.clientWidth;
height = el.clientHeight;
return width + height;
}, resize);
scope.$watch('data', function(){
console.log('On data change');
update();
});
function zoomHandler() {
scope.range = x.domain()
update();
}
function resize(){
// Everything related to client sizes has to go in here
var width = el.clientWidth - margin.left - margin.right;
var height = el.clientHeight - margin.top - margin.bottom;
svg.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
// Translate the whole svg *group* - this means all children of the group
// (like axes, etc) will also be translated
svgG.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// We have already translated our entire svg so we can ignore margins
// See https://gist.github.com/mbostock/3019563
x.range([0, width]);
y.range([height, 0]);
xAxisText.attr("x", width/2 );
yAxisText.attr('x', -height/2);
xGridG.attr('transform', 'translate(' + [0, height] + ')');
xGrid.tickSize(-height, 0, 0);
yGrid.tickSize(-width, 0, 0);
xAxisG.attr('transform', 'translate(' + [0, height] + ')');
var rect = svgX.append("svg:rect") // overlay to catch mouse zoom events
.attr("class", "pane")
.attr("width", el.clientWidth)
.attr("height", el.clientHeight);
rect.call(Zoomer);
update();
}
function update(){
// Everything related to data ranges has to go in here
// (we call this from our data watcher)
x.domain(scope.range);
y.domain(d3.extent(scope.data, function(d) { return d.value; }));
Zoomer.x(x) // update the zooming behavior to match the domain
xAxisG.call(xAxis);
yAxisG.call(yAxis);
xGridG.call(xGrid);
yGridG.call(yGrid);
// Add the line
path.datum(scope.data).attr('d', line);
}
}
return {
link: link,
restrict: 'E',
scope: { data: '=', selectedPoint: '=', range: '='}
};
});
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment