I'm currently working on a game to teach myself HTML5 and Javascript, etc.
Originally I just had static canvases in the HTML body but it became a pain to pass around to my objects, etc. Basically it's easier for me to let each object just create its own as needed.
However, I don't know how to layer them properly like I had before. Now I have have my different canvases stacked atop of each other (my background sits above the player icon and below the objects to avoid).
How do I do this? I know it's related to the z-index (maybe innerHTML) but I'm very n00bish with HTML Javascript.
Thanks!
<canvas id="pointsCanvas"
style="z-index: 40;
position:absolute;
left:0px;
top:0px;
">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
<canvas id="menuCanvas"
style="z-index: 50;
position:absolute;
left:0px;
top:0px;
">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
I had this.
Now I'm trying this:
this.canvasElement = document.createElement('canvas')
Add them to a container element with its position set to relative (preferably create a rule, not an inline style like in this example):
<div id="container" style="position:relative"></div>
Then add canvases with their position set to absolute and left/top to 0:
var canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 400;
canvas.style.cssText = "position:absolute;left:0;top:0";
// add to container
document.getElementById("container").appendChild(canvas);
The first canvas added will be at the bottom, the last on top.
You can use z-indexes but it's better just to create and add canvases in the order you want, knowing first is bottom etc. (imo).
Demo:
var canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 400;
canvas.style.cssText = "position:absolute;left:0;top:0";
var ctx = canvas.getContext("2d");
ctx.font = "80px sans-serif";
ctx.fillText("BOTTOM", 10, 120);
// add to element
document.getElementById("container").appendChild(canvas);
// SECOOND CANVAS
var canvas2 = document.createElement("canvas");
canvas2.width = 500;
canvas2.height = 400;
canvas2.style.cssText = "position:absolute;left:0;top:0";
var ctx2 = canvas2.getContext("2d");
ctx2.font = "80px sans-serif";
ctx2.fillStyle = "blue";
ctx2.fillText("TOP", 16, 120);
// add to element
document.getElementById("container").appendChild(canvas2);
#container {position:relative}
<div id="container"></div>
A more traditional method is to have one canvas display multiple (layered) objects.
To do this you can create an array containing the definitions of each of your objects:
var gameObjects=[];
var gameObjects.push({
type: 'rect',
x:50, y:50,
width:100, height:75,
fill: 'red'
});
... and so on ...
Then for each game frame, you clear the canvas and use the definitions-array to draw the game objects in their new position.
// move the rect 5 pixels rightward
gameObjects[0].x += 5;
context.clearRect(0,0,canvas.width,canvas.height);
for(var i=0;i<gameObjects.length;i++){
var obj=gameObjects[i];
if(i.type='rect'){ drawRect(obj); }
}
function drawRect(r){
context.fillStyle=r.fill;
context.fillRect(r.x,r.y,r.width,r.height);
}
Layering
To do layering, you can add a z-index property to each of your gameObjects. It's not a z-index like a traditional html/css z-index. Instead it just lets you sort your gameObjects into any order (layers) you need.
var gameObjects.push({
type: 'rect',
x:50, y:50,
width:100, height:75,
fill: 'red',
zIndex=3
});
// sort the array by zIndex to apply layering
gameObjects.sort(function(a,b){return a.zIndex - b.zIndex});
// and now draw the "relayered" objects in the array
for(var i=0;i<gameObjects.length;i++){
var obj=gameObjects[i];
if(i.type='rect'){ drawRect(obj); }
}
Related
I want to make an TIC TAC TOE and im pretty new to js. I want to draw the x's and 0's to the canvas.
1. How do I make the height equal to the width of the canvas?
2. What is the best way to scale the canvas to use this for example also on phones and have the same experience?
You can make a full-screen canvas and use it to display your code.
Here is a boilerplate to get you started:
HTML
<canvas id="canvas"></canvas>
CSS
* {
margin: 0;
}
canvas {
display: block;
}
JS
window.onload = function() {
var canvas = document.getElementById("canvas"),
context = canvas.getContext("2d"),
width = canvas.width = window.innerWidth,
height = canvas.height = window.innerHeight;
context.fillRect(0, 0, width, height);
};
I am working on a project where I would like to have darkness covering the screen and the character glowing in the darkness. I tried to animate the scene then draw darkness over it using this code:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var pixelSize = 30;
var width = canvasWidth/pixelSize;
var height = canvasHeight/pixelSize;
var lightX = canvasWidth/2;
var lightY = canvasHeight/2;
var lightDiameter = 100;
var a = lightDiameter*pixelSize;
for(var x = 0; x < width; x++) {
for(var y = 0; y < height; y++) {
var alpha = 1.25 - a/(Math.pow(x*30 - lightX, 2) + Math.pow(y*30 -
lightY, 2));
ctx.fillStyle = "rgba( 25, 25, 30," + alpha + ")";
ctx.fillRect(x*pixelSize, y*pixelSize, pixelSize, pixelSize);
}
}
This worked pretty well and I liked the way it looked, but when this was repeatedly animated alongside the other code it slowed the rest down significantly. I think a possible solution may be to somehow draw a gradient with a lower "quality?", another solution I have considered is to save this drawing in a separate canvas and drawing it translated to the players location but that would make it impossible to add multiple sources of light, which I would like to do by simply adding their effect. I may just have to deal with the lag and I'm a noob at this stuff, but if anyone can help me that would be wonderful.
To clarify, I am using this code in the drawing loop, and also it is re-calculated in every iteration. I would prefer to recalculate this way so I can have multiple moving sources of light.
This is because fillRect is pretty slow compared to other methods. You could probably speed things up by using ImageData objects instead.
The way to do this would be to render everything to the canvas, get the corresponding ImageData, modify its contents and put it back onto the canvas:
var ctx = canvas.getContext("2d");
// render stuff here
var imageData = ctx.getImageData(0,0,canvasWidth,canvasHeight);
for (let x=0;x<canvasWidth;x++){
for (let y=0;y<canvasHeight;y++){
let i = (x+y*canvasWidth)*4;
let alpha = calculateAlpha(x,y); // your method here (should result in a value between 0 and 1)
imageData.data[i] = (1-alpha)*imageData.data[i]+alpha*25;
imageData.data[i+1] = (1-alpha)*imageData.data[i+1]+alpha*25;
imageData.data[i+2] = (1-alpha)*imageData.data[i+2]+alpha*30;
imageData.data[i+3] = 1-(1-alpha)*(1-imageData.data[i+3]);
}
}
ctx.putImageData(imageData,0,0);
This should do the lighting on a per-pixel basis, and much faster than using clearRect all the time. However, it might still slow things down, as you're doing a lot of calculations each frame. In that case, you could speed thing up by doing the lighting in a second canvas that is positioned over your main canvas using css:
<div id="container">
<canvas id="canvas"></canvas>
<canvas id="lightingCanvas"></canvas>
</div>
Css:
#container {
position: relative;
}
#canvas, #lightingCanvas {
position: absolute;
top: 0;
left: 0;
}
#container, #canvas, #lightingCanvas {
width: 480px;
height: 360px;
}
Javascript:
var canvas = document.getElementById("lightingCanvas")
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(25,25,30)";
ctx.fillRect(0,0,canvas.width,canvas.height);
var imageData = ctx.getImageData(0,0,canvasWidth,canvasHeight);
for (let x=0;x<canvasWidth;x++){
for (let y=0;y<canvasHeight;y++){
let i = (x+y*canvasWidth)*4;
let alpha = calculateAlpha(x,y); // your method here (should result in a value between 0 and 1)
imageData.data[i+3] = 255*alpha;
}
}
ctx.putImageData(imageData,0,0);
This way the browser takes care of the blending for you and you just need to plug in the correct alpha values - so rendering should be even faster now.
This will also allow you to bring the large pixels back in - just use a lower resolution on the second canvas and use some css effect like image-rendering: -webkit-crisp-edges to make the canvas pixelated when scaled up.
This question already has an answer here:
HTML Canvas: Drawing grid below a plot
(1 answer)
Closed 6 years ago.
I have a canvas, and I want to use drawImage to draw an image behind the current content on the canvas.
Due to the fact that there is content already on the canvas (I'm using Literally Canvas to create a canvas containing an image, so I can't really draw the image first), I cannot use drawImage before I render the rest of my content.
Is it possible to drawImage behind all other content on a canvas?
Yes you can just use globalCompositeOperation destination-over, but note that your first image needs some transparency, otherwise, you will obviously not see anything :
var img1 = new Image();
var img2 = new Image();
var loaded = 0;
var imageLoad = function(){
if(++loaded == 2){
draw();
}
};
img1.onload = img2.onload = imageLoad;
var draw = function(){
var ctx = c.getContext('2d');
ctx.drawImage(img1, 100,100);
// wait a little bit before drawing the background image
setTimeout(function(){
ctx.globalCompositeOperation = 'destination-over';
ctx.drawImage(img2, 0,0);
}, 500);
}
img1.src = "https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png";
img2.src = "https://picsum.photos/200/200";
<canvas id="c" width="200" height="200"></canvas>
Sorry about the previous post, I didn't properly read your post
Perhaps you could save the canvas, draw your image, and then reload the old content on top of your drawn image? Here's some JS psuedocode:
var imgData=ctx.getImageData(0,0,canvas.width,canvas.height);
ctx.drawImage('Your Image Watermark Stuff');
ctx.putImageData(imgData,0,0);
You can use KonvaJS. And then use layers for it.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.rawgit.com/konvajs/konva/0.13.0/konva.min.js"></script>
<meta charset="utf-8">
<title>Konva Rect Demo</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #F0F0F0;
}
</style>
</head>
<body>
<div id="container"></div>
<script>
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var imageObj = new Image();
imageObj.onload = function() {
var baseImage = new Konva.Image({
x: 50,
y: 50,
width: width,
height: height,
image: image
});
// add the shape to the layer
layer.add(rect);
// add the layer to the stage
stage.add(layer);
};
imageObj.src = 'url to your image'
</script>
</body>
</html>
A simple solution would be to use another canvas behind the first one.
Normally canvas pixels are initialized to transparent black and therefore are perfectly see-through.
If your first canvas is created opaque instead the only other option I can think to is
create a temporary canvas of the same size
draw your image in this temporary canvas
get the ImageData object of both the temporary canvas and of the original canvas
copy from the temporary canvas to the original canvas only where the original canvas is not set at the background color
In code:
var tmpcanvas = document.createElement("canvas");
tmpcanvas.width = canvas.width;
tmpcanvas.height = canvas.height;
var temp_ctx = tmpcanvas.getContext("2d");
// ... draw your image into temporary context ...
var temp_idata = temp_ctx.getImageData(0, 0, canvas.width, canvas.height);
var temp_data = temp_idata.data;
// Access the original canvas pixels
var ctx = canvas.getContext("2d");
var idata = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = idata.data;
// Find the background color (here I'll use first top-left pixel)
var br_r = data[0], bg_g = data[1], bg_b = data[2];
// Replace all background pixels with pixels from temp image
for (var i=0,n=canvas.width*canvas.height*4; i<n; i+=4) {
if (data[i] == bg_r && data[i+1] == bg_g && data[i+2] == bg_b) {
data[i] = tmp_data[i];
data[i+1] = tmp_data[i+1];
data[i+2] = tmp_data[i+2];
data[i+3] = tmp_data[i+3];
}
}
// Update the canvas
ctx.putImageData(idata, 0, 0);
this approach however will have a lower quality if the original canvas graphics has been drawn with antialiasing or if pixels of the background color are also used in the image (e.g. an object on #FFF white background where object highlights are also #FFF). Another problem is if the background color is not a perfectly uniform RGB value (this will happen if the image has been compressed with a lossy algorithm like jpeg).
All these problems could be mitigated with more sophisticated algorithms like range matching, morphological adjustments and color-to-alpha conversions (basically the same machinery used for chroma-keying).
I'm trying to learn how to use HTML's Canvas and it's properties.
Now I'm trying to draw a ground and a sky, and I want the sky to be 75% of screens height and ground to be 25%.
This was easy using a div, and style it with CSS or JavaScript CSS Query. Now that I'm on a Canvas, I can't figure out how to set the fillRect properties...
My code:
var windowHeight = $(window).height();
var windowWidth = $(window).width();
var skyHeight = windowHeight - (windowHeight * 0.25);
var groundHeight = windowHeight - (windowHeight * 0.75);
ctx = gameCanvas.getContext("2d");
ctx.fillStyle="green";
ctx.fillRect(0,0,windowWidth,windowHeight);
ctx.fillStyle="cyan";
ctx.fillRect(0,0,windowWidth,skyHeight);
Also: if I try using i.e.
ctx.fillStyle="cyan";
ctx.fillRect(0,0,30,30);
This draws a perfect square on top of the green "ground"-background, but if I change to i.e.
ctx.fillRect(0,0,200,200);
it will fill my entire height of the screen, and more than 50% of my screen-width.
According to http://www.w3schools.com/tags/canvas_fillstyle.asp canvas fillrect are using pixels, but 200 x 200 pixels should definitely NOT fill my entire screen height. :/
Where's the part where you tell canvas what its dimensions are? Just like any other HTML media element that you need to be of a specific size (video, image), you need to tell it what its width and height is if you want reliably sized content (if you don't, by convention the canvas will be sized 300 x 150px). And because we're setting the number of pixels we can draw on, this is not "purely cosmetic" like CSS, we have to do it properly:
var cvs = document.querySelector("canvas");
// if you didn't use <canvas width="..." height="...">, we need this:
cvs.width = 800;
cvs.height = 400;
And then you can do your drawing:
var ctx = cvs.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(0,0.75*cvs.height, cvs.width,0.25*cvs.height);
ctx.fillStyle = "lightblue";
ctx.fillRect(0,0, cvs.width, 0.75*cvs.height);
See http://jsbin.com/tejiqixeji/edit?html,js,output
I am using javascript to draw a rectangle around DOM elements in a website.
The problem is that the rectangles are drawn in the wrong positions.
I know that canvas, work like a real canvas so you have to "predraw" everything before filling the canvas, otherwise the elements will be on top of each other, in the orders you drew them.
That's why I'm defining canvas, and context outside of the loop.
This is my code:
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.globalAlpha = 0.5;
//Set canvas width/height
canvas.style.width='100%';
canvas.style.height='100%';
//Set canvas drawing area width/height
canvas.width = document.width;
canvas.height = document.height;
//Position canvas
canvas.style.position='absolute';
canvas.style.left=0;
canvas.style.top=0;
canvas.style.zIndex=100000;
canvas.style.pointerEvents='none'; //Make sure you can click 'through' the canvas
document.body.appendChild(canvas); //Append canvas to body element
var listingsRect = Array.prototype.map.call(document.querySelectorAll('.rc'), function(e) {
return e.getBoundingClientRect();
});
listingsRect.forEach(function(listingRect) {
var x = listingRect.left;
var y = listingRect.top;
var width = listingRect.width;
var height = listingRect.height;
//Draw rectangle
context.rect(x, y, width, height);
context.fillStyle = 'yellow';
context.fill();
});
However, when I change
canvas.width and canvas.height to window.innerWidth and window.innerHeight respectively, then canvas draws the rectangles in the right positions, however it only draws them in the visible area of the website (obviously).
Can somebody tell me what's wrong with my code?
Here's a JS bin:
http://jsbin.com/elUToGO/1
The x,y in context.rect(x,y,width,height) are relative to the canvas element not to the browser window.
So if your canvas element is absolutely positioned at 50,75 and you want a rect at window position 110,125 you would draw your rect like this:
context.rect( 110-50, 125-75, width, height );
A few other things:
If you set the canvas element width/height and then position absolutely, you don't need canvas.style.width/height.
document.width/height are deprecated (& not supported in IE) . Use this instead:
//Set canvas drawing area width/height
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
When setting style.left/top, you might want to pass a string with "px" in case you later set >0.
canvas.style.left="0px";
canvas.style.top="0px";
.pointerEvents='none' is supported in most browsers (but not in IE<11)