Skip to content Skip to sidebar Skip to footer

How To To Fit An Image On A Trapezium Html Canvas Which I Drew Using Path

[I want to add another image over the laptop screen`var ctx = myCanvas.getContext('2d'); ctx.beginPath(); ctx.moveTo(13,28); ctx.lin

Solution 1:

Why 2D API is 2D

The canvas 2D API is called a 2D API for a very good reason. It can only do 2D transformations.

Not 2D

The shape you have...

enter image description here

The above image annotates your shape. Lines A,C are not parallel.

2D best fit

The canvas 2D transformation can not fit a rectangle to that shape as it does not carry enough information to deal with the converging lines and how to scale the pixels as they converge.

The best you can do is an approximation

The next image bisects the shape using two lines. These lines will be used to create the transform that will best fit the shape using the 2D transformation.

enter image description here

We use the bisecting lines to define the x and y axis of the transform and use the length of each to determine the scale. The center point of the image is the center of one of the bisecting lines.

enter image description here

const ctx = canvas.getContext("2d");
const path = [13, 28, 237, 7, 285, 105, 73, 151];
const imageURL = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Sarcoramphus_papa_%28K%C3%B6nigsgeier_-_King_Vulture%29_-_Weltvogelpark_Walsrode_2013-01.jpg/675px-Sarcoramphus_papa_%28K%C3%B6nigsgeier_-_King_Vulture%29_-_Weltvogelpark_Walsrode_2013-01.jpg";
functionsetStyle(style){
    Object.keys(style).forEach(key => ctx[key] = style[key]);
}
functiondrawPath(path, style) {
    var i = 0;
    setStyle(style);
    ctx.beginPath();
    ctx.moveTo(path[i++], path[i++]);
    while (i < path.length) {
        ctx.lineTo(path[i++], path[i++]);
    }
    ctx.closePath();
    ctx.stroke();
}
functionmarkPath(path, style, marks) {
    var i = 0;
    var len = path.length;
    setStyle(style);
    while (i < len) {
        ctx.fillText(
            marks[i >> 1],
            (path[i] + path[((i++) + 2) % len]) / 2,
            (path[i] + path[((i++) + 2) % len]) / 2
        );
    }
}
functionbisectPath(path, modulo, style) {
    var i = 0;
    var len = path.length;
    setStyle(style);
    ctx.beginPath();
    while (i < len) {
        ctx.moveTo(
            (path[i] + path[((i++) + 2) % len]) / 2,
            (path[i] + path[((i++) + 2) % len]) / 2
        );
        i += modulo;
        ctx.lineTo(
            (path[i] + path[((i++) + 2) % len]) / 2,
            (path[i] + path[((i++) + 2) % len]) / 2
        );
        i -= modulo + 2;
    }
    ctx.stroke();

}

// functions to create lines, get vectors, length and normalsfunctiongetLine(x1,y1,x2,y2){
   return {
      p1 : { x : x1 , y : y1 },
      p2 : { x : x2 , y : y2 }
   };
}
functiongetVec(line){
   line.vec = { 
       x : line.p2.x - line.p1.x,
       y : line.p2.y - line.p1.y
   };
   return line;
}    
functiongetNormal(line){
   line.len = Math.hypot(line.vec.x, line.vec.y);
   line.norm = { 
       x : line.vec.x / line.len,
       y : line.vec.y / line.len,
   };
   return line;
}    
// create the 2 bisecting linesvar line1 = getNormal( getVec( getLine(
     (path[0] + path[2]) / 2,
     (path[1] + path[3]) / 2,
     (path[4] + path[6]) / 2,
     (path[5] + path[7]) / 2
)));
var line2 = getNormal( getVec( getLine(
     (path[6] + path[0]) / 2,
     (path[7] + path[1]) / 2,
     (path[2] + path[4]) / 2,
     (path[3] + path[5]) / 2
)));

// create an image to fitvar image = newImage;
image.src = imageURL;
image.onload = function(){
    var w, h, sx, sy, cx, cy;
    w = this.width;
    h = this.height;
    // calculate the image scales
    sx = line2.len / w;
    sy = line1.len / h;
    // calculate the center
    cx = (line1.p1.x + line1.p2.x) / 2;
    cy = (line1.p1.y + line1.p2.y) / 2;

    // now we have all the information needed to create the transformation
    ctx.setTransform(
       line2.norm.x * sx,  // scale and direction of x axis
       line2.norm.y * sx,
       line1.norm.x * sy,  // scale and direction of y axis
       line1.norm.y * sy,
       cx, cy,             // the origin coordinate (0,0)
    );
    // Draw the image offset from its center by half its width and heigth
    ctx.drawImage(this, -w / 2, -h / 2);
    // reset the transformation
    ctx.setTransform(1,0,0,1,0,0);

    // draw the guidesdrawPath(path, {
        lineWidth : 0.5,
        strokeStyle : "blue"
    });
    bisectPath(path, 2, {
        lineWidth : 1,
        strokeStyle : "white"
    });
    markPath(path, {
        font : "18px arial",
        textAlign : "center",
        textBaseline : "middle",
        fillStyle : "black"
    }, ["A", "B", "C", "D"]);





}
<canvasid=canvasheight=160></canvas>

That is the best you can do with the canvas 2D API. You could render each pixel calculating the 3D transform to fit the shape you have. Depending on the image size that can take some time (CPU) to do and the result will be OK.

WebGL for 3D

If you really need the 3D transform you should use webGL. There are plenty of examples on SO and the web on how to do that

Post a Comment for "How To To Fit An Image On A Trapezium Html Canvas Which I Drew Using Path"