Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
378 views
in Technique[技术] by (71.8m points)

javascript - Canvas lineTo() drawing y coordinate in wrong place

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();
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Canvas' width and height are different from its CSS width and height!

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):

  • read the canvas' DOM element size and apply it to the canvas itself (see the 4th canvas in the example below)
  • modify its CSS styles based on the canvas' actual size (see the 5th canvas below)

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;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...