HTML5 ( canvas, javascript ) The image, makes traces - javascript

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'

Related

Canvas animation with 'lineargradient' darker on one side and doesn't resize properly?

1). This animation has a a weird effect where it colors part of the screen darker than it should be (it should be more transparent not opaque), also
2). It resizes the screen in the wrong way where it gets smaller than it should be.
Here's the code:
const canvas = document.getElementById('c');
const context = canvas.getContext('2d');
var shiftingValue = 0.1;
var wavePos = 0;
function flowGrad() {
//Clearing between frames
context.clearRect(0, 0, canvas.height, canvas.width);
//Creating linear gradient
var lineargradient = context.createLinearGradient(canvas.width/2, 0, canvas.width/2, canvas.height);
lineargradient.addColorStop(0, 'rgba(255,0,0,.2)');
lineargradient.addColorStop(wavePos, 'rgba(0,255,0,.5)');
lineargradient.addColorStop(1, 'rgba(0,0,255,.2)');
context.fillStyle = lineargradient;
//Drawing triangle
context.beginPath();
context.moveTo(0, 0);
context.lineTo(canvas.width, 0);
context.lineTo(canvas.width/2, canvas.height);
context.fill();
shiftingValue += 0.01;
//Uses absolute value of sin to generate an oscilating value.
wavePos = Math.abs(Math.sin(shiftingValue));
requestAnimationFrame(flowGrad);
}
function init() {
// Register an event listener to call the resizeCanvas() function
// each time the window is resized.
window.addEventListener('resize', resizeCanvas, false);
// Draw canvas for the first time.
requestAnimationFrame(flowGrad);
}
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
init();
and codepen link. Help much appreciated. I'm using chrome if want the effect isn't happening for you.
1). This animation has a a weird effect where it colors part of the screen darker than it should be (it should be more transparent not opaque)
You have swapped height and width; change this line:
context.clearRect(0, 0, canvas.height, canvas.width);
to
context.clearRect(0, 0, canvas.width, canvas.height);
2). It resizes the screen in the wrong way where it gets smaller than it should be.
The animation loop is invoked several times asynchronously within the resize handler. Add a reference for the animation frame request in global scope, then cancel the request every time you resize:
var ref;
// ...
function init() {
window.addEventListener('resize', resizeCanvas, false);
cancelAnimationFrame(ref); // cancel current, if any
ref = requestAnimationFrame(flowGrad);
}
As well as in the loop itself:
function flowGrad() {
// ...
ref = requestAnimationFrame(flowGrad);
}
On top of that you could always add CSS rules to avoid the canvas being offset and therefor produce scroll bars:
html, body {
margin: 0;
overflow: hidden;
}
Also remember to set the canvas size the first time inside init() by calling resizeCanvas() manually.

Can I re-draw a canvas image with its own image?

