|
const margin = {top: 20, right: 10, bottom: 20, left: 60}; |
|
|
|
class Figure { |
|
|
|
constructor(div,width,height) { |
|
this.width = width; |
|
this.height = height; |
|
|
|
// Canvas is drawn first, and then SVG over the top. |
|
this.canvas = div.append("canvas") |
|
.attr("width",width) |
|
.attr("height", height) |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")") |
|
.style("left", margin.left + "px") |
|
.style("top", margin.top + "px") |
|
.style("width", width + "px") |
|
.style("height", height + "px") |
|
.style("position", "absolute") |
|
.style("z-index",0) |
|
.on('click', () => { |
|
// Get coordinates of click relative to the canvas element |
|
let pos = d3.mouse(this.canvas.node()); |
|
console.log('canvas click ' + pos); |
|
}); |
|
|
|
this.svg = div.append("svg") |
|
.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
.append("g") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")") |
|
.style("position", "absolute") |
|
.style("z-index",1) |
|
.on('click', () => { |
|
// Get coordinates of click relative to the canvas element |
|
let pos = d3.mouse(this.svg.node()); |
|
console.log('svg click ' + pos); |
|
}); |
|
|
|
// We make an invisible rectangle to intercept mouse events |
|
this.rect=this.svg.append("rect") |
|
.attr("width", width) |
|
.attr("height", height) |
|
.style("fill", "000") |
|
.style("opacity", 1e-6) |
|
.style("position", "absolute") |
|
.style("z-index",2) |
|
.on("click", () => { |
|
let pos = d3.mouse(this.rect.node()); |
|
console.log('rect click '+pos); |
|
}) |
|
|
|
console.log('Figure created') |
|
} |
|
|
|
loaded(image) { |
|
|
|
this.image=image; |
|
|
|
let width = this.width; |
|
let height = this.height; |
|
|
|
this.x = d3.scale.linear() |
|
.domain([0, image.width]) |
|
.range([0, width]); |
|
|
|
this.y = d3.scale.linear() |
|
.domain([0, image.height]) |
|
.range([height, 0]); |
|
|
|
let x = this.x; |
|
let y = this.y; |
|
|
|
this.xAxis = d3.svg.axis() |
|
.scale(x) |
|
.orient("bottom"); |
|
|
|
this.yAxis = d3.svg.axis() |
|
.scale(y) |
|
.orient("left"); |
|
|
|
this.zoom = d3.behavior.zoom() |
|
.x(x) |
|
.y(y) |
|
// .scaleExtent([1, 10]) |
|
.on("zoom", this.refresh.bind(this)); |
|
|
|
this.canvas.attr("width", image.width) |
|
.attr("height", image.height); |
|
// this.draw(); |
|
|
|
this.rect.call(this.zoom); |
|
console.log('rect zoom assigned') |
|
|
|
this.svg.append("g") |
|
.attr("class", "x axis") |
|
.attr("transform", "translate(0," + height + ")") |
|
.call(this.xAxis) |
|
.call(removeZero); |
|
|
|
this.svg.append("g") |
|
.attr("class", "y axis") |
|
.call(this.yAxis) |
|
.call(removeZero); |
|
|
|
this.draw() |
|
} |
|
|
|
draw() { |
|
this.context = this.canvas.node().getContext("2d"); |
|
this.context.globalAlpha = 1; |
|
this.context.drawImage(this.image, 0, 0); |
|
console.log('image drawn') |
|
} |
|
|
|
// Keep an eye out for "translateExtent" or "xExtent" methods that may be |
|
// added at some point to bound the limits of zooming and panning. Until then, |
|
// this works. |
|
refresh() { |
|
|
|
let zoom=this.zoom; |
|
|
|
let width = this.width; |
|
let height = this.height; |
|
|
|
let x = this.x; |
|
let y = this.y; |
|
|
|
let xmin = x.domain()[0]; |
|
let xmax = x.domain()[1]; |
|
let ymin = y.domain()[0]; |
|
let ymax = y.domain()[1]; |
|
|
|
let t = zoom.translate(); |
|
let s = zoom.scale(); |
|
let tx = t[0], |
|
ty = t[1]; |
|
let xdom = x.domain(); |
|
let reset_s = 0; |
|
if ((xdom[1] - xdom[0]) >= (xmax - xmin)) { |
|
zoom.x(x.domain([xmin, xmax])); |
|
xdom = x.domain(); |
|
reset_s = 1; |
|
} |
|
let ydom = y.domain(); |
|
if ((ydom[1] - ydom[0]) >= (ymax - ymin)) { |
|
zoom.y(y.domain([ymin, ymax])); |
|
ydom = y.domain(); |
|
reset_s += 1; |
|
} |
|
if (reset_s == 2) { // Both axes are full resolution. Reset. |
|
zoom.scale(1); |
|
tx = 0; |
|
ty = 0; |
|
} |
|
else { |
|
if (xdom[0] < xmin) { |
|
tx = 0; |
|
x.domain([xmin, xdom[1] - xdom[0] + xmin]); |
|
xdom = x.domain(); |
|
} |
|
if (xdom[1] > xmax) { |
|
xdom[0] -= xdom[1] - xmax; |
|
tx = -xdom[0] * width / (xmax - xmin) * s; |
|
x.domain([xdom[0], xmax]); |
|
} |
|
if (ydom[0] < ymin) { |
|
y.domain([ymin, ydom[1] - ydom[0] + ymin]); |
|
ydom = y.domain(); |
|
ty = -(ymax - ydom[1]) * height / (ymax - ymin) * s; |
|
} |
|
if (ydom[1] > ymax) { |
|
ydom[0] -= ydom[1] - ymax; |
|
ty = 0; |
|
y.domain([ydom[0], ymax]); |
|
} |
|
} |
|
// Reset (possibly) if hit an edge so that next focus event starts correctly. |
|
zoom.translate([tx, ty]); |
|
|
|
let image = this.image; |
|
|
|
this.context.drawImage(image, |
|
tx * image.width / width, ty * image.height / height, |
|
image.width * s, image.height * s); |
|
|
|
this.svg.select(".x.axis").call(this.xAxis).call(removeZero); |
|
this.svg.select(".y.axis").call(this.yAxis).call(removeZero); |
|
} |
|
|
|
} |
|
|
|
function removeZero(axis) |
|
{ |
|
axis.selectAll("g").filter(function (d) { |
|
return !d; |
|
}).remove(); |
|
} |