Javascript - Why isn't my while loop executing? (Pong) - javascript

Amateur here :) Sorry if this seems like a dumb question, but why isn't my while loop executing? It should draw a pink rectangle with two black rectangle for paddles. P.S. Yes, I know that i<10 doesn't make sense, but it is a placeholder for later and shouldn't impact the running of the code. Anyways, here's my code-
//set up the javascript canvas
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
var i = 1;
//Game loop
while (i < 10) {
//register pressed keys
//var x = event.key;
if (window.event) {
var keynum = event.keyCode;
} else if (event.which) {
var keynum = event.which;
}
//draw the pink background
ctx.beginPath();
ctx.rect(0, 0, 315, 100);
ctx.fillStyle = "#FFC0CB";
ctx.fill();
ctx.closePath();
//Draw the player's paddle
ctx.beginPath();
var playerY = 35;
ctx.rect(15, playerY, 10, 25);
ctx.fillStyle = "#000000";
ctx.fill();
ctx.closePath();
//Draw the bot's paddle
ctx.beginPath();
var botY = 35;
ctx.rect(280, botY, 10, 25);
ctx.fill();
ctx.closePath();
i++;
}

Related

Drawing a slime from Dragon Quest

Basically I want to draw a slime from Dragon Quest (it looks like a water drop). I have done the following, but I'd like to know if there is a better way to do it. Maybe using Bezier curves or something like that?
Update:
I actually got everything to look like how I want it, except for the gradient for the glow spots (white)
Slime.prototype.drawEye = function(x,y,w,h)
{
ctx.save();
ctx.lineWidth = 3;
ctx.beginPath();
var c = [];
c.push("sclera");
//c.push("iris");
c.push("pupil");
var wScale = [1,2.2,3];
var hScale = [1,2,1.3];
for (var i = 0; i < c.length; i++)
{
w /= wScale[i];
h /= hScale[i];
ctx.beginPath();
ctx.ellipse(x,y,w,h,0,degreesToRadians(360),false);
ctx.fillStyle = this.colors[c[i]];
ctx.fill();
ctx.stroke();
}
ctx.restore();
}
Slime.prototype.drawMouth = function()
{
ctx.save();
ctx.beginPath();
var x = this.x+this.w/30;
var y = this.y+this.h/8;
var w = this.w;
var h = this.h/8;
var start = [x-w/3,y];
var end = [x+w/8,y+h/5];
// mouth
bezierCurve(start,[x-w/3.3,y+h*2.3],[x+w/8,y+h*1.8],end);
bezierCurveTo([x-w/10,y+h*0.6],[x-w/6,y+h*0.6],start);
ctx.lineCap = "round";
ctx.lineWidth = 8;
ctx.stroke();
ctx.clip();
ctx.fillStyle = this.colors["mouth"];
ctx.fill();
start = [x-w/3.5,y+h*1];
// tongue
ctx.beginPath();
bezierCurve(start,[x-w/5,y+h*2.5],[x+w/6.5,y+h*1.6],[x+w/9,y+h*0.75]);
bezierCurveTo([x+w/10,y+h/4],[x-w/5,y],start);
ctx.fillStyle = this.colors["tongue"];
ctx.fill();
//ctx.stroke();
ctx.restore();
}
Slime.prototype.drawHealthBar = function()
{
ctx.beginPath();
ctx.rect(this.x-this.w,this.y+this.h/2+30,this.w*2*this.health/this.maxHealth,20);
ctx.fillStyle = "#00ff00";
ctx.fill();
ctx.beginPath();
ctx.rect(this.x-this.w+(this.w*2*this.health/this.maxHealth),this.y+this.h/2+30,(this.maxHealth-this.health)/this.maxHealth*this.w*2,20);
ctx.fillStyle = "#ff0000";
ctx.fill();
ctx.beginPath();
ctx.rect(this.x-this.w,this.y+this.h/2+30,this.w*2,20);
ctx.strokeStyle = "#000000";
ctx.stroke();
}
Slime.prototype.drawBody = function()
{
ctx.save();
ctx.fillStyle = this.colors["body"];
ctx.beginPath();
var x = this.x;
var y = this.y+this.h*0.4;
var w = this.w/2;
var h = this.h;
var left = [x-w*0.97,this.y+h/11];
var bottom = [x,this.y+h*0.495];
var right = [x+w*0.97,this.y+h/15];
var top = [x+this.w/30,this.y-h/2+this.topOffset];
bezierCurve(left,[x-w,y+h/10],[x-w/7,y+h/12],bottom);
bezierCurveTo([x+w*0.8,y+h/10],[x+w*1.05,y-h/7],right);
y = this.y - (y - this.y);
bezierCurveTo([x+w*0.75,y+h/15],[x+w*0.2,y+h/4.5],top);
bezierCurveTo([x-w*0.2,y+h/5],[x-w*0.8,y+h/10],left);
ctx.lineWidth = 3;
ctx.fill();
ctx.strokeStyle = this.colors["bodyOutline"];
ctx.stroke();
ctx.clip();
ctx.shadowColor = "#000033";
ctx.shadowBlur = 40;
ctx.shadowOffsetX = ctx.shadowBlur/4;
ctx.shadowOffsetY = ctx.shadowOffsetX*-0.8;
ctx.lineWidth = 4;
for (var i = 0; i < 7; i++)
{
if (i%2==0)
{
ctx.shadowColor = "#000033";
}
else
{
ctx.shadowColor = "rgba(0,0,80,0.5)";
}
ctx.stroke();
}
ctx.restore();
}
Slime.prototype.drawGlow = function(x,y,r1,r2)
{
ctx.save();
var g = ctx.createRadialGradient(x,y,r1,x,y,r2);
g.addColorStop(0.0,"#ffffff");
g.addColorStop(1.0,this.colors["body"]);
ctx.beginPath();
ctx.arc(x,y,r2,0,degreesToRadians(360),false);
ctx.fillStyle = g;
ctx.fill();
ctx.restore();
}
Slime.prototype.draw = function()
{
ctx.lineWidth = 2;
this.drawBody();
this.drawMouth();
var w = this.w/10;
var h = this.h/10;
var x = this.x;
var y = this.y;
this.drawEye(x-this.w/4.5,y-h/2.5,w,h);
this.drawEye(this.x+this.w/13,y-h/10,w,h);
this.drawGlow(this.x-this.w/2.5,this.y+this.h/12,this.w/50,this.w/16);
this.drawGlow(this.x+this.w/2*0.7,this.y+this.h/10,this.w/50,this.w/16);
}
End update
Yes, you can draw the droplet outline using just 4 cubic Bezier curves.
Here's a proof-of-concept using just 4 C-Bez curves:
Why! Why! Why! .......... Did I enjoy creating a Slime Man??
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
drawDropletMan(20,20,'rgb(63,187,255)');
drawDropletMan(255,55,'rgb(255,180,155)');
function drawDropletMan(x,y,fill){
ctx.translate(x,y);
drawBody(fill);
circularGlow(158,111,3,18,fill);
circularGlow(32,107,3,18,fill);
eye(65,87,17,10);
eye(120,90,17,10);
mouth(33,130);
ctx.translate(-x,-y);
}
//
function drawBody(fillcolor){
ctx.save();
ctx.beginPath();
ctx.moveTo(109,0);
ctx.bezierCurveTo(111,64,199,35,198,123);
ctx.bezierCurveTo(199,139,183,192,99,190);
ctx.bezierCurveTo(66,193,-4,167,1,110);
ctx.bezierCurveTo(9,47,97,45,109,1);
ctx.fillStyle=fillcolor;
ctx.fill();
ctx.strokeStyle='black';
ctx.lineWidth=2;
ctx.stroke();
ctx.clip();
ctx.shadowColor='black';
ctx.shadowBlur=15;
ctx.lineWidth=1;
for(var i=0;i<8;i++){ctx.stroke();}
ctx.restore();
}
//
function circularGlow(x,y,r1,r2,fillcolor){
var g=ctx.createRadialGradient(x,y,r1,x,y,r2);
g.addColorStop(0.00,'white');
g.addColorStop(1.00,fillcolor);
ctx.beginPath();
ctx.arc(x,y,r2,0,Math.PI*2);
ctx.fillStyle=g;
ctx.fill();
}
//
function eye(x,y,r1,r2){
ctx.beginPath();
ctx.arc(x,y,r1,0,Math.PI*2);
ctx.fillStyle='white';
ctx.strokeStyle='black';
ctx.fill();
ctx.lineWidth=2;
ctx.stroke();
//
ctx.beginPath();
ctx.arc(x,y,r2,0,Math.PI*2);
ctx.fillStyle='black';
ctx.fill();
}
//
function mouth(){
ctx.save();
ctx.translate(5,5);
ctx.beginPath();
ctx.moveTo(44,120);
ctx.bezierCurveTo(56,136,112,132,128,123);
ctx.bezierCurveTo(138,123,143,123,132,138);
ctx.bezierCurveTo(113,165,49,169,39,127);
ctx.bezierCurveTo(41,128,32,122,44,120);
ctx.closePath();
ctx.fillStyle='black';
ctx.fill();
ctx.clip();
ctx.beginPath();
ctx.arc(90,200,56,0,Math.PI*2);
ctx.fillStyle='coral';
ctx.fill();
ctx.restore();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=500 height=300></canvas>

2048 in javascript: updating the canvas

I am in a computer science class at my high school, and for my final project, my partner and I have decided to make 2048. We are not going to have the tiles "move", but instead, the 4x4 board will have "tiles" that are have properties of the number and color of the "tile" as it appears on the board. We are able to get the x and y coordinates of a tile to change, but the canvas will not update. We have had this problem for two weeks now, and have tried everything, but nothing has worked. Our code is posted below.
Please keep in mind that as we have only used javascript for a couple months, and only to do very simple things, our understanding is limited. Thank you for your help.
<!DOCTYPE html>
<html lang = "en">
<body style="background-color:lightgrey;">
<title>2048</title>
<canvas id="myCanvas" width="410" height="410" style="border:1px solid #d3d3d3;">
</canvas>
<script>
var x = 10;
var y = 10;
document.addEventListener('keydown', function(event)
{
if(event.keyCode == 37)//left
{
x -= 100;
console.log("x:"+ x);
}
else if(event.keyCode == 38)//up
{
y -= 100;
console.log("y:"+ y);
}
else if(event.keyCode == 39)//right
{
x += 100;
console.log("x:"+ x);
}
else if(event.keyCode == 40)//down
{
y += 100;
console.log("y:"+ y);
}
});
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
// grey rectangle
ctx.beginPath();
ctx.lineWidth = "2";
ctx.strokeStyle = "grey";
ctx.rect(5, 5, 400, 400);
ctx.stroke();
// blue rectangle
ctx.beginPath();
ctx.lineWidth = "5";
ctx.strokeStyle = "blue";
ctx.rect(x, y, 100, 100);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
</script>
</body>
</html>
What your game needs is a game loop. So the drawing needs to happen continously, at the moment you draw only one time at the beginning.
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function draw() {
// clear the canvas so the old rectangles are gone
ctx.clearRect(0, 0, c.width, c.height);
// grey rectangle
ctx.beginPath();
ctx.lineWidth = "2";
ctx.strokeStyle = "grey";
ctx.rect(5, 5, 400, 400);
ctx.stroke();
// blue rectangle
ctx.beginPath();
ctx.lineWidth = "5";
ctx.strokeStyle = "blue";
ctx.rect(x, y, 100, 100);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
// repeat game loop function forever, 30 times per second
window.setInterval(draw, 1000.0 / 30.0)
Notice the use of window.setInterval and ctx.clearRect.
Every time you want to update what is shown on the canvas, you have to 'Draw' again. Your code will run once when the page loads but never draws again so your changes to x and y are never seen.
I added a draw function here that is called on startup and after each keydown.
<!DOCTYPE html>
<html lang = "en">
<body style="background-color:lightgrey;">
<title>2048</title>
<canvas id="myCanvas" width="410" height="410" style="border:1px solid #d3d3d3;">
</canvas>
<script>
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var x = 10;
var y = 10;
document.addEventListener('keydown', function(event)
{
if(event.keyCode == 37)//left
{
x -= 100;
console.log("x:"+ x);
}
else if(event.keyCode == 38)//up
{
y -= 100;
console.log("y:"+ y);
}
else if(event.keyCode == 39)//right
{
x += 100;
console.log("x:"+ x);
}
else if(event.keyCode == 40)//down
{
y += 100;
console.log("y:"+ y);
}
draw();
});
function draw(){
ctx.clearRect(0, 0, c.width, c.height);
// grey rectangle
ctx.beginPath();
ctx.lineWidth = "2";
ctx.strokeStyle = "grey";
ctx.rect(5, 5, 400, 400);
ctx.stroke();
// blue rectangle
ctx.beginPath();
ctx.lineWidth = "5";
ctx.strokeStyle = "blue";
ctx.rect(x, y, 100, 100);
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
draw();
</script>
</body>
</html>

HTML5 Canvas: Applying a gradient to shadow

I was surprised to find out that apparently the canvas API does not allow you to apply gradients to shadows like this:
var grad = ctx.createLinearGradient(fromX, fromY, toX, toY);
grad.addColorStop(0, "red");
grad.addColorStop(1, "blue");
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
ctx.shadowBlur = 10;
ctx.shadowColor = grad; // doesn't seem to work
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.closePath();
ctx.stroke();
// linear gradient from start to end of line
var canvas = document.getElementById('mycanvas'),
ctx = canvas.getContext('2d'),
fromX = 3,
fromY = 3,
toX = 197,
toY = 197,
grad = ctx.createLinearGradient(fromX, fromY, toX, toY);
canvas.width = 200;
canvas.height = 200;
grad.addColorStop(0, "red");
grad.addColorStop(1, "blue");
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
ctx.shadowBlur = 20;
ctx.shadowColor = grad;
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.closePath();
ctx.stroke();
body {
background: black
}
<canvas id="mycanvas"></canvas>
One workaround is to simply draw the line/shape/etc. multiple times at different sizes and opacity to get a similar result:
var grad = ctx.createLinearGradient(fromX, fromY, toX, toY);
canvas.width = 200;
canvas.height = 200;
grad.addColorStop(0, "red");
grad.addColorStop(1, "blue");
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
//ctx.shadowBlur = 20;
//ctx.shadowColor = grad;
for (var i = 10; i > 1; i--) {
ctx.lineWidth = i;
ctx.globalAlpha = 1 / i;
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.closePath();
ctx.stroke();
}
// linear gradient from start to end of line
var canvas = document.getElementById('mycanvas'),
ctx = canvas.getContext('2d'),
fromX = 3,
fromY = 3,
toX = 197,
toY = 197,
grad = ctx.createLinearGradient(fromX, fromY, toX, toY);
canvas.width = 200;
canvas.height = 200;
grad.addColorStop(0, "red");
grad.addColorStop(1, "blue");
ctx.strokeStyle = grad;
ctx.lineWidth = 3;
//ctx.shadowBlur = 20;
//ctx.shadowColor = grad;
for (var i = 10; i > 1; i--) {
ctx.lineWidth = i;
ctx.globalAlpha = 1 / i;
ctx.beginPath();
ctx.moveTo(fromX, fromY);
ctx.lineTo(toX, toY);
ctx.closePath();
ctx.stroke();
}
body {
background: black;
}
<canvas id="mycanvas"></canvas>
Here's the comparison. Although the change is subtle, the right image shows roughly the desired effect.
Is there a better way of doing this? I imagine there's a more efficient way than drawing the same thing multiple times. Does anyone know of a library that provides this kind of functionality?
Use the filter property of the canvas 2d context. MDN filter though (as usual) it does say filter is not supported on Chrome it has been from some time on the Beta version. For IE I do not know and for FF it has been supported for some time. You will have to test for it if you use it.
UPDATE
Support does not seam to be automatic. Though MDN shows support for Firefox you must set the canvas.filters.enable to true (whatever that means, I am sure firefox lovers know) and seams for chrome you must go to chrome://flags then set experimental canvas features to enabled
More
I have added a fallback as there is such limited support. It uses a second canvas to blur the shadow by using the ctx.imageSmoothingEnabled=true; and rendering at a scale one half the blur amount. So if blur is 5 pixels then in background canvas must be one tenth the size. Then on the original canvas render the background canvas at full size with smoothing on.
No the best result and will no be good for lines, but its fast and can be played around with to optimise results.
Snippet to show how to detect support and use.
var canvas = document.getElementById("canV");
var ctx = canvas.getContext("2d");
var g = ctx.createLinearGradient(10,10,100,100);
for(var i = 0; i <= 1; i+= 0.05){
g.addColorStop(i,"hsl("+Math.floor(i*360)+",100%,50%)");
}
var gDark = ctx.createLinearGradient(20,20,100,100);
for(var i = 0; i <= 1; i+= 0.05){
gDark.addColorStop(i,"hsl("+Math.floor(i*360)+",100%,30%)");
}
ctx.font = "16px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "hanging";
if(ctx.filter !== undefined){
ctx.fillText("Using filter.",65,125);
ctx.fillStyle = gDark;
ctx.filter = "blur(5px)"; // set the blur
ctx.fillRect(20,20,100,100); // draw the shadow
ctx.fillStyle = g; // set the lighter gradoent
ctx.filter = "blur(0px)"; // remove the blur
ctx.lineWidth = 2;
ctx.strokeStyle = "black"
ctx.fillRect(10,10,100,100); // draw the box
ctx.strokeRect(10,10,100,100); // with line to look nice.
}else{
// fallback method
ctx.fillText("Using Fallback.",60,125);
var can = document.createElement("canvas"); // create a second canvas
can.width = Math.floor(canvas.width/10); // size to make one pixel the
can.height =Math.floor(canvas.height/10); // size of the blur
var ctxS = can.getContext("2d");
ctxS.setTransform(1/10,0,0,1/10,0,0); // set scale so can work in same coords
ctxS.fillStyle = gDark;
ctxS.fillRect(20,20,100,100); // draw the shadow
ctx.imageSmoothingEnabled=true;
ctx.drawImage(can,0,0,canvas.width,canvas.height);
}
ctx.fillStyle = g; // set the lighter gradoent
ctx.lineWidth = 2;
ctx.strokeStyle = "black"
ctx.fillRect(10,10,100,100); // draw the box
ctx.strokeRect(10,10,100,100); // with line to look nice.
#canV {
width:200px;
height:200px;
}
<canvas id="canV" width = 200 height =200></canvas>

Drawing Shadow of an object in Canvas

I want to draw one circle and a character with shadow on a canvas in a HTML page while loading the page and recreate the image on a button click. I am using this code:
window.onload = function() {
draw();
};
function draw(){
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
var width = c.width;
var height = c.height;
//DRAW A CIRCLE
var centerX = Math.floor((Math.random() * width));
var centerY = Math.floor((Math.random() * height));
var radius = Math.floor(Math.random() * 50);
var color = '#f11';
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
//DRAW A CHARACTER WITH SHADOW
var c = "S";
ctx.font = "300% Verdana";
ctx.shadowBlur = 20;
ctx.shadowColor = "black";
ctx.shadowOffsetX = 20;
ctx.shadowOffsetY = 20;
ctx.fillStyle = "#111";
ctx.fillText(c, 10, 90);
}
In HTML I am calling draw function onclick() event of a button named Refresh.
For the first time it is giving desired output by drawing one circle and a character with shadow. As I click on the Refresh button it is drawing both the objects with shadow. I dont want to draw shadow of the circle. Can anyone please tell me the mistake I'm doing here.
You may want to use the CanvasRenderingContext2D.save() method :
window.onload = function() {
draw();
};
document.getElementById("canvas").addEventListener('click', draw);
function draw(){
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, c.width, c.height);
var width = c.width;
var height = c.height;
//DRAW A CIRCLE
var centerX = Math.floor((Math.random() * width));
var centerY = Math.floor((Math.random() * height));
var radius = Math.floor(Math.random() * 50);
var color = '#f11';
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
//DRAW A CHARACTER WITH SHADOW
//save the actual context
ctx.save();
var c = "S";
ctx.font = "300% Verdana";
ctx.shadowBlur = 20;
ctx.shadowColor = "black";
ctx.shadowOffsetX = 20;
ctx.shadowOffsetY = 20;
ctx.fillStyle = "#111";
ctx.fillText(c, 10, 90);
//restore it
ctx.restore();
}
canvas{border:1px solid;}
<canvas id="canvas" width="400" height="200"></canvas>

Canvas: Draw Lines on top of Filled Rectangle

I have a function here that's supposed to draw a square and a plus sign in the middle, then reverse colors if the mouse is over it. It works fine if the mouse is not on top, but once it is filled, the plus sign disappears. I assume it's being covered up.
function drawAdd(cx, cy, btnW, btnH, mouse)
{
var getID = document.getElementById("canvas_1");
var color = "black";
var px = cx + btnW/2;
var py = cy + btnH/2;
if (getID.getContext)
{
var ctx = getID.getContext("2d");
ctx.clearRect(cx, cy, btnW, btnH);
ctx.lineWidth = "10";
ctx.fillStyle = "black";
if(mouse)
{
ctx.fillRect(cx, cy, btnW, btnH);
color = "white";
}
else
{
ctx.strokeRect(cx, cy, btnW, btnH);
}
ctx.beginPath();
ctx.lineWidth = "20";
ctx.fillStyle = color;
ctx.moveTo(px - 40, py);
ctx.lineTo(px + 40, py);
ctx.moveTo(px, py - 40);
ctx.lineTo(px, py + 40);
ctx.stroke();
ctx.closePath();
}
}
What am I doing wrong here?
You need to replace ctx.fillStyle = color with ctx.strokeStyle = color as line color is set by strokeStyle, not fillStyle.
Here's a working JSFiddle.

Categories