// derived from // https://github.com/bizweekgraphics/photo-annotations/blob/9ae16cd0d427c6115dbb7b8d90e827fd46ac61e5/src/scripts/zoomAndDodge.js function zoomAndDodge(selection) { var opacityScale = d3.scale.linear().domain([100,500]).range([1,0.1]).clamp(true), displacementScale = d3.scale.log().domain([50,1000]).range([100,0]).clamp(true), scaleScale = d3.scale.linear().domain([100,500]).range([1,0.2]).clamp(true); selection.each(function(d,i) { if(!d.annotation) return; var sel = d3.select(this), label = sel.select('a.label'), svg = d3.select("svg"), id = Math.random().toString().split('.')[1]; var anno = sel.append('div.annotation') .text(ƒ('annotation')) .style("opacity", 0); var annoLine = svg.append("line") .attr("x1", 0) .attr("y1", 0) .attr("x2", 0) .attr("y2", 0) .style("opacity", 0); d3.select('body').on('mousemove.'+id, function(d,i) { var mouse = d3.mouse(sel.node()); var point = [ 0 * sel.node().offsetLeft + 1 * label.node().offsetLeft + 0.5 * label.node().offsetWidth, 0 * sel.node().offsetTop + 1 * label.node().offsetTop + 0.5 * label.node().offsetHeight ] var dif = [ mouse[0] - point[0], mouse[1] - point[1] ]; var distance = Math.sqrt(Math.pow(dif[0], 2) + Math.pow(dif[1], 2)); var difNormal = dif.map(function(i) { return i/distance; }); anno .style('left', (displacementScale(distance) * -difNormal[0] + 0.5 * label.node().offsetWidth) + 'px') .style('top', (displacementScale(distance) * -difNormal[1] + 0.5 * label.node().offsetHeight) + 'px') .style('opacity', opacityScale(distance)) .style('transform', 'translate(-50%, -50%) scale('+scaleScale(distance)+')'); annoLine .attr("x1", sel.node().offsetLeft + 0.5 * label.node().offsetWidth + label.node().offsetLeft) .attr("y1", sel.node().offsetTop + 0.5 * label.node().offsetHeight + label.node().offsetTop) .attr("x2", sel.node().offsetLeft + 0.5 * label.node().offsetWidth + displacementScale(distance) * -difNormal[0]) .attr("y2", sel.node().offsetTop + 0.5 * label.node().offsetHeight + displacementScale(distance) * -difNormal[1]) .style("opacity", opacityScale(distance)); }); }) }