I'm trying to render multiple images on a single canvas, but seem to be overlooking something:
var canvas = document.createElement('canvas');
canvas.width = 4086;
canvas.height = 4086;
var ctx = canvas.getContext('2d');
var loadedImages = 0;
for (var j=0; j<4; j++) {
var url = 'https://via.placeholder.com/2048x2048';
var img = new Image();
img.onload = function(j) {
var w = 2048;
var h = 2048;
var x = (j % 2) * w;
var y = (Math.floor(j / 2)) * h;
console.log(img, x, y, w, h)
ctx.drawImage(img, x, y, w, h, x, y, w, h);
if (++loadedImages == 4) {
document.body.appendChild(canvas)
}
}.bind(null, j)
img.src = url;
}
canvas {
width: 500px;
}
Does anyone see what I'm missing? Any help others can offer would be greatly appreciated!
You're using the long-form API to draw the image, which is designed to select a portion of your source image to draw onto the canvas. You really only need the "short" version, because (I think) you want to draw the entire image four times.
When you use the long form, the code is requesting pixels that don't exist in the source image because it's only 2048x2048 pixels in size.
Related
I am new to html5 canvas. I want to draw one canvas grid and fill each grid square with an image from an API response. I have following code to draw the grid but I am struggling to fill each square with image.
This is js code:
window.onload = function(){
var c= document.getElementById('canvas');
var ctx=c.getContext('2d');
ctx.strokeStyle='white';
ctx.linWidth=4;
for(i=0;i<=600;i=i+60)
{
ctx.moveTo(i,0);
ctx.lineTo(i,600);
ctx.stroke();
}
for(j=0; j<=600; j=j+60)
{
ctx.moveTo(0,j);
ctx.lineTo(600,j);
ctx.stroke();
}
}
This code helps me drawing canvas grid but how to access each square and fill it with the image. I referred links related to this but seems difficult to understand. Can somebody please help me with this?
It's hard to answer your question without knowing exactly how the image is being returned by the api response. Assuming the api response is returning the image itself (not the image data in JSON or something like that) here is a solution:
html:
<canvas id="canvas" width="600" height="600"></canvas>
javascript:
window.onload = function() {
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "green";
ctx.lineWidth = 4;
//draw grid
for (let i = 0; i <= 10; i++) {
const x = i*60;
ctx.moveTo(x, 0);
ctx.lineTo(x, canvas.height);
ctx.stroke();
const y = i*60;
ctx.moveTo(0, y);
ctx.lineTo(canvas.width, y);
ctx.stroke();
}
//draw images
const p = ctx.lineWidth / 2; //padding
for (let xCell = 0; xCell < 10; xCell++) {
for (let yCell = 0; yCell < 10; yCell++) {
const x = xCell * 60;
const y = yCell * 60;
const img = new Image();
img.onload = function() {
ctx.drawImage(img, x+p, y+p, 60-p*2, 60-p*2);
};
//TODO: set img.src to your api url instead of the dummyimage url.
img.src = `https://dummyimage.com/60x60/000/fff&text=${xCell},${yCell}`;
}
}
};
Working example:
https://codepen.io/rockysims/pen/dLZgBm
I am trying to add four rotating images to an animated background.
I can only get one image working correctly with my code below.
How can I add in the other three images?
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var img = document.createElement('img');
img.onload = function(){
render();
}
img.src = 'nano3.png';
function drawImage(img,x,y,r,sx,sy){
sx=sx||0;
sy=sy||0;
r=(r*Math.PI/180)||0;
var cr = Math.cos(r);
var sr = Math.sin(r);
ctx.setTransform(cr,sr,-sr,cr,x-(cr*sx-sr*sy),y-(sr*sx+cr*sy));
ctx.drawImage(img,1,2);
}
var r = 1;
function render(){
requestAnimationFrame(render);
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0,0,800,800);
drawImage(img,50,50,r++,img.width/2,img.height/2);
}
This should help you out, I just created an object known as rotatingimage which stores a location, an image and its current rotation. We call the 'draw' method in a 'setInterval' function call which deals with rotating the canvas and then drawing the sprite correctly.
Just a note rotating many images can cause the canvas to lag also the CurrentRotation variable never gets reset to 0 when it reaches >359 so the CurrentRotation variable will keep going higher and higher, you may want to fix that in the RotatingImage.prototype.Draw function
jsFiddle:https://jsfiddle.net/xd8brfrk/
Javascript
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
function RotatingImage(x, y, spriteUrl, rotationSpeed) {
this.XPos = x;
this.YPos = y;
this.Sprite = new Image();
this.Sprite.src = spriteUrl;
this.RotationSpeed = rotationSpeed;
this.CurrentRotation = 0;
}
RotatingImage.prototype.Draw = function(ctx) {
ctx.save();
this.CurrentRotation += 0.1;
ctx.translate(this.XPos + this.Sprite.width/2, this.YPos + this.Sprite.height/2);
ctx.rotate(this.CurrentRotation);
ctx.translate(-this.XPos - this.Sprite.width/2, -this.YPos - this.Sprite.height/2);
ctx.drawImage(this.Sprite, this.XPos, this.YPos);
ctx.restore();
}
var RotatingImages = [];
RotatingImages.push(new RotatingImage(50, 75, "http://static.tumblr.com/105a5af01fc60eb94ead3c9b342ae8dc/rv2cznl/Yd9oe4j3x/tumblr_static_e9ww0ckmmuoso0g4wo4okosgk.png", 1));
RotatingImages.push(new RotatingImage(270, 25, "http://static.tumblr.com/105a5af01fc60eb94ead3c9b342ae8dc/rv2cznl/Yd9oe4j3x/tumblr_static_e9ww0ckmmuoso0g4wo4okosgk.png", 1));
RotatingImages.push(new RotatingImage(190, 180, "http://static.tumblr.com/105a5af01fc60eb94ead3c9b342ae8dc/rv2cznl/Yd9oe4j3x/tumblr_static_e9ww0ckmmuoso0g4wo4okosgk.png", 1));
RotatingImages.push(new RotatingImage(100, 270, "http://static.tumblr.com/105a5af01fc60eb94ead3c9b342ae8dc/rv2cznl/Yd9oe4j3x/tumblr_static_e9ww0ckmmuoso0g4wo4okosgk.png", 1));
setInterval(function() {
ctx.fillStyle = "#000"
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < RotatingImage.length; i++) {
var rotatingImage = RotatingImages[i];
rotatingImage.Draw(ctx);
}
}, (1000 / 60));
you can use save and restore to apply different transform to your drawing
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore
I am trying to make a variant of the boomshine game in javascript and everything works when I draw a circular shape with the arc function. However when i try to replace the arc function with the drawImage function to use a coin image instead of a circular shape I start having problems when I clear the canvas to delete the previous drawn circular shapes. If I don't clear the canvas before rendering the images, the images are drawn on the canvas except the old images are still on the canvas. But when i do clear the canvas before rendering the images again, nothing is drawn on the canvas.
I have included screenshots, the links are below.
This is how I clear the canvas:
var ctx = game.context;
ctx.fillStyle = "darkgray";
ctx.fillRect(0, 0, game.canvas.width, game.canvas.height);
This is how i draw the image:
function drawImageBall(x,y,radius,startAngle,color)
{
var img = document.createElement('img');
img.src = 'img/coin-icon.png';
var tmpCtx= game.context;
var ax = x-radius;
var ay = y-radius;
img.onload = function() {
tmpCtx.save();
tmpCtx.beginPath();
tmpCtx.arc(x, y, radius, 0, Math.PI * 2, true);
tmpCtx.closePath();
tmpCtx.clip();
tmpCtx.drawImage(img, ax, ay, img.width, img.height);
tmpCtx.beginPath();
tmpCtx.arc(0, 0, radius, 0, Math.PI * 2, true);
tmpCtx.clip();
tmpCtx.closePath();
tmpCtx.restore();
};
}
Clearing canvas (screenshot)
Without clearing canvas (screenshot)
Keep in mind that downloading the img will take some time.
During that downloading time, Javascript does not stop(!). Instead JS will continue executing any following code. This is causing your unexpected problems.
So download the img just once at the start of your app. That way your drawImage will be done in the order that you expect because there will be no delay while your image is downloading.
Using your code, I maked some changes, I removed the tmpTcx.clip(), look the fidlle. Tip: For performace questions you don't need load the image every time that you want write the canvas.
Poor Example: https://jsfiddle.net/wf4z0d2h/1/
function clearCanvas(){
var ctx = game.context;
ctx.fillStyle = "darkgray";
ctx.fillRect(0, 0, game.canvas.width, game.canvas.height);
}
function drawImageBall(x,y,radius,startAngle,color)
{
if(x == undefined){x = 100;}
if(y == undefined){y = 100;}
if(radius == undefined){radius = 40;}
//var img = document.createElement('img');
//img.src = 'img/coin-icon.png';
//img.src = "http://ps2.lansa.com/images/icons/normal/256/coin_256.png";
var tmpCtx= game.context;
var ax = x-radius;
var ay = y-radius;
//img.onload = function() {
tmpCtx.save();
tmpCtx.beginPath();
tmpCtx.arc(x, y, radius, 0, Math.PI * 2, true);
tmpCtx.stroke(); // Draw it
tmpCtx.closePath();
//tmpCtx.clip();
tmpCtx.drawImage(img, ax, ay, img.width, img.height);
//tmpCtx.beginPath();
//tmpCtx.arc(0, 0, radius, 0, Math.PI * 2, true);
////tmpCtx.clip();
//tmpCtx.stroke(); // Draw it
//tmpCtx.closePath();
//tmpCtx.restore();
//};
}
var img = document.createElement('img');
//img.src = 'img/coin-icon.png';
img.src = "http://ps2.lansa.com/images/icons/normal/256/coin_256.png";
//drawImageBall();
img.onload = function(){
x = 0;
y = 0;
setInterval(function(){
x = x+10;
y = y+10;
clearCanvas();
drawImageBall(x,y);
},300);
}
I have a picture which which is loaded like this:
<img src="map/racetrack.jpg" id="map">
And here is my drawing function:
this.drawRoute = function(){
var pos = [];
var canvas = $('<canvas/>')[0];
var img = $('#map')[0];
var ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
$(window).on('mousedown', function(e){
pos.push({
x: e.clientX,
y: e.clientY
});
});
if (positions.length > 1) {
ctx.beginPath();
ctx.moveTo(pos[0].x, pos[0].y);
for (var i = 1; i < pos.length; i++) {
ctx.lineTo(pos[i].x, pos[i].y);
}
ctx.stroke();
}
}
But nothing is drawn on the picture. I can't see where the mistake is. I know usually I'm supposed to use <canvas> element, but I only need this particular part done in canvas, nothing else.
Here's the fiddle
Well a few things first you have no canvas element. Secondly I didnt see where you were calling the draw portion.
Live Demo
What I ended up doing was adding a canvas element, hiding the image, and then every time you draw it draws the image to the canvas, and then draws the points over it. This should hopefully be enough to get you started.
var pos = [];
var canvas = $('#canvas')[0];
var img = $('#map')[0];
var ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
function render() {
ctx.drawImage(img, 0, 0);
if (pos.length > 1) {
ctx.beginPath();
ctx.moveTo(pos[0].x, pos[0].y);
for (var i = 1; i < pos.length; i++) {
ctx.lineTo(pos[i].x, pos[i].y);
}
ctx.stroke();
}
}
$(window).on('mousedown', function (e) {
pos.push({
x: e.clientX,
y: e.clientY
});
render();
});
render();
(function (canvasID, imgID) {
"use strict";
var canvas, ctx, myImg;
var initialize = function (){
canvas = document.getElementById(canvasID);
myImg = document.getElementById(imgID);
ctx = canvas.getContext('2d');
};
var renderImg = function (x, y, w, h, img, mixImg, filter){
if(ctx) {
ctx.drawImage(img, x, y, w, h);
mixImg(x, y, w, h, filter);
}
};
var mixImg = function (x, y, w, h, filter){
var r, g, b, a, v;
var canvasData = ctx.getImageData(x, y, w, h);
if(filter) {
switch(filter) {
case 'grayscale':
for (var i = 0; i < canvasData.data.length; i+=4){
r = canvasData.data[i];
g = canvasData.data[i+1];
b = canvasData.data[i+2];
v = 0.2126*r + 0.7152*g + 0.0722*b;
canvasData.data[i] = canvasData.data[i+1] = canvasData.data[i+2] = v;
}
break;
case 'retro':
for (var i = 0; i < canvasData.data.length; i+=4){
r = canvasData.data[i];
g = canvasData.data[i+1];
b = canvasData.data[i+2];
a = canvasData.data[i+3];
canvasData.data[i] = r-40;
canvasData.data[i+1] = g-50;
canvasData.data[i+2] = b+23;
canvasData.data[i+3] = 200;
}
break;
case 'instagram':
for (var i = 0; i < canvasData.data.length; i+=4){
r = canvasData.data[i];
g = canvasData.data[i+1];
b = canvasData.data[i+2];
canvasData.data[i] = r+63;
canvasData.data[i+1] = g+41;
canvasData.data[i+2] = 60;
}
break;
} // end of switch
} // end of if
ctx.putImageData(canvasData, x, y);
};
window.onload = function () {
initialize();
if(canvas && canvas.getContext) {
renderImg(0, 0, 250, 250, myImg, mixImg);
ctx.save();
renderImg(250, 0, 250, 250, myImg, mixImg, 'grayscale');
ctx.save();
renderImg(0, 250, 250, 250, myImg, mixImg, 'retro');
ctx.save();
renderImg(250, 250, 250, 250, myImg, mixImg, 'instagram');
ctx.save();
ctx.translate(0, 500);
ctx.restore();
}
};
})('collage', 'img');
How can I mirror the whole canvas(the 4 four images I rendered) I drew and place it on x:0 y:500?
I tried to save each image I drew and then translate it on different point and then restore it.
But nothing shown except then 4 images I drew. What did I do wrong??
translate only change reference point.
After ctx.translate(0, 500) x = 0 is upper left corner while y = 0 is 500 down.
You have to paint or put data into the canvas as well.
However getImageData and putImageData is not affected by transformation matrix so one have to say e.g.:
var trans = [0, 500];
ctx.putImageData(
canvasData,
x + trans[0],
y + trans[1]
);
or put the data into a memory canvas and use drawImage().
Ref.:
The current path, transformation matrix, shadow attributes, global alpha, the clipping region, and global composition operator must not affect the getImageData() and putImageData() methods.
As your code is now you also do a draw + read + redraw onto same region. You could use one draw and then use that as source for all other. There is also no need for the save and restore.
Edit:
Memory canvas as in document.createElement('CANVAS'). It all depends on how and for what purpose etc. A possible set-up could be.
var img = {}, memc = {}, domc = {};
img.src = document.getElementById(imgId);
img.w = img.src.width;
img.h = img.src.height;
/* DOM Canvas */
domc.can = document.getElementById(canvasId);
domc.ctx = domc.can.getContext('2d');
domc.width = img.w * 2; // Going to duplicate image 2 columns.
domc.height = img.h * 4; // Going to duplicate image 4 rows.
/* Memory Canvas */
memc.can = document.createElement('CANVAS');
memc.ctx = memc.can.getContext('2d');
memc.width = img.w;
memc.height = img.h;