I'm trying to draw some rectangles on a canvas using ctx.lineTo(). They get drawn but the y coordinate is never right. The rectangles become too tall and on the wrong place on the y axis. When I step through with the debugger it shows the y coordinates within the lineTo() methods as being correct, but I made a canvas.click event to alert the coordinates (which are correct as I click in the top left and it alerts (0,0)). The click event shows that the y coordinate is not actually where it states it will be drawn in the lineTo() method. The x coordinate is however always correct. One thing to be considered is I create my canvas by appending html to an element with javascript, and I add an image to it that I draw on. I rescale the coordinates of the rectangles so they are appropriately place on the canvas which is scaled to the image size that works for the size of the device. Here is all my code from canvas creation to using the lineTo() method.
Canvas gets created in early stages of this method (in appendPicture()):
function appendSection(theSection, list) {
list.append('<label class="heading">' + theSection.description + '</label><br/><hr><br/>');
if (theSection.picture) {
appendPicture(list, theSection);
var canvas = document.getElementById('assessmentImage');
var ctx=canvas.getContext("2d");
canvas.addEventListener("mousedown", relMouseCoords, false);
var img=new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0,canvas.width,canvas.height);
}
img.src = "data:image/jpeg;base64,"+ theSection.picture;
img.addEventListener('load', function() {
if(theSection.allHotSpots.length > 0) {
for( var x = 0; x < theSection.allHotSpots.length; x++) {
appendHotSpot(theSection.allHotSpots[x], theSection.thePicture, ctx);
}
}
}, false);
}
appendSectionQuestions(theSection, list);
if (theSection.allSubSections) {
for (var x = 0; x < theSection.allSubSections.length; x++) {
var theSectionA = theSection.allSubSections[x];
appendSection(theSectionA, list);
}
}
}
Here is appendPicture which creates the canvas html and appends it to an element.
function appendPicture(list, theSection) {
list.append('<div id="wrapper' + platform + '" style="width:100%; text-align:center">\
<canvas class="assessmentImageSmall" style="width:100%;height:' + Math.round(theSection.thePicture.ySize * (document.getElementById('assessmentSectionForm' + platform).clientWidth / theSection.thePicture.xSize)) + 'px" id="assessmentImage' + platform + '" align="middle" ></canvas>\
<!--<p style="color:#666;" id="imageInstruction">Tap image to enlarge.</p>-->\
</div>');
$("#wrapper").kendoTouch({
tap: function (e) {
switchImage();
}
});
}
Here is where I draw the rectangle (I call rectangles hotspots in this function)
function appendHotSpot(HotSpot, picture, ctx) {
var imageWidth = document.getElementById('assessmentImage' + platform).clientWidth;
var scale = imageWidth / picture.xSize;
HotSpot.topLeft = [Math.round(HotSpot.topLeft[0] * scale), Math.round(HotSpot.topLeft[1] * scale)];
HotSpot.bottomRight = [Math.round(HotSpot.bottomRight[0] * scale), Math.round(HotSpot.bottomRight[1] * scale)];
var rect = {x1: HotSpot.topLeft[0], y1: HotSpot.topLeft[1], x2: HotSpot.bottomRight[0], y2: HotSpot.bottomRight[1]};
ctx.strokeStyle="red";
ctx.beginPath();
ctx.moveTo(rect.x1, rect.y1);
ctx.lineTo(rect.x2, rect.y1);
ctx.lineTo(rect.x2, rect.y2);
ctx.lineTo(rect.x1, rect.y2);
ctx.lineTo(rect.x1, rect.y1);
ctx.stroke();
}
This means that the canvas gets stretched to align to CSS sizes. This is good for scaling, but can result in issues like the ones you're having with width: 100%.
Two solutions (depending on what you need):
Simple example:
function buildCanvas(w, h, sizeFromDOM, changeCSS, looksFine) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
canvas.style.borderColor = looksFine ? "green" : "red";
if (sizeFromDOM) {
// read its size from the DOM
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
} else {
// or simply apply what was given
canvas.width = w;
canvas.height = h;
}
// change CSS styles if needed
if (changeCSS) {
canvas.style.width = canvas.width + 'px';
canvas.style.height = canvas.height + 'px';
}
// draw the same 80x80 square on each
ctx.strokeStyle = looksFine ? "green" : "red";
ctx.beginPath();
ctx.moveTo(10, 10);
ctx.lineTo(90, 10);
ctx.lineTo(90, 90);
ctx.lineTo(10, 90);
ctx.lineTo(10, 10);
ctx.stroke();
}
buildCanvas(200, 200, false, false, true); // this canvas is fine as it matches the CSS sizes
buildCanvas(300, 200); // this canvas is stretched horizontally
buildCanvas(200, 300); // this canvas is stretched vertically
buildCanvas(200, 300, true, false, true); // let's fix this one
buildCanvas(300, 200, false, true, true); // this one too, but by changing its CSS
canvas {
width: 200px;
height: 200px;
border: 1px solid #aaa;
margin: 4px;
}
Here's a nice little kludge that will set all canvas
elements' heights and widths to equal their CSS settings.
var canvases = document.getElementsByTagName("canvas");
for(var i=0; i<canvases.length; i++)
{
canvas = canvases[i];
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
}
Now you can continue to set canvas dimensions by CSS!
(I wouldn't have figured this out without @Shomz' answer.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With