html canvas draw cylindrical view of the image by drawing vertical slices - javascript

I am using below code to draw a cylindrical view of the image by drawing vertical slices of the image:
DrawMug(canvasId, cupImg, rotate, scale) {
var canvas: any = document.getElementById(canvasId);
var ctx = canvas.getContext("2d");
var productImg = new Image();
productImg.onload = () => {
var iw = productImg.width;
var ih = productImg.height;
console.log("height");
canvas.width = iw;
canvas.height = ih;
ctx.drawImage(productImg, 0, 0, productImg.width, productImg.height, 0, 0, iw, ih);
this.loadUpperIMage(ctx, rotate, scale);
};
productImg.src = cupImg;
}
loadUpperIMage(ctx: any, rotate: number, scale: number) {
var img = new Image();
img.src = this.designImgUrl;
img.onload = function() {
var iw = img.width;
var ih = img.height;
var xOffset = 100, //left padding
yOffset = 110; //top padding
var a = 75.0; //image width
var b = 12.0; //round ness
var scaleFactor = iw / (scale * a);
// draw vertical slices (increasing X by 1)
for (var X = 0; X < iw; X += 1) {
var y = b / a * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
ctx.drawImage(img, (X * scaleFactor), 0, (iw / rotate), ih, X + xOffset, y + yOffset, 1, 174);
}
};
}
It draws the image but the edges doesn't look good as below:
I tried resolving it by decreasing the vertical slice width by replacing the for loop as below
// draw vertical slices (increasing X by 0.1)
for (var X = 0; X < iw; X += 0.1) {
var y = b / a * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
ctx.drawImage(img, (X * scaleFactor), 0, (iw / rotate), ih, X + xOffset, y + yOffset, 0.1, 174);
}
it fixes the edges but it distorts the image as below, probably because here I am taking destination width as 0.1 but, I am not sure how do I set source width accordingly.
Fiddle
https://jsfiddle.net/au8f6d51/

Related

Why this pixelation animation is jolty

I have the following multi-step pixelation animation. It animates from low-to-high pixelation very slowly to show you that it jolts in between some of the steps, as if the image is slightly moved between renderings. I can't figure out why this is happening or how to make it appear as if the image is staying in one place.
var c = document.createElement('canvas')
c.style.display = 'flex'
c.style.width = '100vw'
c.style.height = '100vh'
c.style['image-rendering'] = 'pixelated'
document.body.appendChild(c)
var x = c.getContext('2d')
x.webkitImageSmoothingEnabled = false
x.mozImageSmoothingEnabled = false
x.msImageSmoothingEnabled = false
x.imageSmoothingEnabled = false
var src = c.getAttribute('data-draw')
var small = `https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/M101_hires_STScI-PRC2006-10a.jpg/307px-M101_hires_STScI-PRC2006-10a.jpg`
var large = `https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/M101_hires_STScI-PRC2006-10a.jpg/1280px-M101_hires_STScI-PRC2006-10a.jpg`
// c.width = c.clientWidth
// c.height = c.clientHeight
var stack = []
var start = false
var place = 0
function queue(image, ratio, width, height) {
stack.push({ image, ratio, width, height })
if (start) return
start = true
setTimeout(proceed, 0)
}
function proceed() {
let point = stack.shift()
let w
let h
if (point.ratio) {
w = c.width = c.clientWidth * point.ratio
h = c.height = c.clientHeight * point.ratio
} else {
w = point.width
h = point.height
}
if (!stack.length) {
x.webkitImageSmoothingEnabled = true
x.mozImageSmoothingEnabled = true
x.msImageSmoothingEnabled = true
x.imageSmoothingEnabled = true
c.classList.remove('px')
}
drawImageProp(x, point.image, 0, 0, w, h)
if (stack.length) {
setTimeout(proceed, 1000)
}
}
var s = new Image()
s.onload = function(){
queue(s, 0.01)
queue(s, 0.03)
var i = new Image()
i.onload = function(){
queue(i, 0.03)
queue(i, 0.11)
queue(i, 1)
}
i.src = large
}
s.src = small
function drawImageProp(ctx, img, x, y, w, h, offsetX, offsetY) {
if (arguments.length === 2) {
x = y = 0;
w = ctx.canvas.width;
h = ctx.canvas.height;
}
// default offset is center
offsetX = typeof offsetX === "number" ? offsetX : 0.5;
offsetY = typeof offsetY === "number" ? offsetY : 0.5;
// keep bounds [0.0, 1.0]
if (offsetX < 0) offsetX = 0;
if (offsetY < 0) offsetY = 0;
if (offsetX > 1) offsetX = 1;
if (offsetY > 1) offsetY = 1;
var iw = img.width,
ih = img.height,
r = Math.min(w / iw, h / ih),
nw = iw * r, // new prop. width
nh = ih * r, // new prop. height
cx, cy, cw, ch, ar = 1;
// decide which gap to fill
if (nw < w) ar = w / nw;
if (Math.abs(ar - 1) < 1e-14 && nh < h) ar = h / nh; // updated
nw *= ar;
nh *= ar;
// calc source rectangle
cw = iw / (nw / w);
ch = ih / (nh / h);
cx = (iw - cw) * offsetX;
cy = (ih - ch) * offsetY;
// make sure source rectangle is valid
if (cx < 0) cx = 0;
if (cy < 0) cy = 0;
if (cw > iw) cw = iw;
if (ch > ih) ch = ih;
// fill image in dest. rectangle
ctx.drawImage(img, cx, cy, cw, ch, x, y, w, h);
}
Even in between the last two steps it shifts slightly. Wondering what's going wrong and how to fix it.
FYI, there are two images, a small one and a big one, both the same thing. It first loads the small image at low resolution, then loads the large one. While the large one is loading, it does a few animation steps using the small image to start the animation. Then once the large one is done, it picks up where the small one left off and does a few more animation steps using the large image.

