I'm trying to implement a flip image with canvas, like this but the animation is weird from the middle
update
This is only a part of the image, the final result is
From
transition
To
but am having trouble, for I am creating a small buffer for each animation and then replaced them with a time interval
// globals
animations = [];
handler;
// Main canvas, the user see this
context;
canvas;
function flip(x, y, width, height) {
// getBuffer returns {cv: canvas, cx, context }
var buffer = getBuffer(canvas.width, canvas.height),
// getNextImage return a Image object ready for use
img = getNextImage(),
ready = false,
interval;
buffer.cx.drawImage(img, x, x);
newImage = buffer.cx.getImageData(x, y, width, height);
for (var i = 5; i > 0; i--) {
smallBuff = getBuffer(width, height);
scale = -(1/i);
smallBuff.cx.translate(width / 2, 0);
smallBuff.cx.scale(scale, 1);
smallBuff.cx.putImageData(newImage, x, y);
animations.push(smallBuff);
ready = (1 === i);
};
interval = setInterval(function() {
console.log("Are you ready: ", ready);
if (ready) {
clearInterval(interval);
handler = setInterval(function() {
animation(x, y, width, height);
}, 200);
}
}, 100);
}
function animation(x, y, width, height) {
if(animations.length > 0) {
var anim = animations.pop();
anim.cx.fillstyle = '#000';
anim.cx.clearRect(x, y, width, height);
context.drawImage(anim.cv, x, y);
console.log("left:", animations.length);
} else {
clearInterval(handler);
}
}
Related
(function() {
// Creates a new canvas element and appends it as a child
// to the parent element, and returns the reference to
// the newly created canvas element
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}
function init(container, width, height, fillColor) {
var canvas = createCanvas(container, width, height);
var ctx = canvas.context;
// define a custom fillCircle method
ctx.fillCircle = function(x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
ctx.clearTo = function(fillColor) {
ctx.fillStyle = fillColor;
ctx.fillRect(0, 0, width, height);
};
ctx.clearTo(fillColor || "yel");
// bind mouse events
canvas.node.onmousemove = function(e) {
if (!canvas.isDrawing) {
return;
}
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
var radius = 40; // or whatever
var fillColor = '#ff0000';
ctx.globalCompositeOperation = 'destination-out';
ctx.fillCircle(x, y, radius, fillColor);
};
canvas.node.onmousedown = function(e) {
canvas.isDrawing = false;
};
canvas.node.onmouseup = function(e) {
canvas.isDrawing = true;
};
}
var img = document.createElement("img");
img.src= "blm.jpg";
var container = document.getElementById('canvas');
init(container, window.innerWidth, window.innerHeight, 'img');
})();
Hi im trying to make a cover for my canvas instead of just a plain black color on my code. instead i would like my image named "blm.jpg" to replace the plain black. Im not sure how to. Im very new to coding and would really appreciate any help that i can get :) I added the var img = document 5 lines from the bottom and the 4th line from the bottom, im not sure if thats meant to be there either.
thank you for any help in advance :)
Here is a simple example on how to do that, I'm just drawing grids over the background image
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
backgroundImage = document.createElement("img");
backgroundImage.src = "https://openclipart.org/image/400px/svg_to_png/260587/Surreal-Fantastic-Nature.png";
function makeLine(start_x, start_y, end_x, end_y) {
ctx.moveTo(start_x, start_y);
ctx.lineTo(end_x, end_y);
}
function drawGrid(val, color) {
ctx.beginPath();
ctx.strokeStyle = color;
for(var i = 0; i <= canvas.height; i += val) {
makeLine(0, i, canvas.width, i);
}
for(var j = 0; j <= canvas.width; j += val) {
makeLine(j, 0, j, canvas.height);
}
ctx.stroke();
}
// draw it first to make it the background and only when it loads
backgroundImage.onload = function() {
ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
drawGrid(10, "blue");
drawGrid(20, "red");
drawGrid(40, "black");
}
canvas {
border-radius: 3px;
box-shadow: 1px 1px 10px blue;
}
<canvas width="320" height="200">
You may noticed that we need to wait for the image to load for drawing it and draw other stuff, but here is another way by wrapping the whole code into the window.onload method and add our image as an HTML element and hide it of course
window.onload = function() {
var canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d"),
backgroundImage = document.querySelector("#background-img");
function makeLine(start_x, start_y, end_x, end_y) {
ctx.moveTo(start_x, start_y);
ctx.lineTo(end_x, end_y);
}
function drawGrid(val, color) {
ctx.beginPath();
ctx.strokeStyle = color;
for(var i = 0; i <= canvas.height; i += val) {
makeLine(0, i, canvas.width, i);
}
for(var j = 0; j <= canvas.width; j += val) {
makeLine(j, 0, j, canvas.height);
}
ctx.stroke();
}
// no need for the .onload since the code is executing after everything loads
ctx.drawImage(backgroundImage, 0, 0, canvas.width, canvas.height);
drawGrid(10, "blue");
drawGrid(20, "red");
drawGrid(40, "black");
}
canvas {
border-radius: 3px;
box-shadow: 1px 1px 10px blue;
}
.hidden {
display: none;
}
<canvas width="320" height="200"></canvas>
<img id="background-img" class="hidden" src="https://openclipart.org/image/400px/svg_to_png/260587/Surreal-Fantastic-Nature.png" alt="nature painting">
Im making a painting tool, and one of the feature is showing cropped image of the drawn path.
The path I have drawn(image)
For example in above the picture, the white colored path indicates what I have drawn, just like a painting tool.
Cropped image
And here is the cropped image of the path. If you look at the picture, you can see that it crops the image as if the path is closed and therefore it crops the image "area" not the path.
and here is the code
function crop({ image, points }) {
return Observable.create(observer => {
const { width, height } = getImageSize(image);
const canvas = document.createElement('canvas') as HTMLCanvasElement;
const context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
context.beginPath();
points.forEach(([x, y], idx) => {
if (idx === 0) {
context.moveTo(x, y);
} else {
context.lineTo(x, y);
}
});
context.clip();
context.drawImage(image);
...etc
}
The crop function receives points which is consisted [x coordinate, y coordinate][ ] of the drawn path.
Is there an way to show image only the path that I've painted?
That's more what is generally called a mask then, but note that both for the current clip or for the mask you want to attain, the best is to use compositing.
Canvas context has various compositing options, allowing you to generate complex compositions, from pixels's alpha value.
const ctx = canvas.getContext('2d');
const pathes = [[]];
let down = false;
let dirty = false;
const bg = new Image();
bg.onload = begin;
bg.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Serene_Sunset_%2826908986301%29.jpg/320px-Serene_Sunset_%2826908986301%29.jpg';
function begin() {
canvas.width = this.width;
canvas.height = this.height;
ctx.lineWidth = 10;
addEventListener('mousemove', onmousemove);
addEventListener('mousedown', onmousedown);
addEventListener('mouseup', onmouseup);
anim();
ctx.fillText("Use your mouse to draw a path", 20,50)
}
function anim() {
requestAnimationFrame(anim);
if(dirty) draw();
dirty = false;
}
function draw() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(bg, 0, 0);
ctx.beginPath();
pathes.forEach(path => {
if(!path.length) return;
ctx.moveTo(path[0].x, path[0].y);
path.forEach(pt => {
ctx.lineTo(pt.x, pt.y);
});
});
// old drawings will remain on where new drawings will be
ctx.globalCompositeOperation = 'destination-in';
ctx.stroke();
// reset
ctx.globalCompositeOperation = 'source-over';
}
function onmousemove(evt) {
if(!down) return;
const rect = canvas.getBoundingClientRect();
pathes[pathes.length - 1].push({
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
});
dirty = true;
}
function onmousedown(evt) {
down = true;
}
function onmouseup(evt) {
down = false;
pathes.push([]);
}
canvas {border: 1px solid}
<canvas id="canvas"></canvas>
Don't hesitate to look at all the compositing options, various cases will require different options, for instance if you need to draw multiple paths, you may prefer to render first your paths and then keep your image only where you did already drawn, using the source-atop option:
const ctx = canvas.getContext('2d');
const pathes = [[]];
pathes[0].lineWidth = (Math.random() * 20) + 0.2;
let down = false;
let dirty = false;
const bg = new Image();
bg.onload = begin;
bg.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Serene_Sunset_%2826908986301%29.jpg/320px-Serene_Sunset_%2826908986301%29.jpg';
function begin() {
canvas.width = this.width;
canvas.height = this.height;
addEventListener('mousemove', onmousemove);
addEventListener('mousedown', onmousedown);
addEventListener('mouseup', onmouseup);
anim();
ctx.fillText("Use your mouse to draw a path", 20,50)
}
function anim() {
requestAnimationFrame(anim);
if(dirty) draw();
dirty = false;
}
function draw() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
pathes.forEach(path => {
if(!path.length) return;
ctx.beginPath();
ctx.lineWidth = path.lineWidth;
ctx.moveTo(path[0].x, path[0].y);
path.forEach(pt => {
ctx.lineTo(pt.x, pt.y);
});
ctx.stroke();
});
// new drawings will appear on where old drawings were
ctx.globalCompositeOperation = 'source-atop';
ctx.drawImage(bg, 0, 0);
// reset
ctx.globalCompositeOperation = 'source-over';
}
function onmousemove(evt) {
if(!down) return;
const rect = canvas.getBoundingClientRect();
pathes[pathes.length - 1].push({
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
});
dirty = true;
}
function onmousedown(evt) {
down = true;
}
function onmouseup(evt) {
down = false;
const path = [];
path.lineWidth = (Math.random() * 18) + 2;
pathes.push(path);
}
canvas {border: 1px solid}
<canvas id="canvas"></canvas>
And also remember that you can very well have canvases that you won't append to the document that you can use as layers to generate really complex compositions. (drawImage() does accept a <canvas> as source).
I created render method after canvas image x or y is changed. But only one image is drawn after changing position, the second one isn't drawn. It also depends in what order I add that images on the screen.
App.js
my_Doge = game.addSprite("Doge", "mydoggy", 0, 700, 100, 100);
my_Cat = game.addSprite("Cat", "mycat", 0, 0, 100, 100);
function update(){
game.setPos(my_Cat, my_Cat.xcoord+=2, my_Cat.ycoord+=2);
game.setPos(my_Doge, my_Doge.xcoord+=2, my_Doge.ycoord-=2);
}
Library.js
moopleGame.prototype.setPos = function(sprite, newX, newY)
{ // Set new sprite position
sprite.xcoord = newX;
sprite.ycoord = newY;
for(var i = 0; i < this.addedSprites.length; i++)
{
this.sprite_index = 0;
if(sprite.id == this.addedSprites[i].id) // Find index of that image in array
{
this.sprite_index = i;
}
}
this.render(this.sprite_index, sprite.width, sprite.height, sprite.xcoord, sprite.ycoord);
}
moopleGame.prototype.render = function(index, width, height, x, y) // Draw image to screen
{
ctx = this.ctx;
this.fillCanvas();
this.addedSprites[index].x = x;
this.addedSprites[index].y = y;
var w = new Image();
w.src = this.addedSprites[index].src;
ctx.drawImage(w, this.addedSprites[index].x,
this.addedSprites[index].y,
this.addedSprites[index].width,
this.addedSprites[index].height);
}
Preview
So I'm trying to do some simple javascript animation with an image, and this is as far as I've gotten. I can load the file, but it simply shows the 'end result' and no actual movement (unless I step through each movement with console). I'm pretty sure I need to use setTimeout somewhere in my code, but can't seem to find the right way to use it.
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var img;
img = new Image();
img.src = "img/background4x3.png";
var sun;
sun = new Image();
sun.src = "img/rsz_sun_200x200.png";
var y = 250;
var func1 = function (y) {
ctx.clearRect(0, 0, 600, 500);
ctx.drawImage(img, 0, 0, 600, 500);
ctx.globalCompositeOperation = 'source-over';
setTimeout(100000);
ctx.drawImage(sun, 5, y);
};
img.onload = function() {
//x = 1;
while(y > 150) {
func1(y);
y = y - 5;
setTimeout(100000);
}
};
That's not a correct usage of setTimeout. It doesn't "sleep" for this time. The script just runs a function after some time. Please, read documentation before using.
setTimeout(myFunction, 1000); // runs myFunction after a second
In your case, there are several ways to do this properly. For example, you can use setInterval. Just something like this:
var y = 250, interval;
var func1 = function () {
if (y < 150) {
clearInterval(interval);
return;
}
ctx.clearRect(0, 0, 600, 500);
ctx.drawImage(img, 0, 0, 600, 500);
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(sun, 5, y);
y = y - 5;
};
img.onload = function() {
func1();
interval = setInterval(func1, 10000);
};
or using setTimeout in another way:
var y = 250, interval;
var func1 = function () {
ctx.clearRect(0, 0, 600, 500);
ctx.drawImage(img, 0, 0, 600, 500);
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(sun, 5, y);
y = y - 5;
if (y > 150) {
setTimeout(func1, 1000);
}
};
img.onload = function() {
func1();
};
I have this little JavaScript canvas snippet:
(function() {
// Creates a new canvas element and appends it as a child
// to the parent element, and returns the reference to
// the newly created canvas element
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}
function init(container, width, height, fillColor) {
var canvas = createCanvas(container, width, height);
var ctx = canvas.context;
// define a custom fillCircle method
ctx.fillCircle = function(x, y, radius, fillColor) {
this.fillStyle = fillColor;
this.beginPath();
this.moveTo(x, y);
this.arc(x, y, radius, 0, Math.PI * 2, false);
this.fill();
};
ctx.clearTo = function(fillColor) {
ctx.fillStyle = fillColor;
ctx.fillRect(0, 0, width, height);
};
ctx.clearTo(fillColor || "#ddd");
// bind mouse events
canvas.node.onmousemove = function(e) {
if (!canvas.isDrawing) {
return;
}
var x = e.pageX - this.offsetLeft;
var y = e.pageY - this.offsetTop;
var radius = 10; // or whatever
var fillColor = '#ff0000';
ctx.fillCircle(x, y, radius, fillColor);
};
canvas.node.onmousedown = function(e) {
canvas.isDrawing = true;
};
canvas.node.onmouseup = function(e) {
canvas.isDrawing = false;
};
}
var container = document.getElementById('canvas');
init(container, 200, 200, '#ddd');
})();
Now it takes the div with the id of canvas, and creates a little HTML5 canvas inside it, where the user can basically draw with their mouse. NOW thats nice... but what if I had to save whats inside the canvas with canvas.toDataURL()? As far as I know, I would have to take the canvas, and do the canvas.toDataURL() to save it to an image. But- that needs an ID right? SOOOO when I created the canvas in the createCanvas function, how would I make an id accompanying it, so I could do something like document.getElementById("idofcanvas").toDataURL() ?
Thank you in advance for your help! :)
But- that needs an ID right?
It doesn't need an ID, but an ID won't hurt (so long as it's actually unique ;-)). Any method to select a DOM element will work here. If there's only one canvas on the page, this would suffice, for example:
var canvas = document.getElementsByTagName('canvas')[0];
var imageData = canvas.toDataURL();
When I created the canvas in the createCanvas function, how would I make an id accompanying it?
You'd just set the ID property of the canvas element before returning it. Remember, you need to ensure that this value is unique!
function createCanvas(parent, width, height) {
var canvas = {};
canvas.node = document.createElement('canvas');
canvas.node.id = /* some unique value here */;
canvas.context = canvas.node.getContext('2d');
canvas.node.width = width || 100;
canvas.node.height = height || 100;
parent.appendChild(canvas.node);
return canvas;
}