I'm learning to draw an image with canvas and getting a problem inside this example:
let img = new Image();
img.src = 'https://image.freepik.com/free-photo/hrc-siberian-tiger-2-jpg_21253111.jpg';
let a = function () {
let c1 = document.getElementById('c1'),
c2 = document.getElementById('c2');
c1.width = c2.width = 150;
c1.height = c2.width = 150;
c1.getContext('2d').drawImage(img, 0, 0, 150, 150);
c2.getContext('2d').drawImage(img, 0, 0, 150, 150);
};
let b = function () {
let c2 = document.getElementById('c2');
c2.width = 100;
c2.height = 100;
c2.getContext('2d').drawImage(c2, 0, 0, 100, 100);
};
let c = function () {
let c1 = document.getElementById('c1'),
c3 = document.getElementById('c3');
c3.width = 100;
c3.height = 100;
c3.getContext('2d').drawImage(c1, 0, 0, 100, 100);
};
a();
b();
c();
<div>
<canvas id="c1"></canvas>
</div>
<div>
<canvas id="c2"></canvas>
</div>
<div>
<canvas id="c3"></canvas>
</div>
Inside b function. I want to re-draw (resize) its own image with another size (changing width and height from 150 to 100). But it looks like it couldn't.
Then, I've tried to make another function (c). In this function, I've used the image of canvas c1 to re-draw image of canvas c3. That's ok.
So, my question is: Cannot canvas use its own image to draw an image for itself? (or maybe I've done something wrong)
Edit: At first I thought that using an HTMLCanvasElement in the drawImage() call was an incorrect argument type. That was wrong, it's a valid argument. The actual issue was that the code was not waiting for the image to load.
You need to take a look at how you are getting your initial image data for your first canvas. I would not expect it to work because you could be drawing the image data from img before it is actually loaded. You need to attach callbacks to img to wait for it to finish loading the image and then draw that image on a canvas.
Consider my example below that includes an asynchronous way to load an image, how to draw one canvas in another, and how to draw text (just to show differences between canvases).
function loadImage(path) {
return new Promise((resolve, reject) => {
let img = new Image();
img.addEventListener("load", () => {
resolve(img);
});
img.addEventListener("error", (err) => {
reject(err);
});
img.src = path;
});
}
loadImage("https://cdn.sstatic.net/Sites/stackoverflow/img/sprites.svg")
.then((img) => {
let c1 = document.getElementById("c1"),
c2 = document.getElementById("c2"),
c1Ctx = c1.getContext("2d"),
c2Ctx = c2.getContext("2d");
c1Ctx.drawImage(img, 0, 0, 150, 150);
c1Ctx.strokeText("I'm drawing on canvas 1", 25, 25);
c2Ctx.drawImage(c1, 25, 25, 100, 100);
c2Ctx.strokeText("I'm drawing on canvas 2", 25, 25);
})
.catch(console.error);
<canvas id="c1" width="150" height="150"></canvas>
<canvas id="c2" width="150" height="150"></canvas>
Another thing that you will likely run into since you want to be able to pull data out of your canvas is CORS issues. I point this out explicitly because the image that you are trying to draw onto your canvas is from a different domain (image.freepik.com in your example). Whenever you draw image data from another domain onto a canvas, that canvas becomes tainted an you can no longer use canvas' toBlob(), toDataURL(), or getImageData() methods. See: MDN: CORS enabled image.

HTML5 Canvas; Image clipping and multiple images

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/

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);
});

jquery rotate: changing the point of rotation possible? (transform canvas function)

I'm trying to do an amimation by rotating an image with the jquery roate plugin.
Unfortunately this plugin doesn't change it's point of rotation it's always centered..
and it doesn't have a parameter for this
I've found that doing this with canvas I can set it with canvas.transform() function.
I'm a begginer on working with canvas, but if somebody can give me an example how to do this I will greatly apreciate it.
here's the image rotate plugin: http://wilq32.adobeair.pl/jQueryRotate/Wilq32.jQueryRotate.html
here's made with canvas..(but kind of bad because it leaves trails.don't know why http://jsbin.com/ipeqi3/2)
This is what I have so far, indeed it rotates the image by changing the point of rotation with translate (not transform, my bad...)
What It DOESN'T do it doesn't create a smooth animation, it's very rough..How can I accomplish that?
Another thing I need is that the background HAS to be transparent
here's my code so far:
<script>
var degree=10;
function docanvas() {
var canvas = document.getElementById('canvas');
var cContext = canvas.getContext('2d');
var img = new Image();
// Create new Image object
img.src = 'http://www.gravatar.com/avatar/e555bd971bc2f4910893cd5b785c30ff?s=128&d=identicon&r=PG'; // Set source path // set img src
var cw = img.width, ch = img.height, cx = 0, cy = 0;
canvas.setAttribute('width', 300);
canvas.setAttribute('height', 300);
cContext.fillStyle = "rgba(0, 0, 255, .5)";
cContext.fillRect(0, 0, 300, 300);
cContext.translate(150, 150);
cContext.rotate(degree * Math.PI / 180);
cContext.drawImage(img, -64, -128,128,128);
degree+=5;
}
</script>
</head>
<body onload="setInterval('docanvas()',50);">
<canvas id="canvas"></canvas>
<script>
docanvas(30);
</script>
</body>
</html>

Categories