ctx canvas image decrease width from bottom side

I have drawn the image in the canvas by using this below code in which I have combined the elements images with the folded cup image
canvas2(getFullElementImage);
function canvas2(getFullElementImage) {
var canvas = document.getElementById("finalCanvas");
var ctx = canvas.getContext("2d");
var productImg = new Image();
productImg.onload = function () {
var iw = productImg.width;
var ih = productImg.height;
console.log("height");
canvas.width = iw;
canvas.height = ih;
ctx.drawImage(productImg, 30, 0, productImg.width, productImg.height,
0, 0, iw, ih);
loadUpperIMage()
};
productImg.src = "https://res.cloudinary.com/faizykhan1212/image/upload/v1550583008/folded_cup_1.png"
function loadUpperIMage() {
var img = new Image();
img.src = getFullElementImage;
img.onload = function () {
var iw = img.width;
var ih = img.height;
// alert(iw)
var xOffset = 160, //left padding
yOffset = 110; //top padding
var a = 190.0; //image width
var b = 20; //round ness
var scaleFactor = iw / (4 * a);
// draw vertical slices
for (var X = 0; X < iw; X += 1) {
var y = b / a * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
ctx.drawImage(img, X * scaleFactor, 0, iw / 3, ih, X + xOffset, y + yOffset, 1, 574);
}
};
}
};
This is how image is showing:
I want this to the decrease the width from the bottom so that this will fit to the cup with the left and the right corners.
Thanks
Maybe you can use webGL for examples like this.
This code is brilliant. I made some calibration for this part :
var xOffset = 174, //left padding
yOffset = 110; //top padding
var a = 180.0; //image width
var b = 80; //round ness
canvas2();
function canvas2() {
var canvas = document.getElementById("finalCanvas");
var ctx = canvas.getContext("2d");
var productImg = new Image();
productImg.onload = function () {
var iw = productImg.width;
var ih = productImg.height;
console.log("height");
canvas.width = iw;
canvas.height = ih;
ctx.drawImage(productImg, 30, 0, productImg.width, productImg.height,
0, 0, iw, ih);
loadUpperIMage()
};
productImg.src = "https://res.cloudinary.com/faizykhan1212/image/upload/v1550583008/folded_cup_1.png"
function loadUpperIMage() {
var img = new Image();
img.src = "https://res.cloudinary.com/faizykhan1212/image/upload/v1550655724/download-3.png";
img.onload = function () {
var iw = img.width;
var ih = img.height;
// alert(iw)
var xOffset = 174, //left padding
yOffset = 110; //top padding
var a = 180.0; //image width
var b = 80; //round ness
var scaleFactor = iw / (4 * a);
// draw vertical slices
for (var X = 0; X < iw; X += 1) {
var y = b / a * Math.sqrt(a * a - (X - a) * (X - a)); // ellipsis equation
ctx.drawImage(img, X * scaleFactor, 0, iw / 3, ih, X + xOffset, y + yOffset, 1, 574);
}
};
}
};
<canvas id="finalCanvas"></canvas>

