Skip to content

Instantly share code, notes, and snippets.

@nbremer
Created September 13, 2016 19:58
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 nbremer/e73086c0c3ebd2693a95962b561cbf05 to your computer and use it in GitHub Desktop.
Save nbremer/e73086c0c3ebd2693a95962b561cbf05 to your computer and use it in GitHub Desktop.
Smooth Fisheye
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
</head>
<body>
<!-- Based entirly on http://www.nytimes.com/newsgraphics/2014/02/14/fashion-week-editors-picks/ but only the required bits for 1 canvas -->
<div id="monthFisheye"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script>
var width = 1000,
height = 500;
var monthFisheye = d3.selectAll("#monthFisheye");
var canvas = monthFisheye.append("canvas");
var pixelRatio = 1,
storeRatio = 1,
enabled = false;
if (window.devicePixelRatio >= 2
&& screen.availWidth >= 1280 // iPad can’t even handle it
&& canvas.node().getContext("2d").webkitBackingStorePixelRatio !== 2) { // Safari can’t even
pixelRatio = 2;
storeRatio = 2;
}//if
canvas
.attr("height", height * storeRatio)
.style("height", height + "px");
var context = canvas.node().getContext("2d");
context.scale(storeRatio, storeRatio);
d3.select(window)
.on("resize", resize);
resize();
// Recompute bounding boxes due to reflow.
function resize() {
width = parseInt(monthFisheye.style("width"));
monthFisheye.select("canvas")
.attr("width", width * storeRatio)
.style("width", width + "px");
enableFisheye(canvas);
}//resize
function enableFisheye() {
var that = this,
link = that.parentNode,
touchtime;
var image = new Image,
desiredDistortion = 0,
desiredFocus,
idle = true;
var numRows = 12,
normalHeight = height / numRows,
imageWidth = width,
imageHeight = 300;
var y = fisheye()
.distortion(0)
.extent([0, height]);
canvas
//.on("mousedown", mousedown)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout)
.on("touchstart", touchstart)
.on("touchmove", mousemove)
.on("touchend", mouseout);
render();
function render() {
context.clearRect(0, 0, width, height);
for (var i = 0, n = numRows; i < n; ++i) {
var y0 = y(i * normalHeight),
y1 = y((i + 1) * normalHeight),
dy = Math.min(imageHeight, y1 - y0);
// context.drawImage(
// image,
// 0,
// Math.round((i * imageHeight + (imageHeight - dy) / 2) * pixelRatio),
// imageWidth * pixelRatio,
// dy * pixelRatio,
// 0,
// y0,
// width,
// dy);
context.fillStyle = ('#'+ parseInt(i % 255, 16) + parseInt(255 - (i % 255), 16) + "000000").substr(0,7);;
context.fillRect(0,y0,width,dy);
context.strokeStyle = "rgba(255,255,255,0.8)";
context.beginPath();
context.moveTo(0, y0);
context.lineTo(width, y0);
context.stroke();
}
context.strokeRect(0, 0, width, height);
}//render
function move() {
if (idle) d3.timer(function() {
var currentDistortion = y.distortion(),
currentFocus = currentDistortion ? y.focus() : desiredFocus;
idle = Math.abs(desiredDistortion - currentDistortion) < .01 && Math.abs(desiredFocus - currentFocus) < .5;
y.distortion(idle ? desiredDistortion : currentDistortion + (desiredDistortion - currentDistortion) * .14);
y.focus(idle ? desiredFocus : currentFocus + (desiredFocus - currentFocus) * .14);
render();
return idle;
});
}
function mouseover() {
desiredDistortion = imageHeight / normalHeight - 1;
mousemove();
}
function mouseout() {
desiredDistortion = 0;
mousemove();
}
function mousemove() {
desiredFocus = Math.max(0, Math.min(height - 1e-6, d3.mouse(this)[1]));
move();
}
// function mousedown() {
// var m = Math.max(0, Math.min(height - 1e-6, d3.mouse(that)[1]));
// for (var i = 0, n = numRows; i < n && y(i * normalHeight) < m; ++i);
// link.href = "http://www.nytimes.com/fashion/runway/" + d.slug + "/spring-2014-rtw/" + i + "?fingerprint=true";
// }
function touchstart() {
d3.event.preventDefault();
mouseover();
if (d3.event.touches.length === 1) {
var now = Date.now();
if (now - touchtime < 500) mousedown(), link.click();
touchtime = now;
}
}
}
function fisheye() {
var min = 0,
max = 1,
distortion = 3,
focus = 0;
function G(x) {
return (distortion + 1) * x / (distortion * x + 1);
}
function fisheye(x) {
var Dmax_x = (x < focus ? min : max) - focus,
Dnorm_x = x - focus;
return G(Dnorm_x / Dmax_x) * Dmax_x + focus;
}
fisheye.extent = function(_) {
if (!arguments.length) return [min, max];
min = +_[0], max = +_[1];
return fisheye;
};
fisheye.distortion = function(_) {
if (!arguments.length) return distortion;
distortion = +_;
return fisheye;
};
fisheye.focus = function(_) {
if (!arguments.length) return focus;
focus = +_;
return fisheye;
};
return fisheye;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment