New to javascript (and programming in general) and I am trying to have different characters spawn in my web-game I am making which is represented as different sections in my sprite.
I was wondering how I could achieve this?
I tried to at least change the coordinates as the score would go higher but i couldn't crack it.
function Enemy(nX, nY) {
this.srcX = nX; //i was trying to sub
this.srcY = nY;
this.width = 172;
this.height = 68;
this.speed = 2;
this.drawX = Math.floor(Math.random() * 1000) + gameWidth;
this.drawY = Math.floor(Math.random() * 360);
this.rewardPoints = 5;
}
This is the other half
Enemy.prototype.draw = function () {
this.drawX -= this.speed;
ctxEnemy.drawImage(imgSprite,this.srcX,this.srcY,this.width,this.height,this.drawX,this.drawY,this.width,this.height);
this.checkEscaped();
};
You can see the full thing here. http://dev.yeahnah.tv/gina2/ (fair warning: it's pretty bad.)
Cheers!
How to efficiently spawn random sprites from a spritesheet
Store the position and dimensions of each sprite on a spritesheet in an object:
{x:50, y:15, width:170, height:200}
Then push all sprite definition objects into an array
// store x/y/width/height of each sprite in an object
// add each object to the sprites array
var sprites=[]
sprites.push({x:50,y:15,width:170,height:200});
sprites.push({x:265,y:10,width:140,height:200});
sprites.push({x:460,y:10,width:180,height:200});
sprites.push({x:10,y:385,width:180,height:180});
sprites.push({x:225,y:395,width:200,height:200});
sprites.push({x:445,y:305,width:200,height:160});
Then when you want a random sprite, just use Math.random to pull one out of the sprites array:
function spawnRandomSprite(){
// choose a random sprite and draw it
var sprite=sprites[parseInt(Math.random()*5)];
// draw the sprite by using the spritesheet position and dimensions in the object
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(spritesheet,
sprite.x,sprite.y,sprite.width,sprite.height,
0,0,sprite.width,sprite.height
);
}
You could also store a speed and reward points in the sprite object and use those to drive your animation and your scoring:
{ x:50, y:15, width:170, height:200, speed:3, rewardPoints:5 }
Here’s code and a Fiddle: http://jsfiddle.net/m1erickson/YzUmv/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px;}
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// store x/y/width/height of each sprite in an object
// add each object to the sprites array
var sprites=[]
sprites.push({x:50,y:15,width:170,height:200});
sprites.push({x:265,y:10,width:140,height:200});
sprites.push({x:460,y:10,width:180,height:200});
sprites.push({x:10,y:385,width:180,height:180});
sprites.push({x:225,y:395,width:200,height:200});
sprites.push({x:445,y:305,width:200,height:160});
// choose a random sprite and draw it
function spawnRandomSprite(){
var sprite=sprites[parseInt(Math.random()*5)];
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(spritesheet,
sprite.x,sprite.y,sprite.width,sprite.height,
0,0,sprite.width,sprite.height
);
}
ctx.font="18pt Verdana";
ctx.fillText("Loading spritesheet...",20,20);
var spritesheet=document.createElement("img");
spritesheet.onload=function(){
spawnRandomSprite();
}
spritesheet.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/angryBirds.png";
$("#canvas").click(function(){ spawnRandomSprite(); });
}); // end $(function(){});
</script>
</head>
<body>
<p>Click on the canvas for a random sprite</p>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Related
I have made a canvas with multiple hollow circles. And I am trying to animate it. I tried to use the method requestAnimationFrame, but it was unsuccessful. And if possible, I would prefer trying to stick with this method.
To avoid confusion in my code, I have added as much as reasonable comments which explains how the codes works. Note, that my knowledge regarding Javascript is far from good hence the amount of comments.
Also: Outside of the ending script tag, I am showing the lines of codes I tried to use for animation but was unsuccessful in doing so.
See the code below:
body {
background-color: #000000;
}
canvas {
padding: 0;
margin: auto;
display: block;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: #111416;
border: 10px solid blue;
border-style: double;
box-shadow: 0 0 20px 5px blue;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<link rel="stylesheet" href="canvas.css">
<canvas id="myCanvas" width="350" height="350"></canvas>
<script>
// Get id from the canvas element
var canvas = document.getElementById("myCanvas");
// Provide 2D rendering context for the drawing surface of canvas
var context = canvas.getContext("2d");
// Get width and height of the canvas element
var canvW = document.getElementById("myCanvas").width;
var canvH = document.getElementById("myCanvas").height;
// Create a random range of numbers
const randomRange = (min, max) => {
return Math.random () * (max - min) + min;
}
// Class = Create multiple objects with the same properties
class Point {
// Build the new object and pass parameters
constructor(x, y){
// Define properties
// "this" refers to the scope of the class
// class point contains propertie x and y
this.x = x;
this.y = y;
}
}
// Separate point from canvas
// Make dot a new entity
// Create another class
class Agent {
constructor(x, y){
// First property is position using x and y parameters
this.pos = new Point(x, y);
// Create points with random radius
this.radius = randomRange(4, 12);
}
// Create new method called draw
// Pass paramter as reference to the context
draw (context) {
// Draw the points
context.save();
context.translate(this.pos.x, this.pos.y);
context.beginPath();
context.arc(0, 0, this.radius, 0, Math.PI * 2);
context.lineWidth = 4;
context.strokeStyle = "blue";
context.stroke();
context.restore();
}
}
// Create empty array called
// Note to myself: Cannot be written before the initialization of class agent
const agents = [];
// Create 40 random points using a for loop
for (let i =0; i<40; i++){
const x = randomRange(0, canvW);
const y = randomRange(0, canvH);
// push adds new items to the end of an array
// push changes the length of an array
// push returns the new length
agents.push(new Agent (x, y));
// Call function for each element in the array
agents.forEach(agent => {
agent.draw(context);
});
}
</script>
<!--
// Define velocity of point
this.vel = new Point( randomRange(-1, 1), randomRange(-1, 1));
// Add velocity to the points
update() {
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
}
agent.update();
const animate = () => {}
requestAnimationFrame(animate);
};
animate();
-->
</body>
</html>
I changed a few lines and commented them with 'Info:'.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<link rel="stylesheet" href="canvas.css">
<canvas id="myCanvas" width="350" height="350"></canvas>
<script>
// Get id from the canvas element
var canvas = document.getElementById("myCanvas");
// Provide 2D rendering context for the drawing surface of canvas
var context = canvas.getContext("2d");
// Get width and height of the canvas element
var canvW = document.getElementById("myCanvas").width;
var canvH = document.getElementById("myCanvas").height;
// Create a random range of numbers
const randomRange = (min, max) => {
return Math.random () * (max - min) + min;
}
// Create empty array called
// Note to myself: Cannot be written before the initialization of class agent
// Info: It can, maybe something else was wrong? Although, you might want to keep it together.
const agents = [];
// Class = Create multiple objects with the same properties
class Point {
// Build the new object and pass parameters
constructor(x, y){
// Define properties
// "this" refers to the scope of the class
// class point contains propertie x and y
this.x = x;
this.y = y;
}
}
// Separate point from canvas
// Make dot a new entity
// Create another class
class Agent {
constructor(x, y){
// First property is position using x and y parameters
this.pos = new Point(x, y);
// Define velocity of point.
// Info: Moved velocity in class.
// Todo: Decide where to handle velocity. Set it once? Update it later?
this.vel = new Point(randomRange(-1, 1), randomRange(-1, 1));
// Create points with random radius
this.radius = randomRange(4, 12);
}
// Create new method called draw
// Pass paramter as reference to the context
draw (context) {
// Draw the points
// Info: Added fill to make individual elements visible
context.save();
context.translate(this.pos.x, this.pos.y);
context.beginPath();
context.arc(0, 0, this.radius, 0, Math.PI * 2);
context.lineWidth = 4;
context.strokeStyle = "blue";
context.fillStyle = "white";
context.fill();
context.stroke();
context.restore();
}
// Info: Moved update in class
update() {
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
}
}
// Create 40 random points using a for loop
for (let i =0; i<40; i++){
const x = randomRange(0, canvW);
const y = randomRange(0, canvH);
// push adds new items to the end of an array
// push changes the length of an array
// push returns the new length
agents.push(new Agent (x, y));
}
// Info: Whole drawing and update process can be handled here
const animate = () => {
agents.forEach(agent => {
agent.draw(context);
agent.update();
});
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
I found images that depict what is my problem:
User will able to choose four points on canvas to crop the part of image and than stretch it.
How to do that in HTML5? drawImage function (as I know) works only with rectangles (takes x, y, width and height values) so I can't use irregular shape. The solution have to work in every modern browser, so I don't want things based on webgl or something.
EDIT:
More info: this will be app for editing pictures. I want to let user cut some part of bigger picture and edit that. It will be similar to Paint, so canvas is required to edit pixels.
The effect you're going for is "perspective warping".
Canvas's 2D context cannot do this "out-of-the-box" because it can't turn a rectangle into a trapezoid. Canvas 2D can only do affine transforms which can only form parallelograms.
As user #Canvas says, Canvas 3D (webgl) can do the transforms you're going for.
I did this a while back. It uses Canvas 2d and it redraws an image using 1 pixel wide vertical slices which are stretched to "fake" a perspective warp. You are welcome to use it as a starting point for your project.
Example code and a Demo: http://jsfiddle.net/m1erickson/y4kst2pk/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();
//
var isDown=false;
var PI2=Math.PI*2;
var selectedGuide=-1;
var guides=[];
//
var marginLeft=50;
var marginTop=50;
var iw,ih,cw,ch;
var img=new Image();
img.onload=start;
img.src='https://dl.dropboxusercontent.com/u/139992952/stack1/buildings1.jpg';
function start(){
iw=img.width;
ih=img.height;
canvas.width=iw+100;
canvas.height=ih+100;
cw=canvas.width;
ch=canvas.height;
ctx.strokeStyle="blue";
ctx.fillStyle="blue";
guides.push({x:0,y:0,r:10});
guides.push({x:0,y:ih,r:10});
guides.push({x:iw,y:0,r:10});
guides.push({x:iw,y:ih,r:10});
//
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
drawAll();
}
function drawAll(){
ctx.clearRect(0,0,cw,ch);
drawGuides();
drawImage();
}
function drawGuides(){
for(var i=0;i<guides.length;i++){
var guide=guides[i];
ctx.beginPath();
ctx.arc(guide.x+marginLeft,guide.y+marginTop,guide.r,0,PI2);
ctx.closePath();
ctx.fill();
}
}
function drawImage(){
// TODO use guides
var x1=guides[0].x;
var y1=guides[0].y;
var x2=guides[2].x;
var y2=guides[2].y;
var x3=guides[1].x;
var y3=guides[1].y;
var x4=guides[3].x;
var y4=guides[3].y;
// calc line equations slope & b (m,b)
var m1=Math.tan( Math.atan2((y2-y1),(x2-x1)) );
var b1=y2-m1*x2;
var m2=Math.tan( Math.atan2((y4-y3),(x4-x3)) );
var b2=y4-m2*x4;
// draw vertical slices
for(var X=0;X<iw;X++){
var yTop=m1*X+b1;
var yBottom=m2*X+b2;
ctx.drawImage( img,X,0,1,ih,
X+marginLeft,yTop+marginTop,1,yBottom-yTop );
}
// outline
ctx.save();
ctx.translate(marginLeft,marginTop);
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.lineTo(x4,y4);
ctx.lineTo(x3,y3);
ctx.closePath();
ctx.strokeStyle="black";
ctx.stroke();
ctx.restore();
}
function handleMouseDown(e){
e.preventDefault();
var mouseX=parseInt(e.clientX-offsetX);
var mouseY=parseInt(e.clientY-offsetY);
// Put your mousedown stuff here
selectedGuide=-1;
for(var i=0;i<guides.length;i++){
var guide=guides[i];
var dx=mouseX-(guide.x+marginLeft);
var dy=mouseY-(guide.y+marginTop);
if(dx*dx+dy*dy<=guide.r*guide.r){
selectedGuide=i;
break;
}
}
isDown=(selectedGuide>=0);
}
function handleMouseUp(e){
e.preventDefault();
isDown=false;
}
function handleMouseOut(e){
e.preventDefault();
isDown=false;
}
function handleMouseMove(e){
if(!isDown){return;}
e.preventDefault();
var x=parseInt(e.clientX-offsetX)-marginLeft;
var y=parseInt(e.clientY-offsetY)-marginTop;
var guide=guides[selectedGuide];
guides[selectedGuide].y=y;
if(selectedGuide==0 && y>guides[1].y){guide.y=guides[1].y;}
if(selectedGuide==1 && y<guides[0].y){guide.y=guides[0].y;}
if(selectedGuide==2 && y>guides[3].y){guide.y=guides[3].y;}
if(selectedGuide==3 && y<guides[2].y){guide.y=guides[2].y;}
drawAll();
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Perspective Warp by vertically dragging left or right blue guides.</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
So here's a killing trick : You can use the regular drawImage of the context2d to draw a texture inside a triangle.
The constraint is that the texture coordinates must be axis-aligned.
To draw a textured triangle you have to :
• Compute by yourself the transform required to draw the image.
• Clip to a triangle, since drawImage would draw a quad.
• drawImage with the right transform.
So the idea is to split your quad into two triangles, and render both.
But there's one more trick : when drawing the lower-right triangle, texture reading should start from the lower-right part of the texture, then move up and left. This can't be done in Firefox, which accepts only positive arguments to drawImage. So i compute the 'mirror' of the first point vs the two others, and i can draw in the regular direction again -the clipping will ensure only right part is drawn-.
fiddle is here :
http://jsfiddle.net/gamealchemist/zch3gdrx/
function rasterizeTriangle(v1, v2, v3, mirror) {
var fv1 = {
x: 0,
y: 0,
u: 0,
v: 0
};
fv1.x = v1.x;
fv1.y = v1.y;
fv1.u = v1.u;
fv1.v = v1.v;
ctx.save();
// Clip to draw only the triangle
ctx.beginPath();
ctx.moveTo(v1.x, v1.y);
ctx.lineTo(v2.x, v2.y);
ctx.lineTo(v3.x, v3.y);
ctx.clip();
// compute mirror point and flip texture coordinates for lower-right triangle
if (mirror) {
fv1.x = fv1.x + (v3.x - v1.x) + (v2.x - v1.x);
fv1.y = fv1.y + (v3.y - v1.y) + (v2.y - v1.y);
fv1.u = v3.u;
fv1.v = v2.v;
}
//
var angleX = Math.atan2(v2.y - fv1.y, v2.x - fv1.x);
var angleY = Math.atan2(v3.y - fv1.y, v3.x - fv1.x);
var scaleX = lengthP(fv1, v2);
var scaleY = lengthP(fv1, v3);
var cos = Math.cos,
sin = Math.sin;
// ----------------------------------------
// Transforms
// ----------------------------------------
// projection matrix (world relative to center => screen)
var transfMatrix = [];
transfMatrix[0] = cos(angleX) * scaleX;
transfMatrix[1] = sin(angleX) * scaleX;
transfMatrix[2] = cos(angleY) * scaleY;
transfMatrix[3] = sin(angleY) * scaleY;
transfMatrix[4] = fv1.x;
transfMatrix[5] = fv1.y;
ctx.setTransform.apply(ctx, transfMatrix);
// !! draw !!
ctx.drawImage(bunny, fv1.u, fv1.v, v2.u - fv1.u, v3.v - fv1.v,
0, 0, 1, 1);
//
ctx.restore();
};
Edit : i added the relevant comment of #szym , with his example picture :
This only sort of looks right. If there are any straight lines in the
original image you will see that each triangle is warped differently
(2 different affine transforms rather than a perspective transform).
You need to have a container with perspective and perspective-origin set
You need to use rotateY, skewY and change your heights and width on your image
There is probably a lot of math behind this - personally I just fiddle with it in my browser to make it look pretty close to what I need
So here is a fiddle:
http://jsfiddle.net/6egdevwe/1/
#container {
margin: 50px;
perspective: 166px; perspective-origin: 50% 0px; }
#testimage {
transform: rotateY(93.4deg) skewY(34deg);
width: 207px;
height: 195px; }
css3 transform -> rotation or rotationZ
http://www.w3schools.com/cssref/css3_pr_transform.asp
I have a html5 canvas display that i would like to rotate once a user clicks. I am trying to figure out the best way to allow the animation take effect. I would like the rotation to be smooth and no matter where the user clicks the selected panel would rotate to the top.
I am not sure of the best way to go about this since this is my very first canvas project, and I figured i would open the floor to all of you.
Here is a fiddle: http://jsfiddle.net/JRgtg/
Thanks in advance for the help!
Your task is fairly complex:
create an object for each arc segment around the circle
save the arc objects in an arcs[] array
create a function that draws a specified arc at a specified angle
(the arc is drawn as a path)
listen for mousedown events
in the mousedown handler, use context.isPointInPath to see if user clicked on an arc-segment-path
if clicked, rotate the clicked arc to the top of the circle using animation
Illustration of arc positions before and after the gold arc was clicked.
The gold arc rotated to the top:
Commented Example code and a Demo: http://jsfiddle.net/m1erickson/ZUtL8/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
// canvas and context reference variables
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
// save PI to variables since they are used often
var PI=Math.PI;
var PI2=Math.PI*2;
// animation variables
var rotation;
var desiredRotation;
var deltaRotation=PI/120; // rotate at about 360 degrees over 2 seconds
// define a color for each segment
var colors=["red","green","blue","gold","purple"];
var topAngle=clampAngle(PI*3/2-(PI2/colors.length)/2);
var gapAngle=2*PI/180; // 3 degree gap between arcs
// hold the arc objects in an arcs[] array
var arcs=createArcs(150,150,50,75,colors);
// draw the arcs
for(var i=0;i<arcs.length;i++){
drawArc(arcs[i],true);
}
// listen for mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
// utility function
// make sure angles are expressed between 0 & 2*PI
function clampAngle(a){
return((a+PI2*2)%PI2);
}
// create arc objects for each color
function createArcs(cx,cy,insideRadius,outsideRadius,colors){
var arcs=[];
for(var i=0;i<colors.length;i++){
var a1=clampAngle(i*PI2/colors.length+topAngle);
var a2=clampAngle(a1+PI2/colors.length-gapAngle);
arcs.push({
segment:i,
x:cy,
y:cy,
r1:insideRadius,
r2:outsideRadius,
a1:a1,
a2:a2,
color:colors[i],
rotation:0
});
}
return(arcs);
}
// draw one arc
function drawArc(arc,draw){
var x = arc.x + arc.r1 * Math.cos(arc.a2);
var y = arc.y + arc.r1 * Math.sin(arc.a2);
// define
ctx.beginPath();
ctx.arc(arc.x,arc.y,arc.r2,arc.a1,arc.a2);
ctx.lineTo(x, y);
ctx.arc(arc.x,arc.y,arc.r1,arc.a2,arc.a1,true);
ctx.closePath();
//
if(draw){
ctx.fillStyle=arc.color;
ctx.fill();
}
}
// handle mouse events
function handleMouseDown(e){
e.preventDefault();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// hit test each arc
var hit=-1;
for(var i=0;i<arcs.length;i++){
// define the target arc
drawArc(arcs[i]);
if(ctx.isPointInPath(mouseX,mouseY)){
hit=i;
}
}
// if use clicked on arc, rotate it to the top
if(hit>=0){
rotation=0;
desiredRotation=clampAngle(topAngle-arcs[hit].a1);
animate();
}
}
// animate the rotation of the clicked arc
function animate(){
// stop animating if the arc has been rotated to the top
if(rotation<=desiredRotation){ requestAnimationFrame(animate); }
if(rotation>desiredRotation){ rotation=desiredRotation; }
// clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// add a rotation increment to each arc
for(var i=0;i<arcs.length;i++){
var arc=arcs[i];
arc.a1=clampAngle(arc.a1+deltaRotation);
arc.a2=clampAngle(arc.a2+deltaRotation);
drawArc(arc,true);
}
// increase the rotation angle by the rotation increment
rotation=clampAngle(rotation+deltaRotation);
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Click on a color-arc and it will rotate to top.</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I am trying to draw a grid using two for loops, one for drawing 10 vertical lines, and another for 10 horizontal ones. Like this:
for(var i=1;i<10;i++){
context.moveTo(0,i*b/10);
context.lineTo(a,i*b/10);
context.stroke();
}
So the lines drawn are different widthes, blurry..I read adding 0.5 in both moveTo() and lineTo() methods but that does not work either. There is no proportional way of achieving all 10 lines being the same.
First, why is so and what could I do?
That is really weird. I had tested all you wrote here and the results were the same. Now I desperately opened firefox and there everything looks perfect. So it is about chrome only then.
Add this line before you start drawing
context.translate(0.5, 0.5);
and the lines will be razor sharp provided you use integer numbers for the positions.
Specifically in the code you provide:
context.translate(0.5, 0.5);
....
context.moveTo(0, (i * b / 10)|0); /// y is rounded to integer
or instead of translating:
context.moveTo(0.5, (i * b / 10)|0);
With canvas the center of a pixel is not on an absolute pixel on screen. Therefor you need to offset it half a pixel to align it with the actual pixel or the pixel will get sub-pixeled which result in an anti-aliased line.
You could just as well add 0.5 to each position instead of doing a translate, but the translate is simpler. Just translate back after the grid is done.
Snapshot from demo
You also have a second issue in your code: you are stroking the line then continue to add to the same Path which will accumulate all the lines added previously and reduce performance.
In the same way as there is also no need to use beginPath() for each lines if all the lines will have the same characteristics (color, thickness etc.) you don't need to stroke each line either.
Just add all the lines with moveTo and lineTo to the Path (moveTo will make sure the lines aren't connected) and when the loops are finished then do a common stroke().
ONLINE DEMO HERE
/// translate 0.5
ctx.translate(0.5, 0.5);
/// create grid
ctx.beginPath();
/// add all grid lines to Path
for(;pos < width; pos += step) {
ctx.moveTo(pos, 0);
ctx.lineTo(pos, height);
ctx.moveTo(0, pos);
ctx.lineTo(width, pos);
}
/// common stroke = higher performance
ctx.stroke();
The blurryness is anti-aliasing and can partially be avoided specifying drawing positions in whole integers.
Also, If you draw with lineWidth=.5 and specify a .5 drawing offset, you will have clearer lines.
Here’s code and a Fiddle: http://jsfiddle.net/m1erickson/4gduD/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var w=canvas.width;
var h=canvas.height;
drawGrid("black", 20,20);
function drawGrid(color, stepx, stepy) {
ctx.save()
ctx.strokeStyle = color;
ctx.lineWidth = 0.5;
ctx.clearRect(0, 0, w, h);
for (var i = stepx + 0.5; i < w; i += stepx) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i, h);
ctx.stroke();
}
for (var i = stepy + 0.5; i < h; i += stepy) {
ctx.beginPath();
ctx.moveTo(0, i);
ctx.lineTo(w, i);
ctx.stroke();
}
ctx.restore();
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
To create an animation in Javascript using an HTML5 canvas I first need to be able to describe a point by point path around a hexagonal shape. I already have the x/y coordinate of each vertex. I don't know which direction I will be travelling around the edge so any solution should be able to work in either direction.
The radius, and therefore each side, of the hexagon is 20 pixels. I need to produce a set of 20 points for each side that maps the x and y position of each pixel in that path. This is obviously easy for straight lines where each pixel increments 1 for each step and the other axis remains static. With the angled sides I am failing get the trigonometry required to plot the points.
I'm fairly positive this is trivial but would appreciate some help getting clear in my mind.
This code will draw equidistant dots from point x1/y1 to point x2/y2.
Works in reverse also (x2/y2 to x1/y1).
Since you have all the x/y for each vertex, you should be good to go!
Here is the code and a Fiddle: http://jsfiddle.net/m1erickson/pW4De/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
p{font-size:24px;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
DrawDottedLine(300,400,7,7,7,20,"green");
function DrawDottedLine(x1,y1,x2,y2,dotRadius,dotCount,dotColor){
var dx=x2-x1;
var dy=y2-y1;
var spaceX=dx/(dotCount-1);
var spaceY=dy/(dotCount-1);
var newX=x1;
var newY=y1;
for (var i=0;i<dotCount;i++){
drawDot(newX,newY,dotRadius,dotColor);
newX+=spaceX;
newY+=spaceY;
}
drawDot(x1,y1,3,"red");
drawDot(x2,y2,3,"red");
}
function drawDot(x,y,dotRadius,dotColor){
ctx.beginPath();
ctx.arc(x,y, dotRadius, 0, 2 * Math.PI, false);
ctx.fillStyle = dotColor;
ctx.fill();
}
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=307 height=407></canvas>
</body>
</html>
You might consider Bresenhams line algorithm. It is a standard goto and easy to implement.... http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
function plotLine(p1, p2){
var dx = p2.x - p1.x;
var dy = p2.y - p2.y;
var err = 0.0;
var derr = Math.abs( dy / dx );
var y = p1.y;
for(var x = p1.x; x < p2.x; x++){
drawPoint(new Point(x,y));
err = err + derr;
if(err >= 0.5 ) {
y++;
err = err - 1.0;
}
}
}
Although this might be a bad approach since it wont be anti aliased. The are line drawing algorithms that implement aliasing (Google it...) or the best be is to use the Canvases built in line drawing api and just overlay successive lines that get longer and longer.