This question already has answers here:
Canvas is stretched when using CSS but normal with "width" / "height" properties
(10 answers)
Closed last month.
I've made a basic snake game, and I'm trying to improve one minor detail.
You see, when I place the apple image [or leaf image because I had an SVG image for that handy], the canvas scales the image and makes it quite big.
Here is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>
Basic Snake Game
</title>
</head>
<body>
<svg style="width:50px;height:50px;"><polygon points="10,10 10,14 10.5,17 12,23 14,30 16,34 18,36 22,37 25,37 25,30 24.5,28 24,27 23.5,26 11,10" style="fill:lime;stroke:green;stroke-width:1;" /><line x1="10" y1="10" x2="25" y2="37" style="stroke:green;stroke-width:1;" /></svg>
<script>
var html = document.querySelector('body');
html.innerHTML += '<p></p><br/><input type="button" onclick="heading -= 90" value="Left 90*" style="font-size:20pt;"/><input type="button" onclick="heading += 90" value="Right 90*" style="font-size:20pt;"/><canvas style="width:1000px;height:700px;border:3px solid red;"></canvas>';
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var img = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(img);
img = new Image();
img.src = 'data:image/svg+xml;base64,' + btoa(xml);
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 2000, 1400);
var width = 15;
var speed = 1;
var heading = 0;
var pos = [15, 10];
var leafPos;
leafPos = [Math.floor(Math.random() * 250), Math.floor(Math.random() * 75)];
ctx.fillRect(30, 20, width, 10);
var main = setInterval(function() {
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 2000, 1400);
ctx.fillStyle = '00ff00';
if(heading == -90) {
heading = 270;
} if(heading == 360) {
heading = 0;
} if(heading == 0) {
pos[0] += speed;
} if(heading == 180) {
pos[0] -= speed;
} if(heading == 0 || heading == 180) {
ctx.fillRect(pos[0], pos[1], width, 5);
} if(heading == 270) {
pos[1] -= speed;
} if(heading == 90) {
pos[1] += speed;
} if(heading == 270 || heading == 90) {
ctx.fillRect(pos[0], pos[1], 5, width);
}
if((Math.abs(leafPos[0] - pos[0]) + Math.abs(leafPos[1] - pos[1])) <= 15) {
speed += 0.5;
width ++;
leafPos = [Math.floor(Math.random() * 250), Math.floor(Math.random() * 75)];
}
ctx.drawImage(img, leafPos[0], leafPos[1]); // The line of interest
document.querySelector('p').innerHTML = (speed - 1) *10;
}, 50);
</script>
</body>
</html>
The original SVG image is at the top for reference.
To remedy this, I tried putting ctx.scale(0.5, 0.5) before the main interval and doubling all x, y, width, and height values EXCEPT that of our line of interest. This worked, but the image quality was compromised quite badly. I also tried editing the line of interest by scaling its width and height:
ctx.drawImage(img, leafPos[0], leafPos[1], img.width /2, img.height /2)
However, this produced the same result.
Is there some way to disable automatic scaling of images to avoid this altogether?
OR
Is there some way to resize images without reducing image quality?
Any help is appreciated.
Instead of setting the canvas size on the style:
<canvas style="width:1000px;height:700px;border:3px solid red;"></canvas>'
You should set it like this:
<canvas width=1000 height=700 style="border:3px solid red;"></canvas>
See working sample below
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var img = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(img);
img = new Image();
img.src = 'data:image/svg+xml;base64,' + btoa(xml);
function draw() {
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, 2000, 1400);
ctx.fillStyle = '00ff00';
ctx.drawImage(img, 20, 20);
}
setInterval(draw, 50);
<svg style="width:50px;height:50px;">
<polygon points="10,10 10,14 10.5,17 12,23 14,30 16,34 18,36 22,37 25,37 25,30 24.5,28 24,27 23.5,26 11,10" style="fill:lime;stroke:green;stroke-width:1;" />
<line x1="10" y1="10" x2="25" y2="37" style="stroke:green;stroke-width:1;" />
</svg>
<canvas width=1000 height=700 style="border:3px solid red;"></canvas>
Related
I plan on using the coordinates of my sprites in a canvas to make a board game by making the canvas a background image.
Here is the 8x8 board's js code.
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
c.fillStyle = "#ffb933";
c.fillRect(0,0,100,100);
c.fillRect(200,0,100,100);
c.fillRect(400,0,100,100);
c.fillRect(600,0,100,100);
c.fillRect(100,100,100,100);
c.fillRect(300,100,100,100);
c.fillRect(500,100,100,100);
c.fillRect(700,100,100,100);
c.fillRect(0,200,100,100);
c.fillRect(200,200,100,100);
c.fillRect(400,200,100,100);
c.fillRect(600,200,100,100);
c.fillRect(100,300,100,100);
c.fillRect(300,300,100,100);
c.fillRect(500,300,100,100);
c.fillRect(700,300,100,100);
c.fillRect(0,400,100,100);
c.fillRect(200,400,100,100);
c.fillRect(400,400,100,100);
c.fillRect(600,400,100,100);
c.fillRect(100,500,100,100);
c.fillRect(300,500,100,100);
c.fillRect(500,500,100,100);
c.fillRect(700,500,100,100);
c.fillRect(0,600,100,100);
c.fillRect(200,600,100,100);
c.fillRect(400,600,100,100);
c.fillRect(600,600,100,100);
c.fillRect(100,700,100,100);
c.fillRect(300,700,100,100);
c.fillRect(500,700,100,100);
c.fillRect(700,700,100,100);
Is there any way I can make this a background image of my html file so I can still use the same coordinates and make new sprites above it which I could manipulate with js also. Thanks!
You can significantly reduce that ...
It is just a couple of loops and a condition
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
c.fillStyle = "#ffb933";
size = 20
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
if ((i + j) % 2 != 0)
c.fillRect(i * size, j * size, size, size);
}
}
<canvas width=200 height=200>
If you are set on using an image an easy option is with an SVG image
I got this one from wikimedia:
https://upload.wikimedia.org/wikipedia/commons/7/70/Checkerboard_pattern.svg
function svgimage() {
var image = `
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 5 5">
<rect width="5" height="5"/>
<path d="M0,0V5H1V0zM2,0V5H3V0zM4,0V5H5V0zM0,0H5V1H0zM0,2H5V3H0zM0,4H5V5H0z" fill="#fff" fill-rule="evenodd"/>
</svg>`;
return encodeURIComponent(image);
}
function drawImage() {
ctx.drawImage(img, 0, 0, 500, 500);
}
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = drawImage
img.src = 'data:image/svg+xml;charset=utf-8,' + svgimage();
<canvas id=canvas width=500 height=500></canvas>
You can draw anything you like on top of that, here is an example:
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
c.globalAlpha = 0.5
function drawBoard() {
c.beginPath()
c.fillStyle = "#ffb933";
size = 20
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++) {
if ((i + j) % 2 != 0)
c.fillRect(i * size, j * size, size, size);
}
}
}
delta = 0
function draw() {
c.clearRect(0, 0, canvas.width, canvas.height)
drawBoard()
delta += 0.05
x = Math.sin(delta) * 40 + 80
y = Math.cos(delta) * 20 + 60
c.beginPath()
c.fillStyle = "#ff0000";
c.arc(x, y, 10, 0, 2 * Math.PI);
c.fill()
}
setInterval(draw, 20);
<canvas width=200 height=200>
You can see I converted the double loop example into a function that we call every time we draw
I want to have hella stacked rectangles that go to the center of the canvas and have them all randomly colored, but my code is wrong and isn't working.
Here is the code i have:
var ctx = canvas.getContext("2d");
var cornerpad = 0;
var rectwidth = 790;
var rectheight = 590;
function drawrectangles() {
ctx.beginPath;
ctx.rect(cornerpad, cornerpad, rectwidth, rectheight);
ctx.fillStyle = 'hsl(' + 360 * Math.random() + ', 50%, 50%)'
ctx.fill();
ctx.closePath;
}
while(rectheight > 5) {
rectheight =- 10;
rectwidth =- 10;
cornerpad++;
drawrectangles();
}
I am pretty sure I made the while loop wrong since the random color works and the drawing rectangles works. Please help and explain what I did wrong
You had some mistakes in your code. First of all, you have rectheight =- 10; instead of rectheight -= 10. Your line of code is actually equivalent to rectheight = -10, so you are just setting both of the variables to -10, not decrementing them by 10.
Then, you used fill instead of fillRect. There is a fine difference between them, you may read more here or here. fillRect is a "stand-alone" command that draws and fills a rectangle. So if you issue multiple fillRect commands with multiple fillStyle commands, each new rect will be filled with the preceeding fillstyle.
For a nicer effect, I recommend using strokeRect instead of rect, but that's your decision. Also, you may want to play with the condition from the while, to actually make them appear centered.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var cornerpad = 0;
var rectwidth = 790;
var rectheight = 590;
function drawrectangles() {
ctx.beginPath();
ctx.rect(cornerpad, cornerpad, rectwidth, rectheight);
ctx.fillStyle = 'hsl(' + 360 * Math.random() + ', 50%, 50%)';
ctx.fill();
ctx.stroke();
}
while (rectheight > 5) {
rectheight -= 10;
rectwidth -= 10;
cornerpad += 10;
//console.log(`(h, w)=(${rectheight}, ${rectwidth})`);
drawrectangles();
}
canvas {
border: 1px dotted black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.js"></script>
<canvas id="canvas" width="790" height="590">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
Edit:
I added a version that draws them much nicer, check it out :)
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var cornerpad = 0;
var rectwidth = 600;
var rectheight = 400;
var middleLine = rectheight / 2;
function drawrectangles() {
ctx.strokeRect(cornerpad, cornerpad, rectwidth, rectheight);
ctx.fillStyle = 'hsl(' + 360 * Math.random() + ', 50%, 50%)'
ctx.fillRect(cornerpad, cornerpad, rectwidth, rectheight);
}
while (cornerpad < middleLine) {
rectheight -= 20;
rectwidth -= 20;
cornerpad += 10;
//console.log(`(h, w)=(${rectheight}, ${rectwidth})`);
drawrectangles();
}
canvas {
border: 1px dotted black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/canvasjs/1.7.0/canvasjs.js"></script>
<canvas id="canvas" width="600" height="400">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
Cheers!
Edit 2:
Because I was too absorbed by the =- 10 thing, I forgot to point out to actually use beginPath and closePath as functions and to call them as ctx.beginPath() and ctx.closePath(). Thanks to Kaiido for pointing that out and for the two working additional jsfiddles.
I believe your problem is laying in this expressions =- what you mean is -=. what you are doing now is setting rectheight value to -10 instead of reducing it by 10, thus your while loop just gets executed once.
You missed some closing brackets and had the =- around the wrong way. I also change the rect to fill straight away.
var ctx = canvas.getContext("2d");
var cornerpad = 0;
var rectwidth = 790;
var rectheight = 590;
function drawrectangles() {
ctx.fillStyle = 'hsl(' + 360 * Math.random() + ', 50%, 50%)'
ctx.fillRect(cornerpad, cornerpad, rectwidth, rectheight);
}
while(rectheight > 5) {
rectheight -= 10;
rectwidth -= 10;
cornerpad++;
drawrectangles();
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<canvas id='canvas' height=590 width=790></canvas>
</body>
</html>
i want to make a line that contracts and expands along x-axis randomly it should start from the middle of canvas . I was only able to move the line one way, not both ways! i have the code, if you could please help me i would really appreciate it !
<!DOCTYPE html>
<style>
canvas {
border: solid;
border-color: black; }
</style>
<canvas id="canvas">
</canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "black";
var posX = 0;
var lineLength = 50;
var speed = 2;
function drawLine() {
ctx.beginPath();
ctx.moveTo(posX, 50);
ctx.lineTo(posX + lineLength, 50);
ctx.stroke();
}
function moveLine() {
posX += speed;
if (posX < 0 || posX > canvas.width - 50) {
speed = speed * -1;
}
}
function loop() {
// clear old frame;
ctx.clearRect(0, 0, canvas.width, canvas.height);
moveLine();
drawLine();
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
</script>
You want to be modifying the value of lineLength in your loop, not posX. Like this:
function moveLine() {
lineLength += speed;
if (lineLength < 0 || lineLength > canvas.width - 50) {
speed = speed * -1;
}
}
Then you just need to center your line:
function drawLine() {
var center = canvas.width/2;
ctx.beginPath();
ctx.moveTo(center - (lineLength/2), 50);
ctx.lineTo(center + (lineLength/2), 50);
ctx.stroke();
}
Since the X position of your line isn't variable, you don't need posX at all.
So I have this rectangle that animates across to the right. How can I get the rectangle to reverse it when it hits the boundaries. I'm trying to make it go back and forth.
<!DOCTYPE html>
<html>
<head>
<script type='text/javascript'>
window.onload=function(){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
var y = 50;
var width = 10;
var height = 10;
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(x, y, width, height);
x++;
if(x <= 490) {
setTimeout(animate, 33);
}
}
animate();
}
</script>
</head>
<body>
<canvas id="canvas" width="500" height="400"
style="border: 1px solid #000000;"></canvas>
</body>
</html>
https://codepen.io/forTheLoveOfCode/pen/wqdpeg
Is that what you need? (link to codepen above).
var canvas = document.getElementById("canvas_id");
var context = canvas.getContext('2d');
var x=5;
var y=5;
var velocity = 10;
function move(){
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
x =x + velocity
if ((x+50)>canvas.width || x<0){
velocity *=-1;
}
draw()
}
function draw(){
context.fillStyle = "#E80C7A";
context.strokeStyle = "#000000";
context.lineWidth = '3';
context.fillRect(x, y, 50, 100);
context.strokeRect(x, y, 50, 100);
}
setInterval(move, 100);
<html>
<body>
<canvas id = "canvas_id">
</canvas>
</body>
</html>
here's a solution with boundaries detection
window.onload=function(){
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
var y = 50;
var width = 10;
var height = 10;
var speed = 10; // speed
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(x, y, width, height);
if(
(x >= 500 - width && speed > 0) || // going to the right and bound reached
(x <= 0 && speed < 0) // going to the left and bound reached
) {
speed *= -1; // inverting the direction
}
x += speed;
setTimeout(animate, 33);
}
animate();
}
<canvas id="canvas" width="500" height="400"
style="border: 1px solid #000000;"></canvas>
consider using requestAnimationFrame instead of setTimeout to do this kind of work.
I want to make all overlapped HTML5 canvases full-screen when a button is clicked and I want them to stay overlapped when in full-screen mode.
For example I have 3 overlapped canvases. This example came from this site:
HTML
<section>
<div id="canvasesdiv" style="position:relative;width:400px;height:300px">
<canvas id="layer1" style="z-index: 1;position:absolute;left:0px;top:0px;" height="300px" width="400">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
<canvas id="layer2" style="z-index: 2;position:absolute;left:0px;top:0px;" height="300px" width="400">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
<canvas id="layer3" style="z-index: 3;position:absolute;left:0px;top:0px;" height="300px" width="400">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
</div>
<div>
<p>
<button onclick="goFullScreen();">Go Fullscreen</button>
</p>
</div>
...
JavaScript
var layer1;
var layer2;
var layer3;
var ctx1;
var ctx2;
var ctx3;
var x = 400;
var y = 300;
var dx = 2;
var dy = 4;
var WIDTH = 400;
var HEIGHT = 300;
var city = new Image();
function init() {
city.src = "http://html5.litten.com/layers/city.png";
layer1 = document.getElementById("layer1");
ctx1 = layer1.getContext("2d");
layer2 = document.getElementById("layer2");
ctx2 = layer2.getContext("2d");
layer3 = document.getElementById("layer3");
ctx3 = layer3.getContext("2d");
setInterval(drawAll, 20);
}
function drawAll() {
draw1();
draw2();
draw3();
}
function draw1() {
ctx1.clearRect(0, 0, WIDTH, HEIGHT);
ctx1.fillStyle = "#FAF7F8";
ctx1.beginPath();
ctx1.rect(0, 0, WIDTH, HEIGHT);
ctx1.closePath();
ctx1.fill();
ctx1.fillStyle = "#444444";
ctx1.beginPath();
ctx1.arc(x, y, 10, 0, Math.PI * 2, true);
ctx1.closePath();
ctx1.fill();
if (x + dx > WIDTH || x + dx < 0)
dx = -dx;
if (y + dy > HEIGHT || y + dy < 0)
dy = -dy;
x += dx;
y += dy;
}
function draw2() {
ctx2.clearRect(0, 0, WIDTH, HEIGHT);
ctx2.drawImage(city, 0, 0);
}
function draw3() {
ctx3.clearRect(0, 0, WIDTH, HEIGHT);
ctx3.fillStyle = "#444444";
ctx3.save();
ctx3.translate(200, 200);
ctx3.rotate(x / 20);
ctx3.fillRect(-15, -15, 30, 30);
ctx3.restore();
}
function goFullScreen() {
var canvas1 = document.getElementById("layer1");
var canvas2 = document.getElementById("layer2");
var canvas3 = document.getElementById("layer3");
if (canvas1.requestFullScreen){
canvas1.requestFullScreen();
canvas2.webkitRequestFullScreen();
canvas3.webkitRequestFullScreen();
}
else if (canvas1.webkitRequestFullScreen){
canvas1.webkitRequestFullScreen();
canvas2.webkitRequestFullScreen();
canvas3.webkitRequestFullScreen();
}
else if (canvas1.mozRequestFullScreen){
canvas1.mozRequestFullScreen();
canvas2.webkitRequestFullScreen();
canvas3.webkitRequestFullScreen();
}
}
init();
I have tried to implement what discussed in this answer.
I tried to modify the goFullScreen() function is you can see above. But this only make the first canvas full-screen.
Thank your very much for your help.
You will have to request the fullscreen from the parent div and set canvas elements' width to 100% in CSS :
Style :
div:-webkit-full-screen>canvas {
width: 100% !important;
}
div:-moz-full-screen>canvas {
width: 100% !important;
}
div:-ms-fullscreen>canvas {
width: 100% !important;
}
div:fullscreen>canvas {
width: 100% !important;
}
Js :
function goFullScreen() {
var parentDiv = document.getElementById("canvasesdiv");
if (parentDiv.requestFullscreen){
parentDiv.requestFullscreen();}
else if(parentDiv.webkitRequestFullscreen){
parentDiv.webkitRequestFullscreen();
} else if(parentDiv.mozRequestFullScreen){
parentDiv.mozRequestFullScreen();
}
}