How to draw herringbone pattern on html canvas

I Have to draw Herringbone pattern on canvas and fill with image
some one please help me I am new to canvas 2d drawing.
I need to draw mixed tiles with cross pattern (Herringbone)
var canvas = this.__canvas = new fabric.Canvas('canvas');
var canvas_objects = canvas._objects;
// create a rectangle with a fill and a different color stroke
var left = 150;
var top = 150;
var x=20;
var y=40;
var rect = new fabric.Rect({
left: left,
top: top,
width: x,
height: y,
angle:45,
fill: 'rgba(255,127,39,1)',
stroke: 'rgba(34,177,76,1)',
strokeWidth:0,
originX:'right',
originY:'top',
centeredRotation: false
});
canvas.add(rect);
for(var i=0;i<15;i++){
var rectangle = fabric.util.object.clone(getLastobject());
if(i%2==0){
rectangle.left = rectangle.oCoords.tr.x;
rectangle.top = rectangle.oCoords.tr.y;
rectangle.originX='right';
rectangle.originY='top';
rectangle.angle =-45;
}else{
fabric.log('rectangle: ', rectangle.toJSON());
rectangle.left = rectangle.oCoords.tl.x;
rectangle.top = rectangle.oCoords.tl.y;
fabric.log('rectangle: ', rectangle.toJSON());
rectangle.originX='left';
rectangle.originY='top';
rectangle.angle =45;
}
//rectangle.angle -90;
canvas.add(rectangle);
}
fabric.log('rectangle: ', canvas.toJSON());
canvas.renderAll();
function getLastobject(){
var last = null;
if(canvas_objects.length !== 0){
last = canvas_objects[canvas_objects.length -1]; //Get last object
}
return last;
}
How to draw this pattern in canvas using svg or 2d,3d method. If any third party library that also Ok for me.
I don't know where to start and how to draw this complex pattern.
some one please help me to draw this pattern with rectangle fill with dynamic color on canvas.
Here is a sample of the output I need: (herringbone pattern)
I tried something similar using fabric.js library here is my JSFiddle
Trippy disco flooring
To get the pattern you need to draw rectangles one horizontal tiled one space left or right for each row down and the same for the vertical rectangle.
The rectangle has an aspect of width 2 time height.
Drawing the pattern is simple.
Rotating is easy as well the harder part is finding where to draw the tiles for the rotation.
To do that I create a inverse matrix of the rotation (it reverses a rotation). I then apply that rotation to the 4 corners of the canvas 0,0, width,0 width,height and 0,height this gives me 4 points in the rotated space that are at the edges of the canvas.
As I draw the tiles from left to right top to bottom I find the min corners for the top left, and the max corners for the bottom right, expand it out a little so I dont miss any pixels and draw the tiles with a transformation set the the rotation.
As I could not workout what angle you wanted it at the function will draw it at any angle. On is animated, the other is at 60deg clockwise.
Warning demo contains flashing content.
Update The flashing was way to out there, so have made a few changes, now colours are a more pleasing blend and have fixed absolute positions, and have tied the tile origin to the mouse position, clicking the mouse button will cycle through some sizes as well.
const ctx = canvas.getContext("2d");
const colours = []
for(let i = 0; i < 1; i += 1/80){
colours.push(`hsl(${Math.floor(i * 360)},${Math.floor((Math.sin(i * Math.PI *4)+1) * 50)}%,${Math.floor(Math.sin(i * Math.PI *8)* 25 + 50)}%)`)
}
const sizes = [0.04,0.08,0.1,0.2];
var currentSize = 0;
const origin = {x : canvas.width / 2, y : canvas.height / 2};
var size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
function drawPattern(size,origin,ang){
const xAx = Math.cos(ang); // define the direction of xAxis
const xAy = Math.sin(ang);
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.setTransform(xAx,xAy,-xAy,xAx,origin.x,origin.y);
function getExtent(xAx,xAy,origin){
const im = [1,0,0,1]; // inverse matrix
const dot = xAx * xAx + xAy * xAy;
im[0] = xAx / dot;
im[1] = -xAy / dot;
im[2] = xAy / dot;
im[3] = xAx / dot;
const toWorld = (x,y) => {
var point = {};
var xx = x - origin.x;
var yy = y - origin.y;
point.x = xx * im[0] + yy * im[2];
point.y = xx * im[1] + yy * im[3];
return point;
}
return [
toWorld(0,0),
toWorld(canvas.width,0),
toWorld(canvas.width,canvas.height),
toWorld(0,canvas.height),
]
}
const corners = getExtent(xAx,xAy,origin);
var startX = Math.min(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
var endX = Math.max(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
var startY = Math.min(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
var endY = Math.max(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
startX = Math.floor(startX / size) - 2;
endX = Math.floor(endX / size) + 2;
startY = Math.floor(startY / size) - 2;
endY = Math.floor(endY / size) + 2;
// draw the pattern
ctx.lineWidth = size * 0.1;
ctx.lineJoin = "round";
ctx.strokeStyle = "black";
var colourIndex = 0;
for(var y = startY; y <endY; y+=1){
for(var x = startX; x <endX; x+=1){
if((x + y) % 4 === 0){
colourIndex = Math.floor(Math.abs(Math.sin(x)*size + Math.sin(y) * 20));
ctx.fillStyle = colours[(colourIndex++)% colours.length];
ctx.fillRect(x * size,y * size,size * 2,size);
ctx.strokeRect(x * size,y * size,size * 2,size);
x += 2;
ctx.fillStyle = colours[(colourIndex++)% colours.length];
ctx.fillRect(x * size,y * size, size, size * 2);
ctx.strokeRect(x * size,y * size, size, size * 2);
x += 1;
}
}
}
}
// Animate it all
var update = true; // flag to indecate something needs updating
function mainLoop(time){
// if window size has changed update canvas to new size
if(canvas.width !== innerWidth || canvas.height !== innerHeight || update){
canvas.width = innerWidth;
canvas.height = innerHeight
origin.x = canvas.width / 2;
origin.y = canvas.height / 2;
size = Math.min(canvas.width, canvas.height) * sizes[currentSize % sizes.length];
update = false;
}
if(mouse.buttonRaw !== 0){
mouse.buttonRaw = 0;
currentSize += 1;
update = true;
}
// draw the patter
drawPattern(size,mouse,time/2000);
requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
mouse = (function () {
function preventDefault(e) { e.preventDefault() }
var m; // alias for mouse
var mouse = {
x : 0, y : 0, // mouse position
buttonRaw : 0,
over : false, // true if mouse over the element
buttonOnMasks : [0b1, 0b10, 0b100], // mouse button on masks
buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
bounds : null,
eventNames : "mousemove,mousedown,mouseup,mouseout,mouseover".split(","),
event(e) {
var t = e.type;
m.bounds = m.element.getBoundingClientRect();
m.x = e.pageX - m.bounds.left - scrollX;
m.y = e.pageY - m.bounds.top - scrollY;
if (t === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
else if (t === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
else if (t === "mouseout") { m.over = false }
else if (t === "mouseover") { m.over = true }
e.preventDefault();
},
start(element) {
if (m.element !== undefined) { m.remove() }
m.element = element === undefined ? document : element;
m.eventNames.forEach(name => document.addEventListener(name, mouse.event) );
document.addEventListener("contextmenu", preventDefault, false);
},
}
m = mouse;
return mouse;
})();
mouse.start(canvas);
canvas {
position : absolute;
top : 0px;
left : 0px;
}
<canvas id=canvas></canvas>
Un-animated version at 60Deg
const ctx = canvas.getContext("2d");
const colours = ["red","green","yellow","orange","blue","cyan","magenta"]
const origin = {x : canvas.width / 2, y : canvas.height / 2};
var size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
function drawPattern(size,origin,ang){
const xAx = Math.cos(ang); // define the direction of xAxis
const xAy = Math.sin(ang);
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.setTransform(xAx,xAy,-xAy,xAx,origin.x,origin.y);
function getExtent(xAx,xAy,origin){
const im = [1,0,0,1]; // inverse matrix
const dot = xAx * xAx + xAy * xAy;
im[0] = xAx / dot;
im[1] = -xAy / dot;
im[2] = xAy / dot;
im[3] = xAx / dot;
const toWorld = (x,y) => {
var point = {};
var xx = x - origin.x;
var yy = y - origin.y;
point.x = xx * im[0] + yy * im[2];
point.y = xx * im[1] + yy * im[3];
return point;
}
return [
toWorld(0,0),
toWorld(canvas.width,0),
toWorld(canvas.width,canvas.height),
toWorld(0,canvas.height),
]
}
const corners = getExtent(xAx,xAy,origin);
var startX = Math.min(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
var endX = Math.max(corners[0].x,corners[1].x,corners[2].x,corners[3].x);
var startY = Math.min(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
var endY = Math.max(corners[0].y,corners[1].y,corners[2].y,corners[3].y);
startX = Math.floor(startX / size) - 4;
endX = Math.floor(endX / size) + 4;
startY = Math.floor(startY / size) - 4;
endY = Math.floor(endY / size) + 4;
// draw the pattern
ctx.lineWidth = 5;
ctx.lineJoin = "round";
ctx.strokeStyle = "black";
for(var y = startY; y <endY; y+=1){
for(var x = startX; x <endX; x+=1){
ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
if((x + y) % 4 === 0){
ctx.fillRect(x * size,y * size,size * 2,size);
ctx.strokeRect(x * size,y * size,size * 2,size);
x += 2;
ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
ctx.fillRect(x * size,y * size, size, size * 2);
ctx.strokeRect(x * size,y * size, size, size * 2);
x += 1;
}
}
}
}
canvas.width = innerWidth;
canvas.height = innerHeight
origin.x = canvas.width / 2;
origin.y = canvas.height / 2;
size = Math.min(canvas.width * 0.2, canvas.height * 0.2);
drawPattern(size,origin,Math.PI / 3);
canvas {
position : absolute;
top : 0px;
left : 0px;
}
<canvas id=canvas></canvas>
The best way to approach this is to examine the pattern and analyse its symmetry and how it repeats.
You can look at this several ways. For example, you could rotate the patter 45 degrees so that the tiles are plain orthogonal rectangles. But let's just look at it how it is. I am going to assume you are happy with it with 45deg tiles.
Like the tiles themselves, it turns out the pattern has a 2:1 ratio. If we repeat this pattern horizontally and vertically, we can fill the canvas with the completed pattern.
We can see there are five tiles that overlap with our pattern block. However we don't need to draw them all when we draw each pattern block. We can take advantage of the fact that blocks are repeated, and we can leave the drawing of some tiles to later rows and columns.
Let's assume we are drawing the pattern blocks from left to right and top to bottom. Which tiles do we need to draw, at a minimum, to ensure this pattern block gets completely drawn (taking into account adjacent pattern blocks)?
Since we will be starting at the top left (and moving right and downwards), we'll need to draw tile 2. That's because that tile won't get drawn by either the block below us, or the block to the right of us. The same applies to tile 3.
It turns out those two are all we'll need to draw for each pattern block. Tile 1 and 4 will be drawn when the pattern block below us draws their tile 2 and 3 respectively. Tile 5 will be drawn when the pattern block to the south-east of us draws their tile 1.
We just need to remember that we may need to draw an extra column on the right-hand side, and at the bottom, to ensure those end-of-row and end-of-column pattern blocks get completely drawn.
The last thing to work out is how big our pattern blocks are.
Let's call the short side of the tile a and the long side b. We know that b = 2 * a. And we can work out, using Pythagoras Theorem, that the height of the pattern block will be:
h = sqrt(a^2 + a^2)
= sqrt(2 * a^2)
= sqrt(2) * a
The width of the pattern block we can see will be w = 2 * h.
Now that we've worked out how to draw the pattern, let's implement our algorithm.
const a = 60;
const b = 120;
const h = 50 * Math.sqrt(2);
const w = h * 2;
const h2 = h / 2; // How far tile 1 sticks out to the left of the pattern block
// Set of colours for the tiles
const colours = ["red","cornsilk","black","limegreen","deepskyblue",
"mediumorchid", "lightgrey", "grey"]
const canvas = document.getElementById("herringbone");
const ctx = canvas.getContext("2d");
// Set a universal stroke colour and width
ctx.strokeStyle = "black";
ctx.lineWidth = 4;
// Loop through the pattern block rows
for (var y=0; y < (canvas.height + h); y+=h)
{
// Loop through the pattern block columns
for (var x=0; x < (canvas.width + w); x+=w)
{
// Draw tile "2"
// I'm just going to draw a path for simplicity, rather than
// worrying about drawing a rectangle with rotation and translates
ctx.beginPath();
ctx.moveTo(x - h2, y - h2);
ctx.lineTo(x, y - h);
ctx.lineTo(x + h, y);
ctx.lineTo(x + h2, y + h2);
ctx.closePath();
ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
ctx.fill();
ctx.stroke();
// Draw tile "3"
ctx.beginPath();
ctx.moveTo(x + h2, y + h2);
ctx.lineTo(x + w - h2, y - h2);
ctx.lineTo(x + w, y);
ctx.lineTo(x + h, y + h);
ctx.closePath();
ctx.fillStyle = colours[Math.floor(Math.random() * colours.length)];
ctx.fill();
ctx.stroke();
}
}
<canvas id="herringbone" width="500" height="400"></canvas>

How to curve a texture by offsetting X Pixels

Refer to this fiddle:
// get canvas references (canvas=collar, canvas1=texture)
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvas1 = document.getElementById("canvas1");
var ctx1 = canvas1.getContext("2d");
// preload the texture and collar images before starting
var textureImg, collarImg;
var imageURLs = [];
var imagesOK = 0;
var imgs = [];
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/checkered.png");
imageURLs.push("https://dl.dropboxusercontent.com/u/139992952/stackoverflow/collar.png");
loadAllImages();
function loadAllImages(callback) {
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
img.crossOrigin = "anonymous";
imgs.push(img);
img.onload = function () {
imagesOK++;
if (imagesOK == imageURLs.length) {
textureImg = imgs[0];
collarImg = imgs[1];
start();
}
};
img.src = imageURLs[i];
}
}
function start() {
// set both canvas dimensions
canvas.width = collarImg.width;
canvas.height = collarImg.height + 5;
canvas1.width = textureImg.width;
canvas1.height = textureImg.height;
// draw the textureImg on canvas1
ctx1.drawImage(textureImg, 0, 0, canvas1.width, canvas1.height);
// curve the texture into a collar shaped curved
curveTexture(collarImg.width, collarImg.height);
// draw the collarImg on canvas
ctx.drawImage(collarImg, 0, 0);
// set compositing to source-atop
// any new drawing will ONLY fill existing non-transparent pixels
ctx.globalCompositeOperation = "source-atop";
// draw the curved texture from canvas1 onto the collar of canvas
// (the existing pixels are the collar, so only the collar is filled)
ctx.drawImage(canvas1, 0, 0);
}
function curveTexture(w, h) {
// define a quadratic curve that fits the collar bottom
// These values change if the collar image changes (+5,-32)
var x0 = 0;
var y0 = h + 5;
var cx = w / 2;
var cy = h - 32;
var x1 = w;
var y1 = h + 5;
// get a,b,c for quadratic equation
// equation is used to offset columns of texture pixels
// in the same shape as the collar
var Q = getQuadraticEquation(x0, y0, cx, cy, x1, y1);
// get the texture canvas pixel data
// 2 copies to avoid self-referencing
var imageData0 = ctx1.getImageData(0, 0, w, h);
var data0 = imageData0.data;
var imageData1 = ctx1.getImageData(0, 0, w, h);
var data1 = imageData1.data;
// loop thru each vertical column of pixels
// Offset the pixel column into the shape of the quad-curve
for (var y = 0; y < h; y++) {
for (var x = 0; x < w; x++) {
// the pixel to write
var n = ((w * y) + x) * 4;
// the vertical offset amount
var yy = parseInt(y + h - (Q.a * x * x + Q.b * x + Q.c));
// the offset pixel to read
var nn = ((w * yy) + x) * 4;
// offset this pixel by the quadCurve Y value (yy)
data0[n + 0] = data1[nn + 0];
data0[n + 1] = data1[nn + 1];
data0[n + 2] = data1[nn + 2];
data0[n + 3] = data1[nn + 3];
}
}
ctx1.putImageData(imageData0, 0, 0);
}
// Quadratic Curve: given x coordinate, find y coordinate
function getQuadraticY(x, Q) {
return (Q.a * x * x + Q.b * x + Q.c);
}
// Quadratic Curve:
// Given: start,control,end points
// Find: a,b,c in quadratic equation ( y=a*x*x+b*x+c )
function getQuadraticEquation(x0, y0, cx, cy, x2, y2) {
// need 1 more point on q-curve, so calc its midpoint XY
// Note: since T=0.5 therefore TT=(1-T)=0.5 also [so could simplify]
var T = 0.50;
var TT = 1 - T;
var x1 = TT * TT * x0 + 2 * TT * T * cx + T * T * x2;
var y1 = TT * TT * y0 + 2 * TT * T * cy + T * T * y2;
var A = ((y1 - y0) * (x0 - x2) + (y2 - y0) * (x1 - x0)) / ((x0 - x2) * (x1 * x1 - x0 * x0) + (x1 - x0) * (x2 * x2 - x0 * x0));
var B = ((y1 - y0) - A * (x1 * x1 - x0 * x0)) / (x1 - x0);
var C = y0 - A * x0 * x0 - B * x0;
return ({
a: A,
b: B,
c: C
});
}
body {
background-color: ivory;
padding:20px;
}
canvas {
border:1px solid red;
}
<h3>"Curve" a texture</h3>
<p>by offsetting Y pixels based on Q-curve</p>
<canvas id="canvas" width=300 height=300></canvas>
<p>The temporary texture canvas (canvas1)</p>
<canvas id="canvas1" width=300 height=300></canvas>
http://jsfiddle.net/m1erickson/hdXyk/
I want to convert that horizontal generated lines to vertical. I tries to change the values but unable to achieved it.
I think that "Curve" a texture by offsetting X pixels based on Q-curve might work for getting vertical lines. Please help me for this.
For more you can refer this link : How to fill pattern in canvas and curving along the shape?

html rotate entire canvas by 90 degrees

I have an image drawn to an html cavas. I would like to be able to rotate the image by 90 degrees but I can't seem to find an example on how to rotate the entire canvas image, only an object on the canvas.
Does anyone have example code to rotate an entire canvas by 90 degrees?
I accepted an anwser below but wanted to provide additional example code : http://jsfiddle.net/VTyNF/1/
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.translate(canvas.width/2,canvas.height/2)
context.rotate(90 * (Math.PI / 180));
context.beginPath();
context.rect(188 - canvas.width/2, 50 - canvas.height/2, 200, 100);
context.fillStyle = 'yellow';
context.fill();
context.lineWidth = 7;
context.strokeStyle = 'black';
context.stroke();
</script>
You would have to apply this rotation before you draw your canvas. You can't rotate anything that is already drawn.
So:
To rotate a canvas, the content.rotate() expects a value in radians. So first, lets make it simple by converting degrees to radians using:
function getRadianAngle(degreeValue) {
return degreeValue * Math.PI / 180;
}
You may want to translate the canvas first before rotating so that it's origin is set correctly.
context.translate(context.width/2,context.height/2);
Once we know what value we want, we simply rotate the canvas before we draw anything!
Please note, in your example, the rectangle you have drawn, is also being offset in the first two parameters of context.rect(X,Y,W,H)`.
I find it's easier to set widths as variables, then do simple math to re position the box automatically, notice now it sits perfectly in the center, and rotates nicely!
DEMO: http://jsfiddle.net/shannonhochkins/VTyNF/6/
Say your canvas element has id "foo". In JavaScript you could do something like this:
var canvas = document.getElementById('foo'),
context = canvas.getContext('2d');
// Rotates the canvas 90 degrees
context.rotate(90 * (Math.PI / 180));
Could you use CSS to rotate the <canvas> element with transform: rotate(90deg);?
You can easily rotate the image ninety degrees by manipulating the pixel data. If your canvas isn't square, you will have to make some choices about what the 'correct' answer will be.
Use the getImageData function to retrieve the pixels, manipulate them in the usual manner, and use putImageData to display the result.
This version doesn't require center point for 90 degree turn:
(Not as easy becase it's a 1d array with 4 values per pixel, initialDimensions means horizontal or 0, 180 rotation state vs 90, 270)
//...
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d', {willReadFrequently: true});
const img = document.createElement('img');
const file = inp.files[0]; // file from user input
img.src = URL.createObjectURL(file);
img.initialDimensions = true;
img.addEventListener('load', function () {
canvas.width = this.width;
canvas.height = this.height;
canvas.crossOrigin = "anonymous";
context.drawImage(img, 0, 0);
rotateClockwiseBy90(canvas, context, img);
}
}
function rotateClockwiseBy90(canvas, context, img) {
if (img.initialDimensions == undefined) {
img.initialDimensions = true;
}
var width, height;
if (img.initialDimensions) {
width = img.naturalWidth;
height = img.naturalHeight;
img.initialDimensions = false;
}
else {
width = img.naturalHeight;
height = img.naturalWidth;
img.initialDimensions = true;
}
const imageData = context.getImageData(0, 0, width, height);
const rotatedImageData = context.createImageData(height, width);
//[redIndex, greenIndex, blueIndex, alphaIndex]
const width4 = width * 4;
const height4 = height * 4;
for (let y = 0; y < height4; y += 4) {
for (let x = 0; x < width4; x += 4) {
rotatedImageData.data[x * height + (height4 - y -1) - 3] = imageData.data[y * width + x];
rotatedImageData.data[x * height + (height4 - y -1) - 2] = imageData.data[y * width + x + 1];
rotatedImageData.data[x * height + (height4 - y -1) - 1] = imageData.data[y * width + x + 2];
rotatedImageData.data[x * height + (height4 - y -1)] = imageData.data[y * width + x + 3];
}
}
const cw = canvas.width;
canvas.width = canvas.height;
canvas.height = cw;
context.putImageData(rotatedImageData, 0, 0);
}
If someone is trying to understand the logic:
rotatedImageData.data[x * height ...
should really be:
rotatedImageData.data[x / 4 * height * 4 ...
because for the rotated array x represents row number and height represents row length, but the result is same.
Version for counterclockwise rotation:
for (let y = 0; y < height4; y += 4) {
for (let x = 0; x < width4; x += 4) {
rotatedImageData.data[(height4 * (width - x/4) - height4 + y)] = imageData.data[y * width + x];
rotatedImageData.data[(height4 * (width - x/4) - height4 + y) + 1] = imageData.data[y * width + x + 1];
rotatedImageData.data[(height4 * (width - x/4) - height4 + y) + 2] = imageData.data[y * width + x + 2];
rotatedImageData.data[(height4 * (width - x/4) - height4 + y) + 3] = imageData.data[y * width + x + 3];
}
}

Categories

Resources