HTML5 Canvas; Image clipping and multiple images - javascript

I'm fairly new to canvas and I'm having some issues.
I'm trying to accomplish one end goal: display two images in a single canvas; one image is the background, the other image is clipped to a PNG and appears over top.
I'm part way there but I've hit a wall and I don't know how to get past it.
I've create a jsFiddle for it at http://jsfiddle.net/jhp79yg9/
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var sources = {
img1: 'https://scontent-iad3-1.cdninstagram.com/hphotos-xaf1/t51.2885-15/11351598_1454928098145798_1274636321_n.jpg',
img2: 'https://igcdn-photos-c-a.akamaihd.net/hphotos-ak-xaf1/t51.2885-15/11379733_1139595366067106_273993739_n.jpg',
mask: 'http://img12.deviantart.net/65e4/i/2013/003/6/6/png_floating_terrain_by_moonglowlilly-d5qb58m.png'
};
loadImages(sources, function(images) {
context.drawImage(images.img2, 0, 0, 240, 240);
context.drawImage(images.mask, 20, 95, 500, 349);
context.globalCompositeOperation = 'source-in';
context.drawImage(images.img1, 0, 0, 540, 540);
});
<canvas id="canvas" width="540" height="540"></canvas>
In this example I've reduced the size of the first image to 140x140 from 540x540 just so I can tell if it's messing up, which it is.
What's happening is that it's not showing the second image, it's showing the 'img1' variable twice instead of showing 'img2' at all.
Can anyone offer any assistance?
I also can't tell, because it's rendering the same image, which is in the foreground and which is in the background. Help there as well would be appreciated (the clipped image should be in the foreground).
Thanks!

Another approach would be to cut out the mask from the current content and then draw the background behind the current content:
loadImages(sources, function(images) {
context.drawImage(images.img2, 0, 0, 540, 540);
context.globalCompositeOperation = 'destination-out';
context.drawImage(images.mask, 20, 95, 500, 349);
context.globalCompositeOperation = 'destination-atop';
context.drawImage(images.img1, 0, 0, 540, 540);
})
http://jsfiddle.net/jhp79yg9/6/

I couldn't clearly understand what is your requirement, but making some assumptions I hope this will give you what you need
context.drawImage(images.img2, 100, 180, 350, 350);
context.globalCompositeOperation = 'destination-atop';
context.drawImage(images.mask, 100, 180, 350, 350);
context.drawImage(images.img1, 0, 0, 540, 540);
http://jsfiddle.net/jhp79yg9/5/

Related

HTML5 ( canvas, javascript ) The image, makes traces

enter image description herefirstly sorry for my english. Second, I have a problem with my javascript.
I trying to make something like baner which changes images inside. But when i want to contiune my script, the last image out of canvas field makes me problem. Its makes leaving traces. Its my code.
<html>
<head>
<link rel="stylesheet" href="style.css">
<script>
var cat= new Image();
function init(){
cat.src = 'cat.png';
window.requestAnimationFrame(draw);
}
function draw() {
var context = document.getElementById('spin').getContext('2d');
context.clearRect(0, 0, 530, 110);
context.save();
context.translate(-1, 0);
context.drawImage(cat, 5, 0);
context.drawImage(cat, 110, 0);
context.drawImage(cat, 215, 0);
context.drawImage(cat, 320, 0);
context.drawImage(cat, 425, 0);
context.drawImage(cat, 530, 0); /// its an image with problem
window.requestAnimationFrame(draw);
}
init();
</script>
</head>
<body>
<div class="roulette">
<canvas id="spin" width="530" height="110"></canvas>
</div>
</body>
You never call context.restore(), so you are stacking a lot of CanvasStates in memory (which will make your whole application slow down in few minutes), and more directly noticeable, your context transform is never reset, meaning that at the second call the coordinate 0, 0 will be the pixels at coordinates -1, 0 and at third call -2, 0 etc. which will make your call to clearRect() miss one more pixel on the right side of the canvas every time.
It's quite unclear what you were after, but to correctly clear your context, call context.setTransform(1, 0, 0, 1, 0, 0) before calling clearRect(), this will reset the transformation matrix to its identity matrix.
Then if you want to translate your context by an ever decreasing value, store that value in a variable and use it in your call to translate().
Finally, remove that call to save() because it's not needed.
let x = 0; // the variable where we store the current offset
var cat = new Image();
function init() {
cat.src = 'https://picsum.photos/110/110';
// always wait for your image has loaded before doing anything with it
cat.onload = evt =>
window.requestAnimationFrame(draw);
}
function draw() {
var context = document.getElementById('spin').getContext('2d');
// update the offset
x -= 1;
// last image is hidden, stop the loop?
if (x < (530 + 110) * -1) {
return;
}
// reset the context matrix
context.setTransform(1, 0, 0, 1, 0, 0);
// clear the full canvas
context.clearRect(0, 0, 530, 110);
// set the updated offset
context.translate(x, 0);
context.drawImage(cat, 5, 0);
context.drawImage(cat, 110, 0);
context.drawImage(cat, 215, 0);
context.drawImage(cat, 320, 0);
context.drawImage(cat, 425, 0);
context.drawImage(cat, 530, 0);
window.requestAnimationFrame(draw);
}
init();
<canvas width="530" height="110" id="spin"></canvas>
When loading images in javascript, note that this process is asynchronous. When assigning a value to the src attribute of an Image element, you need to get the element in the onload callback function of this element. Otherwise, when drawing to the canvas, the element may not have fully loaded the corresponding image resource.
const img = new Image()
img.onload = function () {
// img fully loaded here
}
img.src = 'path/to/image/sources'

