I am currently writing a simple snake game.
http://jsfiddle.net/jhGq4/3/
I started with drawing the grid for the background
function fill_grid() {
ctx.beginPath();
var row_no = w/cw;
var col_no = h/cw;
for (var i=0;i<row_no;i++)
{
for (var j=0;j<col_no;j++)
{
ctx.rect(i*cw, j*cw, (i+1)*cw, (j+1)*cw);
ctx.fillStyle = 'black';
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#135d80';
ctx.stroke();
}
}
}
It works great but when i paint the snake, the position gets wrong and the length is doubled. I tried to console.log the x position of my snake but they are correct.
function paint_cell(x, y)
{
console.log(x*cw);
console.log((x+1)*cw);
ctx.fillStyle = '#fff799';
ctx.fillRect(x*cw, y*cw, (x+1)*cw, (y+1)*cw);
ctx.lineWidth = 1;
ctx.strokeStyle = '#135d80';
ctx.strokeRect(x*cw, y*cw, (x+1)*cw, (y+1)*cw);
}
***Because someone wants to learn how to make a snake game,
this is my final solution for this game.
http://jsfiddle.net/wd9z9/
You can also visit my site to play:
use WSAD to play.
http://www.catoyeung.com/snake2/single.php
:D
I tested this alternative init function:
function init()
{
fill_grid();
// create_snake1();
// paint_snake1();
paint_cell(5, 5)
}
Which paints a square starting at coords (5,5), but of size 6x6.
I believe you are misusing the fillRect() method. Use
ctx.fillRect(x*cw, y*cw, cw, cw);
Instead of
ctx.fillRect(x*cw, y*cw, (x+1)*cw, (y+1)*cw);
the same probably applies to strokeRect.
Incidentally, I was looking at your grid-fill function. You fill i*j adjacent squares, which works, but you really only need i vertical lines and j horizontal ones - which is a lot less processing. Run a quicker loop with fewer calculations:
function fill_grid() {
ctx.beginPath();
ctx.fillStyle = 'black';
ctx.fillRect(0,0,w,h);
for (var i=0;i<=h;i+=cw)
{
ctx.moveTo(0,i);
ctx.lineTo(w,i);
ctx.moveTo(i,0);
ctx.lineTo(i,h);
}
ctx.stroke();
}
I would also only define line width and stroke colour once, as you initialise the canvas. You may not need to worry about this of course. But this kind of care can make a difference if you refresh the grid often to animate your snakes.
Related
I was trying to do an Olympic type flag, purely as a way of learning how to draw in JavaScript. This should draw two circles - one blue, one black...
Here is the code (which I apologise for, been moving things between the two functions - Not sure how to refer to the context non-explicitly):
function drawCircle(ctx,x,y,radius, color){
var startAngle = 0;
var endAngle = (Math.PI*2);
var clockwise = true;
ctx.fillStyle = color;
ctx.arc(x,y,radius,startAngle,endAngle, clockwise);
ctx.fill();
ctx.closePath;
}
function drawCircles(){
var canvas = document.getElementById('myCanvasArea');
var ctx = canvas.getContext('2d');
if (canvas.getContext){
drawCircle(ctx,50,25,25, 'blue');
drawCircle(ctx,100,25,25, 'black');
}
}
I get two black circles. I presume I'm not differentiating between the two shapes, therefore the properties of the 2nd are applied to the 1st.
How do I make that distinction? I was thinking of making clicking each one raise an action. Am I going about this incorrectly from the start?
It's because you are never calling beginPath()!
function drawCircle(ctx,x,y,radius, color){
var startAngle = 0;
var endAngle = (Math.PI*2);
var clockwise = true;
ctx.fillStyle = color;
ctx.beginPath(); // <-- Need me!!
ctx.arc(x,y,radius,startAngle,endAngle, clockwise);
ctx.fill();
ctx.closePath;
}
Since you don't call beginPath, you are drawing one blue circle, then you are continuing a path that now has two circles (the old one and the new one), and drawing that path (and thus both circles) black!
Instead you want to draw one blue circle, fill it blue, begin a new path, and draw that one black.
Live code:
http://jsfiddle.net/5PDUb/1/
I'm looking for a way to lines with markers in a <canvas> element, like the picture bellow.
In SVG we can do that thanks the markers but I haven't found a similar way to do that with canvas. I know that we can create patterns in canvas thanks to the createPattern function but I doubt it could help to solve the issue.
EDIT: This is not a duplicate of this question since I'm looking for a way to repeat a shape/marker on a path. It's not about placing marker at a specific given point.
As I've commented bellow, I've discovered the svg-path-properties which is almost perfect for my solution.
There is unfortunately no native way to add markers to strokes in the canvas API. Even if we are able to set strokeStyle to a CanvasPattern, there is no way to make this pattern follow our path's direction.
This means that you'll have to make the calculations of the direction and position of your markers yourself...
For this, you can refer to many posts, like this one proposed by Tomàs Antunes in comments.
However, to do the exact triangle shape you shown to us, I've got an non-mathy hack, which doesn't work very well, but that I'd like to share anyway.
Strokes can have dash-arrays, which will create gaps in the stroke.
By offsetting multiple times this dash-array, and decreasing the lineWidth of our stroke, we can sort-of create these arrow shapes :
var ctx = c.getContext('2d');
// our default path
ctx.beginPath();
ctx.moveTo(50, 20);
ctx.bezierCurveTo(230, 30, 150, 60, 50, 100);
ctx.bezierCurveTo(250, 50, 150, 60, 150, 150);
ctx.stroke();
// the hack
for (var i = 0; i < 9; i += .5) {
// set an dasharray of 1px visible, then 40 invisible
ctx.setLineDash([1, 49]);
// offset the dash-array by one px
ctx.lineDashOffset = i;
// reduce the width of our stroke
ctx.lineWidth = i;
ctx.stroke();
}
<canvas id="c"></canvas>
But the main caveat of this method (apart from requiring to redraw the same path 20 times) is that it will really follow the path, and that in angles, it may not completely look like our triangle shape anymore.
var ctx = c.getContext('2d');
// our default path
ctx.beginPath();
ctx.moveTo(50, 20);
ctx.bezierCurveTo(230, 30, 150, 60, 50, 100);
ctx.stroke();
// the hack
for (var i = 0; i < 9; i += .5) {
// set an dasharray of 1px visible, then 20 invisible
ctx.setLineDash([1, 14]);
// offset the dash-array by one px
ctx.lineDashOffset = i;
// reduce the width of our stroke
ctx.lineWidth = i*2;
ctx.stroke();
}
<canvas id="c"></canvas>
I am trying to build simon game using html5 canvas and pure javascript. I have managed to get the simon game UI using html5 canvas. My next step is to make the four components light up randomly. I am not sure if this is even possible with html5 canvas or probably my approach is wrong. Any hints in the right direction will be of great help. My code is as follows
codepen link: http://codepen.io/anon/pen/QEdPRN?editors=1010
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
//bigger circle
ctx.beginPath();
ctx.arc(235,230,140,0,2*Math.PI);
ctx.fillStyle = '#000';
ctx.fill();
ctx.stroke();
//smaller circle
ctx.beginPath();
ctx.arc(235,230,60,0,2*Math.PI);
ctx.fillStyle = '#fff';
ctx.fill();
ctx.stroke();
//draw the four arcs
var x = [240,240,230,230];
var y = [240,225,225,240];
var start = [0,1.5*Math.PI,1*Math.PI,0.5*Math.PI];
var end = [0.5*Math.PI,0,1.5*Math.PI,1*Math.PI];
var color = ["blue","red","green","yellow"];
var draw = function (a,b,c,d,e) {
ctx.beginPath();
ctx.arc(a,b,90,c,d);
ctx.lineWidth = 50;
ctx.strokeStyle = e;
ctx.stroke();
}
function drawSimon() {
for(var i=0;i<4;i++){
draw(x[i],y[i],start[i],end[i],color[i]);
}
}
drawSimon();
Your first problem: This is just a static image.
You only call drawSimon() once, thus it only gets drawn once. To fix this, you need to use requestAnimationFrame or setInterval (preferably the first).
requestAnimationFrame is like a simple method call, but delays the method, so it lines up with the screen's framerate. You need to call drawSimon from inside drawSimon with this.
function drawSimon() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); //Clear the screen
//Draw the simon here
requestAnimationFrame(drawSimon);
}
drawSimon();
Next you want to choose a random color and make it lighter. There's a problem with this. Your colors are all already pure colors, you can't make them brighter. You need to use darker colors (example: rgb(150, 0, 0) instead of red). Then you need to choose a random index between 0 and 3 (inclusively), and make the color in that place brighter.
var index = Math.floor(Math.random() * 4);
switch (index) {
case 0:
color[0] = "blue";
break;
case 1:
color[0] = "red";
break;
case 2:
color[0] = "green";
break;
case 3:
color[0] = "yellow";
break;
}
Third step: make the colors change back.
You could achieve this with a time counter. Each time you set a color to brighter save the time this was done. Each frame, check the time between the current time and the last time you changed to colors, and if it's over a specific limit, set them back the same way you did with the brighter colors.
//global scope:
var lastChange = 0;
//Change a color to lighter here
lastChange = Date.now();
//Later in the code
if (Date.now() - lastChange > maxTime) {
//Change colors back here
}
I put this maze game together for a school project with the help from a few partner. I've gotten drawing on images to work, but my teacher is looking for some form of boundary/border preventing the user from skipping past walls. I'm not even sure if it's possible.
This is the block for individual images along with a short snippet of the Level selector buttons.
Note: These are just parts of the code arranged to show you guys the part I'm talking about. It's formatted differently in the actual file.
<li align="center" style="color:yellow"><div style="color:yellow">(Medium) </div><input type="button" id="l2" value="Level 2" onClick="test2()"/>
<script>
var img2 = new Image();
function test2() {
can.width = img2.width;
can.height = img2.height;
ctx.drawImage(img2, 0, 0);
}
img2.src = 'http://www.hereandabove.com/cgi-bin/maze?30+30+20+5+5+0+0+0+255+255+255.jpg';
</script>
Here is the code I am using to allow drawing on the images.
<canvas id="can1">
<script>
var el = document.getElementById('can1');
var ctx = el.getContext('2d');
var isDrawing;
el.onmousedown = function(e) {
isDrawing = true;
ctx.strokeStyle = "green";
ctx.lineWidth = 4;
ctx.lineJoin = ctx.lineCap = 'round';
ctx.moveTo(e.offsetX, e.offsetY);
};
el.onmousemove = function(e) {
if (isDrawing) {
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
}
};
el.onmouseup = function() {
isDrawing = false;
};
</script>
Without knowing what your maze images look like it's a little difficult, but a very rudimentary solution would be to check the color of the image at the mouse cursor point, and depending on the color decide whether to keep drawing.
I've got some mostly accurate code below, the idea is in this case to get the color of the image at the position of the mouse cursor. In this case I assume a black and white maze where the walls are black and movable area is white. I check the color data to see that red is maxed out (this will only work if your walking area is a color where red is maxed out, like white, red, or yellow. For different colors adjust accordingly, but here rgbColorData is an array where 0 is red, 1 is green, and 2 is blue). As long as the color determines it's movable, then you move.
Again, without the actual code it's hard to provide a perfect answer, but I'd go in this direction.
var rgbColorData = ctx.getImageData(e.offsetX, e.offsetY, 1, 1).data;
if (rgbColorData[0] == 255) {
isDrawing = true;
ctx.strokeStyle = "green";
ctx.lineWidth = 4;
ctx.lineJoin = ctx.lineCap = 'round';
ctx.moveTo(e.offsetX, e.offsetY);
}
I'm curious to know how applications such as Adobe Photoshop implement their drawing history with the ability to go back or undo strokes on rasterized graphics without having to redraw each stroke from the beginning...
I'm wanting to implement a similar history function on an HTML5 drawing application I'm working on but duplicating the canvas after every stoke seems like it'd use too much memory to be a practical approach, especially on larger canvas'...
Any suggestions on how this might be implemented in a practical and efficient manner?
I may have a solution.....
var ctx = document.getElementById("canvasId").getContext("2d");
var DrawnSaves = new Array();
var Undo = new Array();
var FigureNumber = 0;
var deletingTimer;
function drawLine(startX, startY, destX, destY) {
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(destX, destY);
ctx.stroke();
var Para = new Array();
Para["type"] = "line";
Para["fromX"] = startX;
Para["fromY"] = startY;
Para["toX"] = destX;
Para["toY"] = destY;
DrawnSaves.push(Para);
FigureNumber++;
}
function undo() {
ctx.beginPath();
ctx.clearRect(0, 0, 500, 500);
Undo[FigureNumber] = DrawnSaves[FigureNumber];
DrawnSaves[FigureNumber] = "deleted";
FigureNumber--;
drawEverything();
startTimeoutOfDeleting();
}
function undoTheUndo() {
FigureNumber++;
DrawnSaves[FigureNumber] = Undo[FigureNumber];
drawEverything();
clearTimeout(deletingTimer);
}
function drawEverything() {
for (i = 0; i < DrawnSaves.length; i++) {
if (DrawnSaves[i].type == "line") {
ctx.beginPath();
ctx.moveTo(DrawnSaves[i].fromX, DrawnSaves[i].fromY);
ctx.lineTo(DrawnSaves[i].toX, DrawnSaves[i].toY);
ctx.stroke();
}
}
}
function startTimeoutOfDeleting() {
setTimeout(function() {Undo[FigureNumber] = "deleted";}, 5000);
}
This is really simple, first I draw a line when the function is called and save all his parameters in an array. Then , in the undo function I just start a timer do delete the figure drawn i 2000 miliseconds, clears the whole canvas and makes it can't be redrawn. in the undoTheUndo function, it stops the timer to delete the figure and makes that the figure can be redrawn. In the drawEverything function, it draws everything in the array based on it's type ("line here"). That's it... :-)
Here is an example working : This, after 2sec UNDOs then after 1sec UNDOTHEUNDO