I know that I can use the "translate-rotate-translate back-fillrect" procedure to rotate one single rectangle.
However, what if I want to rotate them both at the same time and have a timeinterval() to make them rotate automatically every second?
I tried to save and restore each time I try to draw, but it didn't work.
You appear to be on the right track!
Save
Translate(rectX,rectY)
Rotate
fillRect(-rectWidth/2,-rectHeight/2,rectWidth,rectHeight) // draws the rect w/ center rotation
Restore
For multiple Rectangles
If you define some rectangle objects like this:
var rects=[];
rects.push({x:50,y:50,w:50,h:35,color:'green',angle:0});
rects.push({x:150,y:120,w:30,h:20,color:'blue',angle:0});
Then you can put them in an animation frame like this:
requestAnimationFrame(animate);
function animate(time){
// call for another loop in the animation
requestAnimationFrame(animate);
// clear canvas and redraw all rects
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<rects.length;i++){
// draw this rect at its specified angle
var rect=rects[i];
ctx.save();
ctx.translate(rect.x+rect.w/2,rect.y+rect.h/2);
ctx.rotate(rect.angle);
ctx.fillStyle=rect.color;
ctx.fillRect(-rect.w/2,-rect.h/2,rect.w,rect.h);
ctx.restore();
// increase this rect's angle for next time
rect.angle+=(Math.PI*2)/60;
}
}
requestAnimationFrame loops at about 60fps so if you increment the rect's angle in each loop by (Math.PI*2)/60 you will rotate the rect about one full turn every second.
Example code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var rects=[];
rects.push({x:50,y:50,w:50,h:35,color:'green',angle:0});
rects.push({x:150,y:120,w:30,h:20,color:'blue',angle:0});
requestAnimationFrame(animate);
function animate(time){
// call for another loop in the animation
requestAnimationFrame(animate);
// clear canvas and redraw all rects
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<rects.length;i++){
// draw this rect at its specified angle
var rect=rects[i];
ctx.save();
ctx.translate(rect.x+rect.w/2,rect.y+rect.h/2);
ctx.rotate(rect.angle);
ctx.fillStyle=rect.color;
ctx.fillRect(-rect.w/2,-rect.h/2,rect.w,rect.h);
// orientation symbol
ctx.fillStyle='red';
ctx.fillRect(-rect.w/2,-rect.h/2,5,5)
ctx.restore();
// increase this rect's angle for next time
rect.angle+=(Math.PI*2)/60;
}
}
function drawRect(rect){
ctx.save();
ctx.translate(rect.x+rect.w/2,rect.y+rect.h/2);
ctx.rotate(rect.angle);
ctx.fillStyle=rect.color;
ctx.fillRect(-rect.w/2,-rect.h/2,rect.w,rect.h);
ctx.restore();
rect.angle+=deltaAngle;
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>
Related
im using a canvas to visualize a small game of mine.
Basicly i have two objects that represent space ships, each of them has a "Location" array which holds the ships current x/y.
According to these arrays, i drawImage on the canvas (totalw/h is 300/300 fyi).
Now, for the difficult part.
i want to draw animations (gunfire) on that canvas. basicly from ship1 x/y to ship2 x/y.
For the animation function itself, im passing an effects object that holds 3 Arrays, shooter.location[x, y], target.location[x, y] and a third array that holds where the EFFECT is currently at [x, y].
this.animateEffects = function(effects){
var shooter = effects.shooter;
var target = effects.target;
var current = effects.current;
var canvas = document.getElementById("effects");
var context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.fillStyle = "red";
context.arc(current[0], current[1], 5, 0, 2*Math.PI);
effects.current[0]++
effects.current[1]++
context.fill();
if (current == target){
console.log("ding");
this.end()
}
}
My "problem" is that im, if possible at all, looking for a smart way to determine (for each frame) if effects[x, y] should go ++ or -- or a combination of the two, depending on where the "moving" ships are located at (at the time, the shooting started).
Any advise or hints are appreciated.
You can fire a bullet from shooter to target using linear interpolation.
Calculate the difference in the original X & Y positions of the shooter and target.
// save the starting position of the bullet (== shooter's original position)
// (these original X & Y are needed in the linear interpolation formula)
bulletOriginalX=shooter.x;
bulletOriginalY=shooter.y;
// calc the delta-X & delta-Y of the shooter & target positions
// (these deltas are needed in the linear interpolation formula)
dx=target.x-shooter.x;
dy=target.y-shooter.y;
Move the bullet towards the target using the interpolation formula
// where percent == the percent you want the bullet to be between
// it's starting & ending positions
// (between starting shooter & starting target positions)
currentBulletX=bulletOriginalX+dx*percent;
currentBulletY=bulletOriginalY+dy*percent;
Here's an example:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
shooter={x:50,y:50};
target={x:100,y:100};
effect={x:50,y:50,dx:0,dy:0,pct:0,speedPct:0.25};
draw();
fire();
$('#test').click(function(){
moveEffect();
draw();
});
function fire(){
effect.x=shooter.x;
effect.y=shooter.y;
effect.dx=target.x-shooter.x;
effect.dy=target.y-shooter.y;
effect.pct=0;
}
function moveEffect(){
effect.pct+=effect.speedPct;
}
function draw(){
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
ctx.arc(shooter.x,shooter.y,15,0,Math.PI*2);
ctx.closePath();
ctx.strokeStyle='green';
ctx.stroke();
ctx.beginPath();
ctx.arc(target.x,target.y,15,0,Math.PI*2);
ctx.closePath();
ctx.strokeStyle='red';
ctx.stroke();
if(effect.pct>1){return;}
var x=effect.x+effect.dx*effect.pct;
var y=effect.y+effect.dy*effect.pct;
ctx.beginPath();
ctx.arc(x,y,3,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle='black';
ctx.fill();
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id=test>Animate 1 frame</button>
<br><canvas id="canvas" width=300 height=300></canvas>
I want to swap two circular cuts from an image and swap their locations and draw them to the canvas. I am having issues, however, drawing the second circular image. Interestingly in second if you uncomment the first line it draws that image, otherwise if you throw it at the end of the function, it doesn't draw (at least on top). If I comment out the first function, I get the image to draw on the canvas. The first function always executes before the second.
function first() {
ctx.drawImage(imgBig, 0, 0);
ctx.beginPath();
ctx.arc(imgObj1.x + imgObj1.width / 2, imgObj1.y + imgObj1.width / 2, imgObj1.width / 2, 0, 6.28, false);
ctx.clip();
ctx.stroke(); // the outline of circle
ctx.closePath();
ctx.drawImage(imgBig, imgObj2.x, imgObj2.y, imgObj2.width, imgObj2.height, imgObj1.x, imgObj1.y, imgObj1.width, imgObj1.height);
function second() {
// ctx.drawImage(imgCuttingBoard, 0, 0); // this will draw over canvas
ctx.beginPath();
ctx.arc(imgObj2.x + imgObj2.width / 2, imgObj2.y + imgObj2.width / 2, imgObj2.width / 2, 0, 6.28, false);
ctx.clip();
ctx.closePath();
ctx.drawImage(imgCuttingBoard, imgObj1.x, imgObj1.y, imgObj1.width, imgObj1.height, imgObj2.x, imgObj2.y,
imgObj2.width, imgObj2.height); // this doesn't draw on top of the image (might be drawing underneath?)
// ctx.drawImage(imgCuttingBoard, 0, 0); // this will not draw over canvas here
}
Assuming you have given the image time to fully load before trying to drawImage it. You did use image.onload and wait for image to load?
Then your problem is likely clipping ...
context.clip is cumulative. If one clip (clip#1) is applied to the canvas followed by another clip (clip#2), then the resulting clipping area is the intersection of clip#1 and clip#2. The resulting clip is not clip#2.
So if you want to undo clip#1 so you can use the full clip#2, you must wrap the first clip in context.save and context.restore.
Here's a slightly different way of doing it using a temporary canvas
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/faces%20no%20background.png";
function start(){
cw=canvas.width=img.width;
ch=canvas.height=img.height;
ctx.drawImage(img,0,0);
// do the swap
clipCircle(img,63.5,56,54,203,177);
clipCircle(img,203,177,54,63.5,56);
}
function clipCircle(img,sourceCX,sourceCY,r,newCX,newCY){
var c=document.createElement('canvas');
var cctx=c.getContext('2d');
c.width=2*r;
c.height=2*r;
// define an clipping circle
cctx.beginPath();
cctx.arc(r,r,r,0,Math.PI*2);
// draw the source into the temp canvas
cctx.drawImage(img,-sourceCX+r,-sourceCY+r);
// draw the temp canvas onto the main canvas
ctx.drawImage(c,newCX-r,newCY-r);
}
body{ background-color: ivory; }
canvas{border:1px solid red; margin:0 auto; }
<h4>Swapped clipping on canvas<br>(top-left swapped with bottom-center)</h4>
<canvas id="canvas" width=300 height=300></canvas>
<h4>Original Image</h4>
<img src='https://dl.dropboxusercontent.com/u/139992952/multple/faces%20no%20background.png'>
image = new Image();
image.src = 'assets/img/image.png';
for (var i = 0; i < this.bombs.length; i++) {
var bomb = this.bombs[i];
ctx.drawImage(image, bomb.x - 2, bomb.y - 2, 15, 8);
}
This is an image in my canvas game and falling down from top. But I want, get images rotate random each state.
I tried this:
function drawRotated(degrees){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(canvas.width/2,canvas.height/2);
ctx.rotate(degrees*Math.PI/180);
ctx.drawImage(image,-image.width/2,-image.width/2);
ctx.restore();
}
Added this function in my image but its not working. How can I do it?
You're on the right track!
To rotate one (or more) images:
Translate to the center point where you want the image centered.
Rotate by the desired angle
Draw the image
Undo the transformations (undo translate & rotate).
A couple of changes:
You always translate to the middle of the canvas. If you want the bomb to drop you must increasingly translate down the canvas.
A typo: In drawImage you have used width for both the width & height argument.
An efficiency: context.save and context.restore are more expensive operations because they save & restore all of the canvas styles. Instead, it's more efficient to reset only your transformations (translate,rotate) with context.setTransform(1,0,0,1,0,0)
Here's your code refactored for these changes:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var bomb;
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/danger.png";
function start(){
var aa=img.width*img.width;
var bb=img.height*img.height;
var cc=Math.sqrt(aa+bb);
bomb={
x:150,y:-img.height/2,
degrees:0,image:img,
maxRotatedHalfHeight:cc/2
};
requestAnimationFrame(animate);
}
function drawRotated(b){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save();
ctx.translate(b.x,b.y);
ctx.rotate(b.degrees*Math.PI/180);
ctx.drawImage(b.image,-b.image.width/2,-b.image.height/2);
ctx.restore();
}
function animate(time){
drawRotated(bomb);
bomb.y+=1;
bomb.degrees+=1;
if(bomb.y<=canvas.height+bomb.maxRotatedHalfHeight){
requestAnimationFrame(animate);
}else{
alert('Animation is complete.');
}
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=175></canvas>
I have a bunch of HTML elements that I want to connect with lines via Canvas. Here's a mockup of what I'm trying to achieve:
Currently, I just have the lines, with no text. I want to place text halfway between each line, but seeing as they're diagonals I'm not sure how to do it.
Current code:
// 'connectors' is an array of points corresponding to
// the middle of each big blue buttons' x-value
ctx.clearRect(0,0,canvas.width,canvas.height);
for(var i=0;i<connectors.length;i++){
var wpoint = connectors[i];
var pos1 = {w: wpoint, h: 0};
var pos2 = {w: canvas.width / 2, h: canvas.height};
ctx.beginPath();
ctx.moveTo(pos1.w,pos1.h);
ctx.lineTo(pos2.w,pos2.h);
ctx.stroke();
// Write Text Halfway
ctx.fillStyle = "blue";
ctx.font = "bold 16px Arial";
ctx.fillText("2702", 100, canvas.height / 2);
// No idea what to put as the x value here
}
What's the best way to achieve this? Potentially drawing half the line, writing the text, then drawing the rest of the line?
EDIT: Perhaps a better title / question would be: How do I find the midpoint between two arbitrary points in HTML Canvas? I want to draw text there.
Here's how:
Calculate the line's midpoint
Draw the line
Erase the line at its midpoint
Tell canvas to horizontally & vertically center any drawn text around a specified [x,y]
Draw the text at the midpoint
Here's annotated code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var line={x0:20,y0:20,x1:150,y1:150};
textAtMidLine(line,'2702','verdana',14)
function textAtMidLine(line,text,fontface,fontsize){
// save the unmodified context state
ctx.save();
// calc line's midpoint
var midX=line.x0+(line.x1-line.x0)*0.50;
var midY=line.y0+(line.y1-line.y0)*0.50;
// calc width of text
ctx.font=fontsize+'px '+fontface;
var textwidth=ctx.measureText(text).width;
// draw the line
ctx.beginPath();
ctx.moveTo(line.x0,line.y0);
ctx.lineTo(line.x1,line.y1);
ctx.lineWidth=2;
ctx.strokeStyle='lightgray';
ctx.stroke();
// clear the line at the midpoint
ctx.globalCompositeOperation='destination-out'; // "erases"
ctx.fillRect(midX-textwidth/2,midY-fontsize/2,textwidth,fontsize*1.286);
ctx.globalCompositeOperation='source-over'; // reset to default
// tell canvas to horizontally & vertically center text around an [x,y]
ctx.textAlign='center';
ctx.textBaseline='middle'
// draw text at the midpoint
ctx.fillText(text,midX,midY);
// restore the unmodified context state
ctx.restore();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>
im using a canvas to visualize a small game of mine.
Basicly i have two objects that represent space ships, each of them has a "Location" array which holds the ships current x/y.
According to these arrays, i drawImage on the canvas (totalw/h is 300/300 fyi).
Now, for the difficult part.
i want to draw animations (gunfire) on that canvas. basicly from ship1 x/y to ship2 x/y.
For the animation function itself, im passing an effects object that holds 3 Arrays, shooter.location[x, y], target.location[x, y] and a third array that holds where the EFFECT is currently at [x, y].
this.animateEffects = function(effects){
var shooter = effects.shooter;
var target = effects.target;
var current = effects.current;
var canvas = document.getElementById("effects");
var context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.fillStyle = "red";
context.arc(current[0], current[1], 5, 0, 2*Math.PI);
effects.current[0]++
effects.current[1]++
context.fill();
if (current == target){
console.log("ding");
this.end()
}
}
My "problem" is that im, if possible at all, looking for a smart way to determine (for each frame) if effects[x, y] should go ++ or -- or a combination of the two, depending on where the "moving" ships are located at (at the time, the shooting started).
Any advise or hints are appreciated.
You can fire a bullet from shooter to target using linear interpolation.
Calculate the difference in the original X & Y positions of the shooter and target.
// save the starting position of the bullet (== shooter's original position)
// (these original X & Y are needed in the linear interpolation formula)
bulletOriginalX=shooter.x;
bulletOriginalY=shooter.y;
// calc the delta-X & delta-Y of the shooter & target positions
// (these deltas are needed in the linear interpolation formula)
dx=target.x-shooter.x;
dy=target.y-shooter.y;
Move the bullet towards the target using the interpolation formula
// where percent == the percent you want the bullet to be between
// it's starting & ending positions
// (between starting shooter & starting target positions)
currentBulletX=bulletOriginalX+dx*percent;
currentBulletY=bulletOriginalY+dy*percent;
Here's an example:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
shooter={x:50,y:50};
target={x:100,y:100};
effect={x:50,y:50,dx:0,dy:0,pct:0,speedPct:0.25};
draw();
fire();
$('#test').click(function(){
moveEffect();
draw();
});
function fire(){
effect.x=shooter.x;
effect.y=shooter.y;
effect.dx=target.x-shooter.x;
effect.dy=target.y-shooter.y;
effect.pct=0;
}
function moveEffect(){
effect.pct+=effect.speedPct;
}
function draw(){
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
ctx.arc(shooter.x,shooter.y,15,0,Math.PI*2);
ctx.closePath();
ctx.strokeStyle='green';
ctx.stroke();
ctx.beginPath();
ctx.arc(target.x,target.y,15,0,Math.PI*2);
ctx.closePath();
ctx.strokeStyle='red';
ctx.stroke();
if(effect.pct>1){return;}
var x=effect.x+effect.dx*effect.pct;
var y=effect.y+effect.dy*effect.pct;
ctx.beginPath();
ctx.arc(x,y,3,0,Math.PI*2);
ctx.closePath();
ctx.fillStyle='black';
ctx.fill();
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id=test>Animate 1 frame</button>
<br><canvas id="canvas" width=300 height=300></canvas>