
Question:
[I want to add another image over the laptop screen`var ctx = myCanvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(13,28);
ctx.lineTo(237,7);
ctx.lineTo(285,105);
ctx.lineTo(73,151);
ctx.closePath();
ctx.lineWidth = 0.5;
ctx.strokeStyle = 'blue';
ctx.stroke();
//ctx.clip();
//ctx.rotate(-20*Math.PI/180);
var img = new Image;
img.onload = function()
{
var width = img.width;
var height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height,
0, 0, myCanvas.width, myCanvas.height);
}
};
img.src = reader.result;
`][1]
how do I skew the image to fit in the trapezium ctx that i have created
Answer1:<h2>Why 2D API is 2D</h2>
The canvas 2D API is called a 2D API for a very good reason. It can only do 2D transformations.
<h3>Not 2D</h3>The shape you have...
<a href="https://i.stack.imgur.com/EFGyu.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/EFGyu.png" data-original="https://i.stack.imgur.com/EFGyu.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>
The above image annotates your shape. Lines A,C are not parallel.
<h3>2D best fit</h3>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.
<a href="https://i.stack.imgur.com/6NOV7.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/6NOV7.png" data-original="https://i.stack.imgur.com/6NOV7.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>
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.
<a href="https://i.stack.imgur.com/31zo7.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/31zo7.png" data-original="https://i.stack.imgur.com/31zo7.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>
<pre class="snippet-code-js lang-js prettyprint-override">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";
function setStyle(style){
Object.keys(style).forEach(key => ctx[key] = style[key]);
}
function drawPath(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();
}
function markPath(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
);
}
}
function bisectPath(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 normals
function getLine(x1,y1,x2,y2){
return {
p1 : { x : x1 , y : y1 },
p2 : { x : x2 , y : y2 }
};
}
function getVec(line){
line.vec = {
x : line.p2.x - line.p1.x,
y : line.p2.y - line.p1.y
};
return line;
}
function getNormal(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 lines
var 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 fit
var image = new Image;
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 guides
drawPath(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"]);
}
<pre class="snippet-code-html lang-html prettyprint-override"><canvas id=canvas height=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.
<h3>WebGL for 3D</h3>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