everyone. I'm trying to write a script that would render images from file inputs on a canvas in a row.
First I cycle through images to calculate the canvas width (because the canvas is wiped on resizing). Then cycle again to render the images.
canvas.width = 0;
let x = 0,
y = 0,
totalWidth = 0;
for (let i = 0; i < document.querySelectorAll(".has-file").length; i++) {
let input = document.querySelectorAll(".has-file")[i];
let image = input.files[0];
let img = new Image();
console.log(img);
img.src = window.URL.createObjectURL(image);
img.addEventListener('load', () => {
console.log(img);
let newWidth = img.width * canvas.height / img.height;
totalWidth += newWidth;
canvas.width = totalWidth;
}, false);
};
for (let i = 0; i < document.querySelectorAll(".has-file").length; i++) {
let input = document.querySelectorAll(".has-file")[i];
let image = input.files[0];
let img = new Image();
img.src = window.URL.createObjectURL(image);
img.addEventListener('load', () => {
let newWidth = img.width * canvas.height / img.height;
ctx.drawImage(img, x, y, newWidth, canvas.height);
x += newWidth;
}, false);
};
}
The app behaves weird, the images are not always rendered, and when they do, not always where they supposed to be.
First problem with the code is that you're loading images twice, and the randomness is due to the fact that image loading can be ambiguous. Check out this jsfiddle. I have used text input instead of files, drawing takes place when it is the last image otherwise resizing the canvas can cause canvas to reset, losing the previous draw.
var canvas = document.getElementById("canvas");
const ctx = canvas.getContext('2d', {
antialias: false,
depth: false
});
canvas.width = 0;
let x = 0,
y = 0,
totalWidth = 0;
let obj = [];
let k = 0;
for (let i = 0; i < document.querySelectorAll(".has-file").length; i++) {
let input = document.querySelectorAll(".has-file")[i];
let image = input.value;
let img = new Image();
img.src = image;//window.URL.createObjectURL(image);
img.addEventListener('load', () => {
console.log(img);
let newWidth = img.width * canvas.height / img.height;
totalWidth += newWidth;
canvas.width = totalWidth;
obj.push({img: img, x: x, y: y, newWidth: newWidth, height: canvas.height});
k++;
x += newWidth;
if (k == document.querySelectorAll(".has-file").length )
draw();
}, false);
};
function draw() {
for (var i = 0; i < obj.length; i++) {
ctx.drawImage(obj[i].img, obj[i].x, obj[i].y, obj[i].newWidth, obj[i].height);
}
}
<input style="display: none;" class="has-file" value="https://i.imgur.com/I86rTVl.jpg" />
<input style="display: none;" class="has-file" value="https://images.unsplash.com/photo-1446292267125-fecb4ecbf1a5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80" />
<canvas id="canvas"></canvas>
Related
I want to show four canvas from the same image
I'm working with an image which I need to be splitted into four pieces. I don't know the actual dimensions of the image so I need it to be dynamic. I already get this far, and I think the first piece is working fine, but I don't know why it is not working for the rest of the pieces. Could you point where the error could be?
I am new working with canvas, so my code is based on this answer:
https://stackoverflow.com/a/8913024/6929416
var image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png';
image.onload = cutImageUp;
function cutImageUp() {
var natW = image.width / 2;
var natH = image.height / 2;
var widthOfOnePiece = jQuery(window).width() / 2;
var heightOfOnePiece = jQuery(window).height() / 2;
var imagePieces = [];
for (var x = 0; x < 2; ++x) {
for (var y = 0; y < 2; ++y) {
var canvas = document.createElement('canvas');
canvas.width = widthOfOnePiece;
canvas.height = heightOfOnePiece;
var context = canvas.getContext('2d');
context.drawImage(image,
x * natW, y * natH,
natW, natH,
x * canvas.width, y * canvas.height,
canvas.width, canvas.height
);
/*drawImage(image,
sx, sy,
sWidth, sHeight,
dx, dy,
dWidth, dHeight);*/
imagePieces.push(canvas.toDataURL());
}
}
// imagePieces now contains data urls of all the pieces of the image
// load one piece onto the page
var anImageElement = document.getElementById('testing');
var anImageElement2 = document.getElementById('testing2');
var anImageElement3 = document.getElementById('testing3');
var anImageElement4 = document.getElementById('testing4');
anImageElement.src = imagePieces[0];
anImageElement2.src = imagePieces[1];
anImageElement3.src = imagePieces[2];
anImageElement4.src = imagePieces[3];
}
img{ border: 1px solid; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<img id="testing" src="">
<img id="testing2" src="">
<img id="testing3" src="">
<img id="testing4" src="">
</section>
I expect the canvas dimensions to fit on the screen, so I set them to half of the windows width and height.
The parameters for drawImage are
drawImage(
source,
sourceX, sourceY, sourceWidth, sourceHeight,
destinationX, destinationY, destinationWidth, destinationHeight
)
In your code, you are setting destinationX to x * canvas.width and destinationY to y * canvas.height. This means that for every iteration where either x or y are not 0, you are drawing outside of the destination area, that is for every parts but the first one.
Simply hard-code destinationX-Y to 0and you'll be good.
var image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png';
image.onload = cutImageUp;
function cutImageUp() {
var natW = image.width / 2;
var natH = image.height / 2;
var widthOfOnePiece = jQuery(window).width() / 2;
var heightOfOnePiece = jQuery(window).height() / 2;
var imagePieces = [];
for (var x = 0; x < 2; ++x) {
for (var y = 0; y < 2; ++y) {
var canvas = document.createElement('canvas');
canvas.width = widthOfOnePiece;
canvas.height = heightOfOnePiece;
var context = canvas.getContext('2d');
context.drawImage(image,
x * natW, y * natH,
natW, natH,
0, 0,
canvas.width, canvas.height
);
/*drawImage(image,
sx, sy,
sWidth, sHeight,
dx, dy,
dWidth, dHeight);*/
imagePieces.push(canvas.toDataURL());
}
}
// imagePieces now contains data urls of all the pieces of the image
// load one piece onto the page
var anImageElement = document.getElementById('testing');
var anImageElement2 = document.getElementById('testing2');
var anImageElement3 = document.getElementById('testing3');
var anImageElement4 = document.getElementById('testing4');
anImageElement.src = imagePieces[0];
anImageElement2.src = imagePieces[1];
anImageElement3.src = imagePieces[2];
anImageElement4.src = imagePieces[3];
}
img{ border: 1px solid; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<img id="testing" src="">
<img id="testing2" src="">
<img id="testing3" src="">
<img id="testing4" src="">
</section>
But note that toDataURL should almost never be used. Instead one should always prefer faster non-blocking and memory friendly toBlob().
Also, no need to create 4 canvases here, you can reuse the same at every round.
And finally, you might want to preserve your image's aspect ratio:
var image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png';
image.onload = cutImageUp;
function cutImageUp() {
var natW = image.width / 2;
var natH = image.height / 2;
var widthOfOnePiece = jQuery(window).width() / 2 - 20;
// preserve aspect ratio
var heightOfOnePiece = widthOfOnePiece * (natH / natW);
var canvas = document.createElement('canvas');
canvas.width = widthOfOnePiece;
canvas.height = heightOfOnePiece;
var context = canvas.getContext('2d');
var promises = [];
for (var y = 0; y < 2; ++y) {
for (var x = 0; x < 2; ++x) {
context.clearRect(0, 0, canvas.width, canvas.height);
context.drawImage(image,
x * natW, y * natH,
natW, natH,
0, 0,
canvas.width, canvas.height
);
promises.push(new Promise(function(resolve, reject) {
canvas.toBlob(function(blob) {
if (!blob) reject();
resolve(blob);
});
}));
}
}
return Promise.all(promises).then(function(blobs) {
// imagePieces now contains data urls of all the pieces of the image
// load one piece onto the page
var anImageElement = document.getElementById('testing');
var anImageElement2 = document.getElementById('testing2');
var anImageElement3 = document.getElementById('testing3');
var anImageElement4 = document.getElementById('testing4');
anImageElement.src = URL.createObjectURL(blobs[0]);
anImageElement2.src = URL.createObjectURL(blobs[1]);
anImageElement3.src = URL.createObjectURL(blobs[2]);
anImageElement4.src = URL.createObjectURL(blobs[3]);
})
}
img {
border: 1px solid;
display: inline-block;
}
body{margin: 0;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section>
<img id="testing" src="">
<img id="testing2" src="">
<img id="testing3" src="">
<img id="testing4" src="">
</section>
I have been trying to put filters (like brightness, contrast) on image loaded in canvas.
Below is a snippet of what I am doing
/******Loading Image in Canvas******/
let canvas = document.getElementById('demo');
let ctx = canvas.getContext('2d');
var reader = new FileReader();
reader.onload = function(event){
var img = new Image();
img.onload = function(){
canvas.width = 220;
canvas.height = 250;
ctx.drawImage(img,0,0,canvas.width,canvas.height);
}
img.src = event.target.result;
self.oldImg = event.target.result;
}
reader.readAsDataURL(xevents.target.files[0]);
I have added brightness, contrast logic and is working fine on the image.
The Issue actually arise when I do any operation on image like resizing the canvas(using jquery UI resizeable). The filters applied get lost.
Below is the snippet for redrawing on canvas when resizes.
$(".stretch").resizable({ resize: function(event, ui) {
$("#demo", this).each(function() {
$(this).attr({ width: ui.size.width, height: ui.size.height });
self.reDraw(this);
});
} });
reDraw () {
var img = new Image();
var canvas = document.getElementById('demo');
var c = canvas.getContext("2d");
img.src = this.oldImg;
c.drawImage(img, 0, 0, canvas.width, canvas.height);
}
I believe, since it is creating again a new image object (new Image()), the filters are getting lost.
Can anyone help me on this. I want the filters to be retained as I resize the canvas.
Appreciated !! Thanks
Update
process (type, amount) {
var img = new Image();
var canvas = document.getElementById('demo');
var c = canvas.getContext("2d");
img.src = this.oldImg;
c.drawImage(img, 0, 0, canvas.width, canvas.height);
let imgData = c.getImageData(0, 0, canvas.width, canvas.height);
let data = imgData.data;
if(type === 'b') {
for (let i = 0; i < data.length; i += 4) {
data[i] += amount;
data[i + 1] += amount;
data[i + 2] += amount;
}
}
if(type === 'c') {
amount = (amount/100) + 1; //convert to decimal & shift range: [0..2]
let intercept = 128 * (1 - amount);
for(let i=0;i<data.length;i+=4){ //r,g,b,a
data[i] = data[i]*amount + intercept;
data[i+1] = data[i+1]*amount + intercept;
data[i+2] = data[i+2]*amount + intercept;
}
}
c.putImageData(imgData, 0, 0);
}
I am using a face tracking library (tracking.js) to capture a video stream and place an image on top of the face.
The image is drawn on a canvas, which has the same width and height as the video therefore, the overlay.
I am trying to take a picture and video of the stream + canvas image, however O can only get a crude implementation of the stream and image that is distorted.
Here is a CodePen
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const tracker = new tracking.ObjectTracker('face');
const flowerCrownButton = document.getElementById('flower-crown');
tracker.setInitialScale(1);
tracker.setStepSize(2.7);
tracker.setEdgesDensity(.2);
const img = document.createElement("img");
img.setAttribute("id", "pic");
img.src = canvas.toDataURL();
let filterX = 0;
let filterY = 0;
let filterWidth = 0;
let filterHeight = 0;
function changePic(x, y, width, height, src) {
img.src = src;
filterX = x;
filterY = y;
filterWidth = width;
filterHeight = height;
}
function flowerCrown() {
changePic(0, -0.5, 1, 1, 'https://s3-us-west-
2. amazonaws.com / s.cdpn.io / 450347 / flower - crown.png ')
}
flowerCrownButton.addEventListener('click', flowerCrown);
//listen for track events
tracker.on('track', function(event) {
//if (event.data.length === 0) {
//alert("No objects were detected in this frame.");
//} else {
context.clearRect(0, 0, canvas.width, canvas.height)
event.data.forEach(rect => {
context.drawImage(img, rect.x + (filterX * rect.width),
rect.y + (filterY * rect.height),
rect.width * filterWidth,
rect.height * filterHeight
)
})
//}// end of else
});
//start tracking
tracking.track('#video', tracker, {
camera: true
})
const canvas2 = document.getElementById('canvas2');
const context2 = canvas2.getContext('2d');
const video = document.getElementById("video");
video.addEventListener("loadedmetadata", function() {
ratio = video.videoWidth / video.videoHeight;
w = video.videoWidth - 100;
h = parseInt(w / ratio, 10);
canvas2.width = w;
canvas2.height = h;
}, false);
function snap() {
context2.drawImage(video, 10, 5);
context2.drawImage(img, 10, 10)
}
}
Any ideas? I prefer to use the media recorder API and have tried it, but again could not get a stream or picture with the image filter overlay.
Thanks and please don't be snarky :)
var canvas = document.getElementById("canvas");
wheel = canvas.getContext("2d");
for (var i = 0; i < 10; i++) {
var img = new Image;
img.src = image;
wheel.drawImage(img, -170, -10, 140, 140);
}
My above code draw only 9(Nine) images but lastOne(10th) image not drawing on canvas. I tried above code but not getting success. Anyone can know solution for this problem.
Can any one help me to find out online JSFiddle Demo which draw image on canvas in loop.
Perhaps your image isn't finished loading before your first draw attempt.
Here is the syntax to make sure your image has finished loading:
var canvas = document.body.appendChild(document.createElement("canvas"));
canvas.width = 1100;
canvas.height = 110;
var wheel = canvas.getContext("2d");
var img = document.createElement("img");
img.onload = function() {
for (var i = 0; i < 10; i++) {
wheel.drawImage(img, 5 + 110 * i, 5);
}
};
img.src = "https://placeholdit.imgix.net/~text?txtsize=60&txt=1&w=100&h=100";
EDIT 1 - PROMISES
Working with multiple images:
List sources
map to generates DOM nodes
Setup a Promise per DOM node in a Promise.all
Then draw images
var canvas = document.body.appendChild(document.createElement("canvas"));
canvas.width = 1100;
canvas.height = 110;
var wheel = canvas.getContext("2d");
var images = [
"https://placeholdit.imgix.net/~text?txtsize=60&txt=1&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=2&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=3&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=4&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=5&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=6&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=7&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=8&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=9&w=100&h=100",
"https://placeholdit.imgix.net/~text?txtsize=60&txt=10&w=100&h=100",
]
.map(function(i) {
var img = document.createElement("img");
img.src = i;
return img;
});
Promise.all(images.map(function(image) {
return new Promise(function(resolve, reject) {
image.onload = resolve;
});
}))
.then(function() {
for (var i = 0; i < images.length; i++) {
var img = images[i];
wheel.drawImage(img, 5 + 110 * i, 5);
}
});
I would do the similar approach to Emil, only difference having a method that loads them individually and once the image is loaded, try to load the next one and so on ...
take a look:
var canvas = document.getElementById("canvas");
// setting canvas size
canvas.height = window.innerHeight - 10;
canvas.width = window.innerWidth - 10;
var image_test = document.querySelector(".hidden");
var count = 0;
var total_images = 10;
var wheel = canvas.getContext("2d");
function loadImage() {
var img = new Image();
img.onload = function() {
wheel.drawImage( this , 105 * count , 0 , 100, 100);
if( count < total_images ) {
count++;
loadImage();
}
}
// img.src = "image-" + count + ".jpg";
img.src = "https://placeholdit.imgix.net/~text?txtsize=60&txt=1&w=100&h=100";
}
loadImage();
https://jsfiddle.net/gugalondon/r5xw6u7L/7
I'm trying to shatter image to pieces using canvas , this is my code :
var image = new Image();
image.src = 'koals.jpg';
image.onload = cutImageUp;
var imagePieces = [];
function cutImageUp() {
for(var x = 0; x < 5; x++) {
for(var y = 0; y < 5; y++) {
var canvas = document.createElement('canvas');
canvas.width = 50+"px";
canvas.height = 50+"px";
var context = canvas.getContext('2d');
context.drawImage(image, x *50, y * 50, 50, 50, 0, 0, 50, 50);
imagePieces.push(canvas.toDataURL());
}
}
var anImageElement = document.getElementById('img');
anImageElement.src = imagePieces[0];
}
the problem is that image is returning "blank image" (a.k.a its not loaded);
Console ain't throwing any error.
I'm opening it as local html file , both image and html document are in the same folder so there shouldn't be problem with toDataURL() not returning data due to image being on another domain.
Your problem is with the 50+"px" for canvas width and height, remove the +"px" part and your good.
From w3Specs:
The canvas element has two attributes to control the size of the coordinate space: width and height. These attributes, when specified, must have values that are valid non-negative integers.
var image = new Image();
image.src = 'koals.jpg';
image.onload = cutImageUp;
var imagePieces = [];
function cutImageUp() {
for(var x = 0; x < 5; x++) {
for(var y = 0; y < 5; y++) {
var canvas = document.createElement('canvas');
canvas.width = 50;
canvas.height = 50;
var context = canvas.getContext('2d');
context.drawImage(image, x *50, y * 50, 50, 50, 0, 0, 50, 50);
imagePieces.push(canvas.toDataURL());
}
}
var anImageElement = document.getElementById('img');
anImageElement.src = imagePieces[0];
}
jsfiddle