Rotate Moving Object on Canvas - javascript

I'm trying to draw an object based on it's rotate property, but it seems to get confused when the object is moving.
ctx.save();
ctx.translate(this.width * 0.5, this.height*0.5);
ctx.rotate(DegToRad(45));
ctx.translate(-this.width*0.5,-this.height*0.5);
ctx.drawImage(this.img, this.spriteOffset, 0, this.width, this.height, this.x, this.y,this.width,this.height);
ctx.restore();
The image is drawn rotated 45 degrees, however it now moves in a down-left direction, when the object should only be moving downwards. The movement code is simply handled by incrementing the this.y position. Is there a simpler way to accomplish this?

This is because the whole canvas is being rotated.
What you could try is looking into a framework like Kinetic JS, which creates a sort of SVG API for the canvas.
This site has tons of information on how to use it as well, including rotation, transition, transformation, and pretty much anything else that you might need in working with it.
This should suit your needs rather well.
I think I should provide a low-level, framework-free option as well. Basically, using raw javascript and HTML to pull this off.
Now, as I understood your question, you are trying to make an object (let's assume a black square) move downwards AND have it spin. The only way I can think of to spin it in the canvas without going into path hell is to rotate the entire rendering context. BUT you can also import an image into canvas, for instance, a transparent black diamond (i.e. that same square rotated). So you'd use a separate canvas to render each step of the rotation for the square.
Essentially something like this:
var canvas2 = document.createElement('canvas'), ctx2 = canvas2.getContext('2d');
//do something with the second canvas
//let's assume the second canvas is the same size as the square
ctx.drawImage(canvas2, squareX, squareY);
See my attempt
As you can see, it is a bit wonky, but it does do essentially what the question asks; it moves the square down, and rotates it. It also plots the result of that rotation below the actual canvas so you can see what's happening under the hood, but the square cuts out due to the "center" being on the top left of the square, and not in the middle.
In the end, it really comes down to how you want to do it.

I was playing around with the API and found it was easier to just keep track of where the object should be. here's an example of a square moving diagonally across the screen and rotating.
<canvas id="canvas" style="width: 100%; height: 100%;">
</canvas>
<script>
var DELAY = 15; // ms
var RECT_WIDTH = 100; // px
var RECT_HEIGHT = 100; // px
var canvas = document.getElementById('canvas');
// set intrinsic dimensions
canvas.width = 1000;
canvas.height = 1000;
var ctx = canvas.getContext('2d')
ctx.fillStyle = 'teal';
var step = 0
var vx = 2
var animate = setInterval(function () {
ctx.resetTransform()
ctx.clearRect(0, 0, 1000, 1000);
ctx.translate(vx * step, vx * step);
// rotation in place, translate to center of square and back
ctx.translate(RECT_WIDTH / 2, RECT_HEIGHT / 2);
ctx.rotate((Math.PI / 180) * step);
ctx.translate(-(RECT_WIDTH / 2), -(RECT_HEIGHT / 2));
// Draw the rectangle
ctx.fillRect(0, 0, RECT_WIDTH, RECT_HEIGHT);
step = step + 1
}, DELAY)
setTimeout(function () {
clearInterval(animate);
}, 5000);
</script>
Here, I use vx and keep track of the steps to translate, and calculate what the rotation will be in radians based on the steps a new not caring what the previous state was. Make sure you rotate across the center of where you're square will be as well.

Use ctx.translate to set the object's position before applying the rotation. This should fix the problem.
ctx.save();
ctx.translate(this.x, this.y);
ctx.translate(this.width * 0.5, this.height*0.5);
ctx.rotate(DegToRad(45));
ctx.translate(-this.width*0.5,-this.height*0.5);
ctx.drawImage(this.img, this.spriteOffset, 0, this.width, this.height, 0, 0,this.width,this.height);
ctx.restore();

Related

How to clear an element of canvas that is moving (leaves a streak) without clearing canvas

Say I have drawn a circle on a canvas that has something else drawn on it that stops me from clearing the canvas - due to the other element being randomly generated
var circleX = 50;
var circleY = 10;
var moveCircX = 2;
var moveCircY = 3;
function createCirc(){
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(circleX, circleY, 10, 0, Math.PI*2, true);
ctx.fill();
}
function circMove(){
circleY = (circleY + circMoveY)
//then validation to stop it from being drawn of the canvas
So what I'm trying to do is move the circle but clear the previous drawn circle from the canvas. So is there a solution to clearing the circle or would it be easier to create a sprite that replicates the circle?
Since your background isn't changing, the simplest strategy is to copy the background before you first draw your circle, then draw your circle. When you're moving, redraw that part of the background from the copy you kept, then draw your circle in the new place.
An efficient way to do that is to use getImageData and putImageData.
So, (my javascript is rusty, so this may not be perfect. Feel free to correct any mistakes), before the first time you createCirc, simply do:
imageData = ctx.getImageData(0,0, ctx.canvas.width, ctx.canvas.height)
And, in your circMove function, before you move and redraw the circle, you want:
ctx.putImageData(imageData, circleX, circleY, circleX, circleY, 2*circle_radius, 2*circle_radius)
(You don't define circle_radius, but I'm sure you must have a similar value. I'm using 2x the radius to presumably be the size of the image that is drawn.)

HTML 5 Canvas - Rotate, Scale, Translate and drawImage

I'm trying to place an html5 image inside canvas. The image is translated, rotated and scaled using the css transform property. The main problem is how to copy the properties of the image and apply them on the canvas.
You can try the jsfiddle link below: the image can be scaled, moved and rotated and whatever appears in the greenbox should be the same as the one in the red.
https://jsfiddle.net/q7y1py5f/3/. Here I can move and scale the image, but when the rotation kicks in nothing works. Here is the code that bugs me (58 line in the javascript jsfiddle) :
context.save();
context.translate(image.width / 2, image.height / 2);
context.rotate(angle * Math.PI/180);
context.translate(((-image.width) / 2), ((-image.height) / 2));
context.translate(-x, -y);
context.scale(scale, scale);
context.drawImage(image, 0, 0);
context.restore();
Here the x and y are the difference between the top left of the green container and the top left of the image. Any help would be appreciated.
I've managed to get each property to work individually, but when combining them all together it doesn't work. When I only translate and scale the image and apply to the canvas context everything is fine, but when I add the rotation with the image is not where it's supposed to be.
I tried the following - calculated the top-left of the element with the angle to get the right value and then translating and rotating with it.
In function canvasCropping you write
var left = cropper.offset().left - img.offset().left,
top = cropper.offset().top - img.offset().top,
but rotating and scaling will change element offset, so you got wrong coordinats for translation. Change it to:
var left = -(cropper.offset().left - transform.translate.x-initialOffset.left),
top = -(cropper.offset().top -transform.translate.y-initialOffset.top),
initialOffset - initial offset of your image.
And some changes in drawRotatedImage
`
var drawRotatedImage = function (context, image, x, y, angle, width, height, scale) {
context.save();
// Move registration point to the center of the canvas
context.translate(x+image.width/2,y+image.height/2);
context.rotate(angle* Math.PI / 180);// angle must be in radians
context.scale(scale, scale);
// Move registration point back to the top left corner of canvas
context.translate((-image.width)/2, (-image.height)/2);
context.drawImage(image, 0, 0);
context.restore();
};
`

JavaScript: Is it possible to cut a shape out of a rectangle to make a transparent hole in it?

I am trying to make a 2d top-down game in Javascript at the moment. I've currently got a day/night system working where a black rectangle gradually becomes more opaque (as the day goes on) before it finally is fully opaque, simulating the peak of the night where the player can not see.
I want to implement an artificial light system, where the player could use a torch that will illuminate a small area in-front of them. However, my problem is that I don't seem to be able to find a way to 'cut out' a shape from my opaque rectangle. By cutting out a shape, it would look like the player has a torch.
Please find an example mock-up image I made below to show what I mean.
http://i.imgur.com/VqnTwoR.png
Obviously the shape shouldn't be as roughly drawn as that :)
Thanks for your time,
Cam
EDIT: The code used to draw the rectangle is as follows:
context.fillStyle = "#000033";
context.globalAlpha = checkLight(gameData.worldData.time);
context.fillRect(0, 0, 512, 480);
//This is where you have to add the cut out triangles for light!
context.stroke();
Instead of drawing a rectangle over the scene to darken it when the "light" is on, instead draw an image with the "lit" area completely transparent and the rest of the "dark" area more opaque.
One way is to use declare a triangular clipping area and draw your revealed scene. The scene would display only inside the defined clipping area.
Example code and a Demo:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var offset = 50;
var img = new Image();
img.onload = function() {
knockoutAndRefill(50,100,300,50,75,350);
};
img.src = 'http://guideimg.alibaba.com/images/trip/1/03/18/7/landscape-arch_68367.jpg';
function knockoutAndRefill(x0,y0,x1,y1,x2,y2){
context.save();
context.fillStyle='black';
context.fillRect(0,0,canvas.width,canvas.height);
context.beginPath();
context.moveTo(x0,y0);
context.lineTo(x1,y1);
context.lineTo(x2,y2);
context.closePath();
context.clip();
context.drawImage(img,0,0);
context.restore();
}
<canvas id=myCanvas width=500 height=400>

Html 5 canvas and background

after trying a few ways to preserve a background under a canvas drawing a moving rectangle (animation code not reproduced), here is my best try :
canvas {background-image:url('background.png');}
var x,y, pixels;
function draw() {
if(pixels) {
context.putImageData(pixels,x,y);
}
x = //calculate new X
y = //calculate new Y
pixels = context.getImageData(x, y, 10, 10);
context.fillStyle = 'red';
context.fillRect(x, y, 10, 10);
}
My first question is : why won't replacing the first two lines of draw() with :
context.fillStyle = 'rgba(10,10,10,0)';
context.fillRect(x,y,10,10);
work to clear the previously drawn pixels?
And my second question is : is there really no better way than get and putImageData(), which are very labor-intensive?
EDIT: in particular is there a div containing the background image trick that would maybe work without the get and putImageData calls?
Thanks!
By default, whatever you draw gets drawn on top of each other, instead of being replaced.
Use context.clearRect(x,y,10,10); instead.
There is also the context.globalCompositeOperation property, which could be set to "destination-out" before using fillRect .. but you're better off with clearRect.

Canvas roulette wheel - using background images as each slice

I have a roulette wheel created with HTML5 canvas and currently each slice is a plain color generated by using the fillStyle(), beginPath(), stroke(), then fill() methods.
I would like to use images that that crop appropriately in the shape of the slice, but I'm not sure how to implement this using drawImage().
Here's the jsfiddle
http://jsfiddle.net/pyD2q/2/
Any help or resources is appreciated.
Instead of filling the color in you can use it as a clipping mask instead. For each arc, set clip, draw the image and repeat.
Something like this (untested):
for (i = 0; i < s.members.length; i++) {
angle = s.startAngle + i * s.arc;
ctx.beginPath();
ctx.arc(s.width / 2, s.height / 2, s.outsideRadius, angle, angle + s.arc, false);
ctx.arc(s.width / 2, s.height / 2, s.insideRadius, angle + s.arc, angle, true);
ctx.save(); /// store current clip-state
ctx.clip(); /// set current arc as clipping mask
ctx.drawImage(someImageArray[i], x, y, width, height);
ctx.restore(); /// restore clip-state
ctx.stroke(); /// stroke arc
}
You will of course also need to place the images correctly relative to the section and if you want to rotate the image you would need to rotate it at this point.
In that regard I would recommend creating this wheel on an off-screen canvas. This way you only need to draw the off-screen canvas into the main canvas rotated and what have you (saves you the trouble of calculating each angle as well and the performance will be better).

Categories

Resources