Related
I'm doing the typical Pong game with just HTML canvas and JavaScript, but I've been stuck on this issue for a while now. I have checked similar Stackoverflow questions, but none of the answers worked for me.
Basically I have a window.onload function that renders the game (draw and move) every second. The ball moves, but draws a replica of itself with each movement. I think it's got something to do with the fact that I'm rendering both things each second, but I can't figure it out.
var framesPerSecond = 60;
setInterval(function() {
drawGame();
move();
}, 1000/framesPerSecond);
Here's the Fiddle.
Thanks.
Canvas needs to be cleared before redrawing:
canvasContext.clearRect(0, 0, canvas.width, canvas.height);
Otherwise, it would keep modifying existing canvas state
See in action - https://jsfiddle.net/ew2d1quL/2/
You have to redraw from beginning or you save another canvas/image somewhere and draw this first before you draw the rest of the game
var canvas;
var canvasContext;
var printScore = document.createElement("p");
var score = 0;
window.onload = function() {
canvas = document.getElementById("gameCanvas");
canvasContext = canvas.getContext('2d');
var framesPerSecond = 60;
setInterval(function() {
drawGame();
move();
}, 1000/framesPerSecond);
var leftPaddle = {
x: 0,
y: (canvas.height/2) - 50,
width: 10,
height: 100,
color: "yellow",
score: 0
}
var rightPaddle = {
x: canvas.width - 10,
y: (canvas.height/2) - 50,
width: 10,
height: 100,
color: "yellow",
score: 0
}
var ball = {
x: canvas.width/2,
y: canvas.height/2,
radius: 9,
color: "white",
speed: 5,
velocityX: 5,
velocityY: 5,
}
function drawGame() {
// redraw background
canvasContext.fillStyle = 'black';
canvasContext.fillRect(0, 0, canvasContext.canvas.width, canvasContext.canvas.height);
drawRect(leftPaddle.x, leftPaddle.y, leftPaddle.width, leftPaddle.height, leftPaddle.color);
drawRect(rightPaddle.x, rightPaddle.y, rightPaddle.width, rightPaddle.height, rightPaddle.color);
drawCircle(ball.x, ball.y, ball.radius, ball.color);
}
function drawRect(x, y, w, h, color) {
canvasContext.fillStyle = color;
canvasContext.fillRect(x, y, w, h);
}
function drawCircle(x, y, r, color) {
canvasContext.fillStyle = color;
canvasContext.beginPath();
canvasContext.arc(x, y, r, 0, Math.PI*2, false);
canvasContext.fill();
canvasContext.closePath();
}
function move() {
ball.x += ball.velocityX;
ball.y += ball.velocityY;
if ( (ball.y + ball.radius > canvas.height ) || (ball.y - ball.radius < 0) ) {
ball.velocityY =- ball.velocityY;
} else if ( (ball.x + ball.radius > canvas.width ) || (ball.x - ball.radius < 0)) {
ball.velocityX =- ball.velocityX;
}
drawGame();
}
}
canvas {
margin-top: 0px auto;
border: 2px solid black;
background-color: black;
border-radius: 10px;
display: block;
width: 50%;
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Pong</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<script src='main.js'></script>
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
</body>
</html>
I have a large array of objects to draw on a canvas.
The center of the objects needs to blend with standard alpha.
The border (stroke) of later objects needs to appear to remove any underlying borders while leaving the fill intact for blending.
An example as a code snippet with a couple of failed attempts - please note the 'desired' outcome was produced manually.
The solution needs to scale too as this is for a requestAnimationFrame and there will be thousands of objects to iterated over so performing individual beginPath()/stroke() combinations isn't likely to be viable.
var canvas = document.getElementById('canvas');
canvas.width = 600;
canvas.height = 600;
var ctx = canvas.getContext('2d');
//set up control objects
let objects = [{
x: 20,
y: 20,
w: 60,
h: 30,
rgba: "rgba(255, 0,0,.5)"
}, {
x: 40,
y: 30,
w: 60,
h: 30,
rgba: "rgba(0,255,0,.5)"
}]
//manually produce desired outcome
ctx.beginPath();
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y, myObject.w, myObject.h);
}
ctx.beginPath();
ctx.moveTo(40, 50);
ctx.lineTo(20, 50);
ctx.lineTo(20, 20);
ctx.lineTo(80, 20);
ctx.lineTo(80, 30);
ctx.rect(40, 30, 60, 30);
ctx.stroke();
ctx.font = "15px Arial"
ctx.fillStyle = "black";
ctx.fillText("Desired outcome - (done manually for example)", 120, 50);
//Attempt one: fill on iterate, stroke on end
ctx.beginPath();
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.rect(myObject.x, myObject.y + 70, myObject.w, myObject.h);
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y + 70, myObject.w, myObject.h);
}
ctx.stroke();
ctx.fillStyle = "black";
ctx.fillText("Attempt #1: inner corner of red box fully visible", 120, 120);
//Attempt two: fill and stroke on iterate
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x, myObject.y + 140, myObject.w, myObject.h);
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y + 140, myObject.w, myObject.h);
ctx.stroke();
}
ctx.fillStyle = "black";
ctx.fillText("Attempt #2: inner corner of red box partly visible", 120, 170);
ctx.fillText("(This also scales very badly into thousands of strokes)", 120, 190);
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
You can achieve this by drawing in two passes:
First you will composite your strokes, by iteratively
clearing each previous strokes where the filling would fall, using globalCompositeOperation = "destination-out"
draw this rect's stroke
When this is done, your canvas will only have the final strokes remaining.
Now you have to draw the fills, but since we want the strokes to be in front of the fills, we have to use an other compositing mode: "destination-over" and to iterate our rects in reversed order:
(async () => {
var canvas = document.getElementById('canvas');
canvas.width = 600;
canvas.height = 600;
var ctx = canvas.getContext('2d');
ctx.scale(2,2)
//set up control objects
let objects = [{
x: 20,
y: 20,
w: 60,
h: 30,
rgba: "rgba(255, 0,0,.5)"
}, {
x: 40,
y: 30,
w: 60,
h: 30,
rgba: "rgba(0,255,0,.5)"
},
{
x: 10,
y: 5,
w: 60,
h: 30,
rgba: "rgba(0,0,255,.5)"
}]
// first pass, composite the strokes
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x, myObject.y, myObject.w, myObject.h);
// erase the previous strokes where our fill will be
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#000"; // must be opaque
ctx.fill();
// draw our stroke
ctx.globalCompositeOperation = "source-over";
ctx.stroke();
}
await wait(1000);
// second pass, draw the colored fills
// we will draw from behind to keep the stroke at frontmost
// so we need to iterate our objects in reverse order
for (let i = objects.length- 1; i >= 0; i--) {
let myObject = objects[i];
// draw behind
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y, myObject.w, myObject.h);
}
})();
function wait(ms){
return new Promise(res => setTimeout(res, ms));
}
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
And of course, this can be used in an animation too:
var canvas = document.getElementById('canvas');
canvas.width = 100;
canvas.height = 100;
var ctx = canvas.getContext('2d');
//set up control objects
let objects = [{
x: 20,
y: 20,
w: 60,
h: 30,
rgba: "rgba(255, 0,0,.5)"
}, {
x: 40,
y: 30,
w: 60,
h: 30,
rgba: "rgba(0,255,0,.5)"
},
{
x: 10,
y: 5,
w: 60,
h: 30,
rgba: "rgba(0,0,255,.5)"
}]
objects.forEach( rect => {
rect.speedX = Math.random() * 2 - 1;
rect.speedY = Math.random() * 2 - 1;
});
requestAnimationFrame(anim);
onclick = anim
function anim() {
update();
draw();
requestAnimationFrame( anim );
}
function update() {
objects.forEach( rect => {
rect.x = rect.x + rect.speedX;
rect.y = rect.y + rect.speedY;
if(
rect.x + rect.w > canvas.width ||
rect.x < 0
) {
rect.speedX *= -1;
}
if(
rect.y + rect.h > canvas.height ||
rect.y < 0
) {
rect.speedY *= -1;
}
});
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// first pass, composite the strokes
for (let i = 0, l = objects.length; i < l; i++) {
let myObject = objects[i];
ctx.beginPath();
ctx.rect(myObject.x, myObject.y, myObject.w, myObject.h);
// erase the previous strokes where our fill will be
ctx.globalCompositeOperation = "destination-out";
ctx.fillStyle = "#000"; // must be opaque
ctx.fill();
// draw our stroke
ctx.globalCompositeOperation = "source-over";
ctx.stroke();
}
// second pass, draw the colored fills
// we will draw from behind to keep the stroke at frontmost
// so we need to iterate our objects in reverse order
for (let i = objects.length- 1; i >= 0; i--) {
let myObject = objects[i];
// draw behind
ctx.globalCompositeOperation = "destination-over";
ctx.fillStyle = myObject.rgba;
ctx.fillRect(myObject.x, myObject.y, myObject.w, myObject.h);
}
ctx.globalCompositeOperation = "source-over";
}
<canvas name="canvas" id="canvas" style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
How do I emulate Gravity with canvas Objects having only a few variables to work with.
I created the base canvas, game, time, score, everything, even gameStates, but I'm stuck on the part where you "add the velocity and the gravity factor into the Player Y variables".
I tried multiplying the gravity factor by a specified value, and then adding it to the yVel and then adding that to the actual Y value, but I cant translate the positioning correctly.
I think if I figured out how to "create gravity" creating jumping, moving right, and moving left wouldnt be too difficult.
Heres the main code im using to look for the platforms:
map.plates represents an array full of arrays which each contain 4 values for a plate (platform plate)
e is the map.plates.Arrays. playY is basically the player's exactly Y Height,, all rendered into fillRect();
function detectGravity() {
map.plates.forEach(e => {
if (playY > e[1] && playX <= e[0] && playX >= e[0] + e[2]) {
} else {
playY += 0; // Gravity Calculations here
}
});
}
I dont really know if I should include anything else here, but if you want the whole project, see the snippet below.
If theres anything wrong with the question, please tell me, I havent been on here in nearly half a year.
Full code incase codepen dies (suggested in comments):
"esversion: 6";
const can = document.querySelector(".block"),
ctx = can.getContext("2d"),
mScore = 100,
map = {
plates: [
[25, 25, 25, 2],
[75, 25, 25, 2],
[125, 25, 25, 2],
[175, 25, 25, 2],
[225, 25, 25, 2],
[25, 75, 25, 2],
[75, 62, 25, 2],
[125, 50, 25, 2],
[175, 38, 25, 2],
[25, 87, 25, 2],
[75, 100, 25, 2]
],
moneys: [
[25, 25],
[125, 25],
[225, 25],
[75, 62],
[75, 100]
],
player: [25, 25, 2, 2],
badSpt: []
};
let score = 0,
time = 60,
gameOn = 0;
let playX,
playY,
velX,
velY,
grav = 1.05;
can.addEventListener("click", startGame);
function startGame() {
if (gameOn != 1) {
gameOn = 1;
init();
gameTime = setInterval(() => {
if (time != 0) {
time -= 1;
}
}, 1000);
}
}
function init() {
can.width = 300;
can.height = 300;
drawEnviron();
drawLevel();
drawPlayer();
drawGame();
drawPixels();
if (time == 0) {
clearInterval(gameTime);
time = 60;
gameOn = 2;
}
window.requestAnimationFrame(init);
}
function drawEnviron() {
with (ctx) {
fillStyle = "#000000";
fillRect(0, 0, can.width, can.height);
fillStyle = "rgba(255, 255, 255, 0.5)";
fillRect(0, 0, can.width, can.height);
fillStyle = "#000000";
fillRect(0, 0, can.width, can.height / 15);
fillStyle = "#ffffff";
font = can.height / 15 + "px Verdana";
fillText("Score: " + score + "/" + mScore, 1, can.height / 19);
fillText("Time: " + time, can.width / 1.5 + 6, can.height / 19);
}
}
function drawLevel() {
map.plates.forEach(e => {
ctx.fillStyle = "#ffffff";
ctx.fillRect(e[0], can.height / 15 + e[1], e[2], e[3]);
});
map.moneys.forEach(e => {
ctx.beginPath();
ctx.fillStyle = "#fcba03";
ctx.arc(e[0] + 12.5, e[1] + 12.5, 4, 0, 2 * Math.PI);
ctx.fill();
});
}
function detectGravity() {
map.plates.forEach(e => {
if (playY > e[1] && playX <= e[0] && playX >= e[0] + e[2]) {
} else {
playY += 0;
}
});
}
function drawPlayer() {
const a = map.player;
if (gameOn == 0 || gameOn == 2) {
playX = a[0];
playY = a[1];
velX = 0;
velY = 0;
}
ctx.beginPath();
ctx.fillStyle = "#ff0000";
ctx.arc(playX + 12.5, playY + 12.5, 4, 0, 2 * Math.PI);
ctx.fill();
}
function drawGame() {
if (gameOn == 0) {
can.style.animation = "none";
with (ctx) {
fillStyle = "rgba(0, 0, 0, 0.5)";
fillRect(0, 0, can.width, can.height);
strokeStyle = "#000000";
lineWidth = 5;
fillStyle = "#ffffff";
textAlign = "center";
strokeText("Click to Start", 150, can.height / 4);
fillText("Click to Start", 150, can.height / 4);
}
} else if (gameOn == 2) {
can.style.animation = "0.2s flash infinite";
with (ctx) {
fillStyle = "rgba(0, 0, 0, 0.5)";
fillRect(0, 0, can.width, can.height);
strokeStyle = "#000000";
lineWidth = 5;
fillStyle = "#ff0000";
textAlign = "center";
strokeText("-- Game Over --", 150, can.height / 4);
fillText("-- Game Over --", 150, can.height / 4);
}
} else {
can.style.animation = "none";
}
}
function drawPixels() {
var fw = (can.width / 2) | 0,
fh = (can.height / 2) | 0;
ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.msImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = false;
ctx.drawImage(can, 0, 0, fw, fh);
ctx.drawImage(can, 0, 0, fw, fh, 0, 0, can.width, can.height);
}
init();
* {
box-sizing: border-box;
overflow: hidden;
}
.block {
border: 2px solid black;
}
#keyframes flash {
0%, 100% {
border: 2px solid black;
}
50% {
border: 2px solid red;
}
}
<canvas class="block"></canvas>
Simple step based gravity.
Gravity
Gravity manifests as a change in speed over time (acceleration). It has a direction and magnitude (a vector)
We define the gravity vector going down the canvas
const gravity = {x: 0, y: 1};
Normally we apply gravity over time units of seconds. This is not a handy unit for animation. In this case we can define it as pixels per frame. A frame is 1/60th of a second. Thus the gravity defined above having a magnitude of 1 pixel per tick squared. So in one second an object would be traveling at 60 pixels per tick or 3600 pixels per second.
This is a little too fast for most animations so we can slow it down somewhat
const gravity = {x: 0, y: 0.1};
The object
An object has a position (a coordinate) and a velocity (a vector) having a direction and magnitude.
const object = {
pos: {x: 0, y: 0}, // position
vel: {x, 0, y: 0}, // velocity
}
To simulate gravity on this object we can add a behavior in the form of a function. In this case we can call it update. In the update function we accelerate the object, by adding the gravity vector to the velocity vector (object.vel). Then we update the position by adding the velocity vector object.vel to the position coordinate object.pos
const gravity = {x: 0, y: 0.1};
const object = {
pos: {x: 0, y: 0}, // position
vel: {x, 0, y: 0}, // velocity
update() {
this.vel.x += gravity.x;
this.vel.y += gravity.y;
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
}
}
The world
By its self this object will fall forever so we need to have it interact with the world. We can define a ground line. In its most basic a line at a y position on the canvas.
const ground = ctx.canvas.height; // ground at bottom of canvas.
To interact we need to add to the objects update function. In this we check the object position against the ground. If the position is below the ground, we move the position up away from the ground the same distance it has moved into the ground, and reverse the velocity (bounced).
We can define the springyness of the ground as a fraction of the velocity.
We also need to give the object a size.
Thus we get.
const gravity = {x: 0, y: 0.1};
const ground = ctx.canvas.height; // ground at bottom of canvas.
const bounce = 0.5;
const object = {
pos: {x: 0, y: 0}, // position
vel: {x, 0, y: 0}, // velocity
size: {w: 10, h: 10},
update() {
this.vel.x += gravity.x;
this.vel.y += gravity.y;
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
const g = ground - this.size.h; // adjust for size
if(this.pos.y >= g) {
this.pos.y = g - (this.pos.y - g); //
this.vel.y = -Math.abs(this.vel.y) * bounce; // change velocity to moving away.
}
}
}
Then all that is needed is to call update every frame and draw the object at the correct position.
Demo
Putting it into practice.
A simple box called object falls from the top of the canvas and hits the ground (bottom of the canvas) bounces a bit and stop. (Click to reset)
Update: I forgot to check if the object is at rest.
The math will have the box vibrate and never really stop moving if we don't add a little extra code to update.
The box will now appear to come to a complete stop when its bounce is less than gravity. See comment // check for rest.
const ctx = canvas.getContext("2d");
canvas.width = innerWidth-4;
canvas.height = innerHeight-4;
requestAnimationFrame(mainLoop); // starts the animation
const gravity = {x: 0, y: 0.1};
const ground = ctx.canvas.height; // ground at bottom of canvas.
const bounce = 0.9; // very bouncy
const object = {
pos: {x: ctx.canvas.width / 2, y: 0}, // position halfway on canvas
vel: {x: 0, y: 0}, // velocity
size: {w: 10, h: 10},
update() {
this.vel.x += gravity.x;
this.vel.y += gravity.y;
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
const g = ground - this.size.h; // adjust for size
if(this.pos.y >= g) {
this.pos.y = g - (this.pos.y - g); //
this.vel.y = -Math.abs(this.vel.y) * bounce;
if (this.vel.y >= -gravity.y) { // check for rest.
this.vel.y = 0;
this.pos.y = g - gravity.y;
}
}
},
draw() { ctx.fillRect(this.pos.x, this.pos.y, this.size.w, this.size.h) },
reset() { this.pos.y = this.vel.y = this.vel.x = 0 },
}
function mainLoop() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
object.update(); // move object
object.draw();
requestAnimationFrame(mainLoop);
}
canvas.addEventListener("click", object.reset.bind(object));
body {
margin: 0px;
padding: 0px;
}
canvas {
position: absolute;
top: 0px;
left: 0px;
border: 1px solid black;
}
<canvas id="canvas"></canvas>
I wrote the following code on my text editor:
!DOCTYPE html><html><head><script>
window.onload = function()
{
canvas = document.getElementById("canvasArea");
context = canvas.getContext("2d");
var smallIMage = new Image();
smallImage.src = "https://vignette.wikia.nocookie.net/cardfight/images/8/89/De.jpg/revision/latest?cb=20130414214050";
smallImage.onload = function()
{
context.shadowOffsetX = 4;
context.shadowOffsetY = 4;
context.shadowBlur = 20;
context.shadowColor = "lavender";
context.strokestyle = "gray";
context.lineWidth = 1;
var repeatPattern = context.createPattern(smallImage, "repeat");
var noRepeatPattern = context.createPattern(smallImage, "no-repeat");
var repeatXPattern = context.createPattern(smallImage, "repeat-x");
var repeatYPattern = context.createPattern(smallImage, "repeat-y");
context.fillStyle = repeatPattern;
context.fillRect (125, 125, 325, 325);
context.strokeRect (125, 125, 325, 325);
context.fillStyle = noRepeatPattern;
context.fillRect (0, 0, 100, 100);
context.strokeRect (0, 0, 100, 100);
context.fillStyle = repeatXPattern;
context.fillRect (125, 0, 350, 100);
context.strokeRect (125, 0, 350, 100);
context.fillStyle = repeatYPattern;
context.fillRect (0, 125, 100, 350);
context.strokeRect (0, 125, 100, 350);
}
}
</script></head><body>
<div style = "width:500px; height:500px;
margin:0 auto; padding:5px;">
<canvas id = "canvasArea"
width = "500" height = "500"
style = "border:2px solid black">
Your browser doesn't currently support HTML5 canvas.
</canvas>
</div>
</body>
</html>
This code s supposed to create a pattern from an online image, but it's not showing as the canvas is completely blank. Can you please tell me what I did wrong.
There wasn't much wrong with your code apart from correcting a few spelling inconsistencies and defining the new Image() event handler before assigning it's .src property. Also, remember to declare the charset of the document in the head section of the file. E.G...
<head>
<meta charset="utf-8">
... etc ...
</head>
Anyway, after a little editing the major components of your page look like this.
document.addEventListener('DOMContentLoaded', function() {
var canvas = document.getElementById("canvasArea"),
ctx = canvas.getContext("2d"),
smallImage = new Image();
/* set canvas dimensions */
canvas.width = canvas.height = 500;
smallImage.addEventListener("load", function() {
/* the image is bound to the
function's 'this' property */
var repeatPattern = ctx.createPattern(this, "repeat"),
noRepeatPattern = ctx.createPattern(this, "no-repeat"),
repeatXPattern = ctx.createPattern(this, "repeat-x"),
repeatYPattern = ctx.createPattern(this, "repeat-y");
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
ctx.shadowBlur = 20;
ctx.shadowColor = "rgb(230,230,250)";
ctx.strokestyle = "rgb(128,128,128)";
ctx.lineWidth = 1;
ctx.fillStyle = repeatPattern;
ctx.fillRect (125, 125, 325, 325);
ctx.strokeRect (125, 125, 325, 325);
ctx.fillStyle = noRepeatPattern;
ctx.fillRect (0, 0, 100, 100);
ctx.strokeRect (0, 0, 100, 100);
ctx.fillStyle = repeatXPattern;
ctx.fillRect (125, 0, 350, 100);
ctx.strokeRect (125, 0, 350, 100);
ctx.fillStyle = repeatYPattern;
ctx.fillRect (0, 125, 100, 350);
ctx.strokeRect (0, 125, 100, 350);
}, !1);
/* define Image.onload event handler
before assigning Image.src */
smallImage.src = "https://vignette.wikia.nocookie.net/cardfight/images/8/89/De.jpg/revision/latest?cb=20130414214050";
}, !1);
body {
background:rgb(90,90,110);
padding:33px 0 }
#box {
width:500px;
height:500px;
background:rgb(50,50,70);
margin:0 auto;
box-shadow:0 0 0.25em 0.5em rgba(0,0,0,0.25) }
#canvasArea {
width:100%;
height:100%;
border:2px solid rgb(22,22,22) }
<div id="box">
<canvas id="canvasArea">
Your browser doesn't currently support HTML5 canvas.
</canvas>
</div>
BP ;)
I am trying to solve a question which has been bugging me for a while, can someone explain to me how to make the *red rectangle face/aim towards my mouse and explain to me how it works it would be awesome!!!
Here is the Fiddle
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var player = {
x: 200,
y: 200,
}
drawPlayer = function(something) {
context.beginPath();
context.fillStyle = "blue";
context.arc(something.x, something.y, 30, 0, 2 * Math.PI);
context.fill();
context.fillStyle = "red";
context.fillRect(something.x, something.y - 10, 50, 20);
context.restore();
}
update = function() {
context.clearRect(0, 0, 500, 500)
drawPlayer(player);
}
setInterval(update, 20);
<canvas id="canvas" style="outline: 1px solid #ccc" width="500" height="500"></canvas>
Use context.translate to translate coordinates to the center of your player and then context.rotate to rotate the rectangle.
To find the angle between the mouse position and the center of the player you can use Math.atan2 function.
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var player = {
x: 200,
y: 200,
}
drawPlayer = function(something, angle) {
context.clearRect(0, 0, 500, 500);
context.beginPath();
context.fillStyle = "blue";
context.arc(something.x, something.y, 30, 0, 2 * Math.PI);
context.fill();
// save the untranslated context
context.save();
context.beginPath();
// move the rotation point to the center of the player
context.translate(something.x, something.y);
context.rotate(angle);
context.fillStyle = "red";
// note that coordinates are translated,
// so 0 is player.x and -10 is player.y - 10
context.fillRect(0, - 10, 50, 20);
// restore the context to its untranslated state
context.restore();
}
drawPlayer(player, 0);
document.onmousemove = function(e) {
var angle = Math.atan2(e.pageY - player.y, e.pageX - player.x)
drawPlayer(player, angle);
}
<canvas id="canvas" style="outline: 1px solid #ccc" width="500" height="500"></canvas>