HTML5 canvas context doesn't clear with clearRect()

I have two canvases: a buffer and a display canvas whose context gets a drawImage() call with the buffer canvas. I clearRect() both canvases before adding new objects and calling drawImage(), but both display all previously-added objects. This is the object-adding code (check out the full JSFiddle below):
ctx.clearRect(0, 0, 500, 500);
buf.clearRect(0, 0, 500, 500);
img.src = bufCanvas.toDataURL();
buf.fillStyle = 'hsl(' + ~~(Math.random()*360) + ', 100%, 70%)';
buf.rect(Math.random()*w, Math.random()*h, 20, 20);
buf.fill();
ctx.drawImage(bufCanvas, 0, 0);
JSFiddle: http://jsfiddle.net/3movoayv/10/
The top is the buffer (click your mouse there to generate new rects), the middle is the display canvas that I copy the buffer to, and the bottom is an img element that displays the contents of the buffer canvas after I call clearRect() (which always correctly displays nothing).
What's going on here?
You've forgotten to begin a new path each time using .beginPath() (prevents the event stacking issue you're witnessing):
bufCanvas.addEventListener('mousedown', function (e) {
ctx.clearRect(0, 0, 500, 500);
buf.clearRect(0, 0, 500, 500);
img.src = bufCanvas.toDataURL();
buf.beginPath(); // < begin a new path
buf.fillStyle = 'hsl(' + ~~(Math.random()*360) + ', 100%, 70%)';
buf.rect(Math.random()*w, Math.random()*h, 20, 20);
buf.fill();
ctx.drawImage(bufCanvas, 0, 0);
});

Layer.remove(Image); not working in Kinetic.js

