Built with blockbuilder.org
Created
September 13, 2016 19:58
-
-
Save nbremer/e73086c0c3ebd2693a95962b561cbf05 to your computer and use it in GitHub Desktop.
Smooth Fisheye
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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