Tracing the edge of a hexagon pixel by pixel - javascript

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.

Related

Crop the image in irregular shape and stretch it

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

Draw image to canvas with points

I want to draw simple rectangle image to canvas. I have a four point like a;
(0) 345,223
(1) 262,191
(2) 262,107
(3) 347,77
Rendered rectangle and image are bellow;
What is the best practice to do this?
Well that was some fun. Haven't done software texture mapping in over 10 years. Nostalgia is great, but openGL is better. :D
Basically, the idea is to draw vertical slices of the image. The ctx only lets us draw images or parts of them with vertical or horizontal stretching. So, to get around this, we divide the image up into vertical slices, stretching each of them to fill a rectangle 1 pixel wide and from the top edge to the bottom edge.
First, we calculate the slope of the top and bottom edges. This corresponds to the amount that the edge rises (or falls) for each pixel travelled in the +X direction. Next, since the image may be larger or smaller than the are it will be draw onto, we must calculate how wide the strips are that correspond to 1 pixel in the X direction in the canvas.
Note, it isn't perspective-correct. Each step to the right on the canvas represents a step of the same width slice on the image - perspective correct mapping would step by varying amounts across the width of the image. Less as the image got closer, more as the image was further away from us.
Finally, it should be noted that there are a few assumptions made about the entered coordinates.
The coords appear as pairs of x and y
The coords list starts with the top-left corner
The coords must be listed in a clockwise direction
The left-edge and the right-edge must be vertical.
With these assumptions adhered to, I get the following:
Result
Code:
<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded()
{
var mImg = newEl('img');
mImg.onload = function() { stretchImage(this, quadPoints, byId('tgtCanvas') ); }
mImg.src = imgSrc;
}
var quadPoints = [ [262,107], [347,77], [347,223], [262,191] ];
var imgSrc = "img/rss128.png";
function stretchImage(srcImgElem, points, canvasElem)
{
var ctx = canvasElem.getContext('2d');
var yTopStart = points[0][1];
var yTopEnd = points[1][1];
var tgtWidth = points[1][0] - points[0][0];
var dX = tgtWidth;
var topDy = (yTopEnd-yTopStart) / dX;
var yBotStart = points[3][1];
var yBotEnd = points[2][1];
tgtWidth = points[2][0] - points[3][0];
dX = tgtWidth;
var botDy = (yBotEnd-yBotStart) / dX;
var imgW, imgH, imgDx;
imgW = srcImgElem.naturalWidth;
imgH = srcImgElem.naturalHeight;
imgDx = imgW / dX;
var curX, curYtop, curYbot, curImgX;
var i = 0;
// ctx.beginPath();
for (curX=points[0][0]; curX<points[1][0]; curX++)
{
curYtop = yTopStart + (i * topDy);
curYbot = yBotStart + (i * botDy);
curImgX = i * imgDx;
// ctx.moveTo(curX, curYtop);
// ctx.lineTo(curX, curYbot);
var sliceHeight = curYbot - curYtop;
// console.log(sliceHeight);
ctx.drawImage(srcImgElem, curImgX, 0, 1,imgH, curX, curYtop, imgDx, sliceHeight);
i++;
}
// ctx.closePath();
// ctx.stroke();
}
</script>
<style>
canvas
{
border: solid 1px black;
}
</style>
</head>
<body>
<canvas width=512 height=512 id='tgtCanvas'></canvas>
</body>
</html>
Src image:

Rotating a canvas display depending on where the user clicks

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>

lineTo() method in a for loop

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>

Randomize drawing of canvas sections

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>

Categories

Resources