I am using the latest kineticjs.(v.3.10)
Here is the problem
I am using a single function to send images to the canvas which are outside the canvas on click.
I am making the images draggable and all..
I have also added a remove image function on double click.
when I double click on any image..
the last added image gets removed and after that if I try to click on some other image..
I get this error: TypeError: this.children[child.index] is undefined
Here is a little code.:
Using this function to fetch the path from another file using ajax
function loadajax(imgpath,imgid){
sources = {
yoda1 : imgpath,
};
loadImages(sources,initStage1);
};
Sources function:----
function loadImages(sources, callback){
var images = {};
var loadedImages = 0;
var numImages = 0;
for (var src in sources) {
numImages++;
}
for (var src in sources) {
images[src] = new Image();
images[src].onload = function(){
if (++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
Here is the function that I'm using to remove/drag and drop/etc..
function initStage1(images){
yodaGroup1 = new Kinetic.Group({
x: 100,
y: 110,
draggable: true,
});
layery = new Kinetic.Layer();
layery.add(yodaGroup1);
stage.add(layery);
var yoda1 = new Kinetic.Image({
image: images.yoda1,
x: 0,
y: 0,
width: 100,
height: 120,
name:"image",
detectionType:"Pixel"
});
yodaGroup1.add(yoda1);
yodaGroup1.on("dragstart", function() {
yodaGroup1.moveToTop();
layery.draw();
});
yodaGroup1.on("dblclick dbltap", function() {
layery.remove(yodaGroup1);
layery.draw();
});
yodaGroup1.on("dragend", function() {
layery.draw();
yoda1.saveImageData();
});
addAnchor(yodaGroup1,0, 0, "topLeft");
addAnchor(yodaGroup1, 100, 0, "topRight");
addAnchor(yodaGroup1, 100, 120, "bottomRight");
addAnchor(yodaGroup1, 0, 120, "bottomLeft");
stage.draw();
yoda1.saveImageData();
}
NOW according to this function
I should be able to add the images to the canvas(which is working fine)
Should be able to move one image over another when I drag it (.ie.moveToTop function)_Not working
Should be able to remove the images on double click(working only for the latest added image)
Please help
Thanks
layery.remove(yodaGroup1);
refers to this function :
remove() remove layer from stage
http://www.kineticjs.com/docs/symbols/Kinetic.Layer.php#remove
There is no function to remove only one node contained in the layer :/
To remove a node contained in a layer just use this:
node.remove();
So in your case you would do:
yodaGroup1.remove();
layery.draw();
http://kineticjs.com/docs/Kinetic.Image.html

How can i load an array of images in html5 canvas and swap one of the images on button click

i'm trying to build a bike configurator. This is my problem.
How can i load and array of images to construct the bike and another array with parts on the right from which when the user click i take the part and load it on the left where is the bike without preloading the entire canvas again. the new image will be the same width and height and should be on the same x/y position. This is how i load and array of images, but then i don't know how to swap them. I know that i can't clear the image that is loaded on the canvas and i have to draw the new one on top but i don't know how.
<script type="text/javascript" charset="utf-8">
function loadImages(sources, callback){
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for (var src in sources) {
numImages++;
}
for (var src in sources) {
images[src] = new Image();
images[src].onload = function(){
if (++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
window.onload = function(images){
var canvas = document.getElementById("config");
var context = canvas.getContext("2d");
var sources = {
one: "one.jpg",
two: "two.jpg"
};
loadImages(sources, function(images){
context.drawImage(images.one, 100, 100, 180, 200);
context.drawImage(images.two, 280, 100, 180, 200);
});
};
</script>
How can i change image two.jpg on button click. The button should be outside the canvas.
button.addEventListener('click', function () {
canvas.width = canvas.width; // redraws canvas
sources.two = 'three.jpg';
// fyi, you are drawing the images as many times as there are sources.
loadImages(sources, function(images){
context.drawImage(images.one, 100, 100, 180, 200);
context.drawImage(images.two, 280, 100, 180, 200);
});
}, false);

canvas shown on IE but not chrome and firefox

I want to use canvas to make an image to grayscale. There are a number of examples. But it has problem with my latest Chrome and Firefox. Surprisingly, IE9 is good. Is it the problem of my code?
And here is my code:
function draw() {
canvas = document.getElementById('canvas')
ctx = canvas.getContext('2d');
image = new Image();
image.src = 'ichiro.jpg';
ctx.drawImage(image, 0, 0);
imgd = ctx.getImageData(0, 0, 480, 400);
for (i=0; i<imgd.data.length; i+=4) {
grays = imgd.data[i]*.3 + imgd.data[i+1]*.6 + imgd.data[i+2]*.1;
imgd.data[i ] = grays; // red
imgd.data[i+1] = grays; // green
imgd.data[i+2] = grays; // blue
}
ctx.putImageData(imgd, 0, 0);
imggray = new Image();
imggray.src = canvas.toDataURL();
imggray.onload = function() {
ctx.drawImage(imggray, 0, 0);
}
}
I am new to HTML5 and javascript. So any help will be appreciated.
EDIT:
Sorry, I misread your question. It is almost certainly because of a security error. You are not allowed to use getImageData if you have drawn an image to the canvas that is from a different "origin" (a different domain or from your local file system). In Chrome locally you can get around it if you do:
C:\Users\root\AppData\Local\Google\Chrome\Application\chrome.exe --allow-file-access-from-files
There's something called the origin-clean flag and it is removed once you drawImage from a different origin. All files are of a different origin for (good) security reasons.
Original answer:
You need to wait for image to load:
working example: http://jsfiddle.net/SYLW2/1107/
...
// this now happens only after the image is loaded:
image.onload = function() {
ctx.drawImage(image, 0, 0);
imgd = ctx.getImageData(0, 0, 480, 400);
for (i=0; i<imgd.data.length; i+=4) {
grays = imgd.data[i]*.3 + imgd.data[i+1]*.6 + imgd.data[i+2]*.1;
imgd.data[i ] = grays; // red
imgd.data[i+1] = grays; // green
imgd.data[i+2] = grays; // blue
}
ctx.putImageData(imgd, 0, 0);
imggray = new Image();
imggray.src = canvas.toDataURL();
imggray.onload = function() {
ctx.drawImage(imggray, 0, 0);
}
}
image.src = 'http://placekitten.com/400/400';

Categories