Reputation: 557
After Dynamically resizing a canvas using javascript points plotted on it no longer line up with where they are placed, and also arcs are getting stretched from how they should be rendered.
The below runnable example demonstrates the issue, because what should be a point where you click the cursor turns into a large miss-shapen oval which is in the wrong place.
var TargetWidth = 400;
var canvases = $(".hotspot-canvas")
for (i = 0; i < canvases.length; i++) {
canvas = $(canvases[i]);
var src = canvas.attr("data-img");
initilizePlotPointCanvas(canvas, src);
}
function initilizePlotPointCanvas(canvas, src)
{
var my_image = new Image();
my_image.onload = function(){
var w1 = this.width;
var w2 = TargetWidth
var r = w1 / w2;
var h1 = this.height;
var h2 = this.height / r;
canvas.width(TargetWidth).height(h2).css("background-image", "url("+src+")");
setTimeout(function(){
var jcanvas = canvas[0];
var ctx = jcanvas.getContext('2d'),
w = jcanvas.width,
h = jcanvas.height;
ctx.translate(-0.1, -0.1);
jcanvas.onmousedown = function(e) {
var rect = jcanvas.getBoundingClientRect();
x3 = e.clientX - rect.left;
y3 = e.clientY - rect.top;
ctx.clearRect(0, 0, w, h)
ctx.beginPath();
ctx.arc(x3, y3, 5, 0, 2 * Math.PI, false);
ctx.fillStyle = 'red';
ctx.fill();
ctx.stroke();
}
}, 500)
}
my_image.src = src;
}
.hotspot-canvas {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
cursor: crosshair;
border: 1px solid black;
background-repeat: no-repeat;
background-position: center center;
background-clip: border-box;
background-origin: padding-box;
-moz-background-size: cover;
background-size: cover;
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<canvas
data-id="554924"
data-img="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQXXarydhE3CZRSMgXCProd1w0_oSwIOPd7zJN5EQmLtQtPDD21"
class="hotspot-canvas"
data-responses="0" data-notes="0" data-actions="0" data-email="123" data-responder="true" data-response-id="" orig_type="14" data-tag="6bbf8f97-758f-47ca-8b8c-24a1cd3ddd55" data-formtemplatequestiontype="s" data-valueifparentna="" data-exportkey=""></canvas>
Upvotes: 4
Views: 1039
Reputation: 1735
Like I mentioned in my comment, I've run into this same problem quite recently, so I'd like to share my solution. Basically what's happening is your canvas element's dimensions are scaled and its origin is translated due to CSS, but the internal resolution of the canvas is still the same. Also, the border and padding of your canvas also affect the DOMRect
's properties returned by getBoundingClientRect
.
You need to take the clientX
and clientY
values from some MouseEvent
or TouchEvent
, the internal resolution of your canvas, and the dimensions and offset of the displayed canvas element, including border and padding, and use them to calculate the xy-coordinate relating to the canvas' internal coordinate system. This code requires JQuery and ES6 support, but you can compile down with Babel or something if you're worried about browser compatibility.
// translate coords in viewport to internal coords of canvas
const translateCoords = ({clientX, clientY}) => {
// dimensions of canvas element (top-left with respect to border-box)
const bcr = canvas.getBoundingClientRect();
// offsets from border and padding
const sideWidth = ["top", "right", "bottom", "left"].reduce((sum, side) => {
sum[side] = Object.values($(canvas).css([
`border-${side}-width`,
`padding-${side}`
])).reduce((a, b) => a + parseFloat(b), 0);
return sum;
}, {});
// ratio of internal canvas resolution to canvas displayed dimensions
const scaleX = canvas.width / (bcr.width - (sideWidth.left + sideWidth.right));
const scaleY = canvas.height / (bcr.height - (sideWidth.top + sideWidth.bottom));
// translate and scale screen coords to canvas internal coords
const x = (clientX - (bcr.left + sideWidth.left)) * scaleX;
const y = (clientY - (bcr.top + sideWidth.top)) * scaleY;
return {x, y};
};
// example event listener
canvas.addEventListener("click", (event) => {
const {x, y} = translateCoords(event);
// do something with translated coordinates
console.log(x, y);
});
Upvotes: 0
Reputation: 54026
Display size and canvas resolution are two different entities.
When you set the canvas style width and height you set the display size
canvas.style.width = "100px";
canvas.style.height = "100px";
When you set the canvas width and height you set the resolution.
canvas.width = 100;
canvas.height = 100;
You have set the display size but neglected to match the resolution to the display size.
You can fix it by just setting the resolution to match the display size
var bounds = jcanvas.getBoundingClientRect();
jcanvas.width = bounds.width;
jcanvas.height = bounds.height;
var TargetWidth = 400;
var canvases = $(".hotspot-canvas")
for (i = 0; i < canvases.length; i++) {
canvas = $(canvases[i]);
var src = canvas.attr("data-img");
initilizePlotPointCanvas(canvas, src);
}
function initilizePlotPointCanvas(canvas, src)
{
var my_image = new Image();
my_image.onload = function(){
var w1 = this.width;
var w2 = TargetWidth
var r = w1 / w2;
var h1 = this.height;
var h2 = this.height / r;
canvas.width(TargetWidth).height(h2).css("background-image", "url("+src+")");
setTimeout(function(){
var jcanvas = canvas[0];
var bounds = jcanvas.getBoundingClientRect();
jcanvas.width = bounds.width;
jcanvas.height = bounds.height;
var ctx = jcanvas.getContext('2d'),
w = jcanvas.width,
h = jcanvas.height;
ctx.translate(-0.1, -0.1);
jcanvas.onmousedown = function(e) {
var rect = jcanvas.getBoundingClientRect();
x3 = e.clientX - rect.left;
y3 = e.clientY - rect.top;
ctx.clearRect(0, 0, w, h)
ctx.beginPath();
ctx.arc(x3, y3, 5, 0, 2 * Math.PI, false);
ctx.fillStyle = 'red';
ctx.fill();
ctx.stroke();
}
}, 500)
}
my_image.src = src;
}
.hotspot-canvas {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
cursor: crosshair;
border: 1px solid black;
background-repeat: no-repeat;
background-position: center center;
background-clip: border-box;
background-origin: padding-box;
-moz-background-size: cover;
background-size: cover;
}
<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<canvas
data-id="554924"
data-img="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQXXarydhE3CZRSMgXCProd1w0_oSwIOPd7zJN5EQmLtQtPDD21"
class="hotspot-canvas"
data-responses="0" data-notes="0" data-actions="0" data-email="123" data-responder="true" data-response-id="" orig_type="14" data-tag="6bbf8f97-758f-47ca-8b8c-24a1cd3ddd55" data-formtemplatequestiontype="s" data-valueifparentna="" data-exportkey=""></canvas>
Upvotes: 3