I'm quite new to JS and have gone through courses online but, very frustratingly, I just seem to have such a hard time on my own so I'm sorry if this question has an obvious answer. Basically, this program bounces a colored ball around within a box. I want that color to change every time it hits a wall. I figured out a way to do so by putting all information under one function but the tutorial I'm using is saying (for tidy code purposes) that 2 functions will be better and so I really just want to understand how to do what I want to do when info is available in different functions since I know I will have to do that in the future. I will comment important code lines. Thank you so much to anyone who can help.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var y = canvas.height-30;
var dx = 4;
var dy = -4;
var ballRadius = 30;
function drawBall() { \\draws the ball
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "#ff0000";
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
x += dx;
y += dy;
if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) { \\says when to bounce
dx = -dx;
drawBall.ctx.fillStyle = "#ff0000"; \\this line and next line are lines I wrote
drawBall.ctx.fill(); \\that are obviously incorrect (same goes for
} \\ if statement below). What am I doing wrong?
if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) {
dy = -dy;
drawBall.ctx.fillStyle = "#0095DD";
drawBall.ctx.fill();
}
}
setInterval(draw, 10);
what you can do is pass parameters that will alter the behavior of the function.
in this case you will be passing the color you want.
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var y = canvas.height-30;
var dx = 4;
var dy = -4;
var ballRadius = 30;
function drawBall(color) { // draws the ball
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = color;
ctx.fill();
ctx.closePath();
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
x += dx;
y += dy;
if(x + dx > canvas.width-ballRadius || x + dx < ballRadius) { // says when to bounce
dx = -dx;
drawBall("#ff0000");
}
if(y + dy > canvas.height-ballRadius || y + dy < ballRadius) {
dy = -dy;
drawBall("#0095DD");
}
}
It seems that you mix some concepts of JavaScript. So for reasons of readability and design, I would create a 'class' for the ball. Something like this:
function Ball(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
}
You can create an instance of your ball with this:
var ball = new Ball(x, y, radius, color);
and access the properties in Java-style:
ball.color = "#0095DD";
You can also add some methods to your ball:
function Ball(x, y, radius, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.draw = function(ctx) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);
ctx.fillStyle = this.color;
ctx.fill();
ctx.closePath();
}
}
You can extend your code with this class and code. I think, you get it.
Related
So I'm working on a project where I have a canvas filled with moving balls. Its an extension/inspired by this codepen project : https://codepen.io/zetyler/pen/LergVR .
It essentially runs with the same physics in place as the codepen, but now I'm trying to draw the moving and colliding balls with images instead of random colors.
The original draw() method looks like this :
var pen = canvas.getContext('2d');
const W = canvas.width;
const H = canvas.height;
var numBalls = 30;
var grav = [0,-0.1];
function Ball(x,y,dx,dy,r) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.r = r;
this.color = 'hsl('+(Math.random()*360)+',90%,50%)';
this.draw = function() {
pen.fillStyle = this.color;
pen.beginPath();
pen.arc(this.x,this.y,this.r,0,2*Math.PI);
pen.fill();
}
I'm refactoring the draw method to try and work with an image instead of a random color fill, and so far I can't even get an image to show up. Currently my draw method looks like this:
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
let numBalls = 1;
let grav = [0,-0.1];
//try feeding the ball function an object
//and destructuring the inputs
class Ball {
constructor (x, y, dx, dy, r) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.r = r;
//probably won't need this
//this.color = 'hsl(' + (Math.random() * 360) + ', 90%, 50%)';
}
draw() {
var thumbImg = document.createElement('img');
thumbImg.src = './svgs/javascriptIcon.svg';
thumbImg.onload = function() {
context.save();
context.beginPath();
context.arc(25, 25, 25, 0, Math.PI * 2, true);
context.closePath();
context.clip();
context.drawImage(thumbImg, 0, 0, 50, 50);
context.beginPath();
context.arc(0, 0, 25, 0, Math.PI * 2, true);
context.clip();
context.closePath();
context.restore();
};
}
It's been so long since I've used the html canvas. I can't figure out what I'm doing wrong. I thought I would at least be able to get the image to show up, but no such luck.
Thanks for checking it out! Please let me know what you think.
I am not sure why you are using ctx.clip,
but if you just want to replace the coloured balls with images try this in your draw method
this.draw = function() {
// make sure the img is loaded
//pen.fillStyle = this.color;
pen.beginPath();
pen.arc(this.x,this.y,this.r,0,2*Math.PI);
pen.drawImage(img,this.x, this.y,this.r, this.r)
pen.fill();
}
after that play with IMG x and y positions for example pen.drawImage(img,this.x + somefactor, this.y - somefactor,this.r + somefactor, this.r + somefatcor)
just to make sure that img is perfectly cover the coloured ball so it behavies just like it
everybody!
I want to make a small tank that will follow the mouse and turn its cannon towards the mouse.
I made a deal following the mouse, but there is no way I can turn the gun towards the mouse.
My code:
// canvas variable
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// browser window size
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
//set canvas size
canvas.width = windowWidth;
canvas.height = windowHeight;
var mouseX = 0;
var mouseY = 0;
var baseAngle = 1;
class Cannon {
constructor(x,y, angle, size, speed){
this.x = x;
this.y = y;
this.angle = angle;
this.size = size;
this.speed = speed;
}
draw() {
// bullet
ctx.setTransform(1, 0, 0, 1, this.x, this.y - (this.size/2));
ctx.translate(this.x, this.y);
ctx.rotate(baseAngle);
ctx.beginPath();
ctx.rect(this.x, this.y - (this.size/2), this.size*2, this.size);
ctx.fillStyle = 'rgb(192,192,192)';
ctx.fill();
ctx.lineWidth = 3;
ctx.strokeStyle = 'rgba(128,128,128)';
ctx.stroke();
ctx.closePath();
//body
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
ctx.fillStyle = 'rgb(0,96,255)';
ctx.fill();
ctx.lineWidth = 3;
ctx.strokeStyle = 'rgba(128,128,128)';
ctx.stroke();
ctx.closePath();
}
update() {
// mooving
var dx = (mouseX - this.x)*.125;
var dy = (mouseY - this.y)*.125;
var dist = Math.sqrt(dx*dx + dy*dy);
if(dist > this.speed){
dx *= this.speed / dist;
dy *= this.speed / dist;
}
this.x += dx;
this.y += dy;
// rootating
baseAngle = Math.atan2(mouseY - this.y, mouseX - this.x);
}
}
const newCannon = new Cannon(80,60, 1, 20, 7);
onmousemove = function(e){
mouseX = e.clientX;
mouseY = e.clientY;
}
function gameUpdate() {
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
ctx.clearRect(0, 0, canvas.width, canvas.height);
newCannon.draw();
newCannon.update();
requestAnimationFrame(gameUpdate);
}
gameUpdate();
<!DOCTYPE html>
<html>
<head>
<title>CRUSH DEMO</title>
</head>
<body>
<canvas id="canvas"></canvas>
</body>
</html>
The cannon must be rotated in the direction of the mouse (the circle itself must not be rotated).
help me please fix the code!
I'm not proficient in js to say the very least.
I have a bouncy ball in a container in js. When the ball hits a certain part of its container, I want a box to light up briefly. I want to control the duration of the blink. I want the blink to happen once.
So far this works but the blink is incredibly brief:
I have a canvas object
<Canvas id="button1" width="100px" height="50px"></canvas>
And this code in place. Let's assume for simplicity's sake that the ball being in the right place sets ball_is_in_area to T for a very short duration.
var button1;
var button1ctx;
var ball_is_in_area = F;
button1 = document.getElementById("button1");
button1ctx = button1.getContext("2d");
button1ctx.fillStyle = "white";
button1ctx.fillRect(0, 0, canvas.width, canvas.height);
if( ball_is_in_area == T){
snd.play();
button1ctx.fillStyle = "red";
button1ctx.fillRect(0, 0, canvas.width, canvas.height);
}
"Don't use a canvas object use X" type answers are also appreciated!
Using requestAnimationFrame and based on this sample, you can use the timestamp parameter provided and, whenever you hit a wall, extend the timer for when the blink will end. Then use this timer to determine the color of the background.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var x = canvas.width / 2, y = canvas.height / 2;
var ballRadius = 10, dx = 2, dy = -2;
var blinkDuration = 60; // Duration in milliseconds
var endBlink = 0;
function drawBall() {
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI * 2);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
function draw(timeStamp) {
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
endBlink = timeStamp + blinkDuration; // Hit a wall
dx = -dx;
}
if (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
endBlink = timeStamp + blinkDuration;
dy = -dy;
}
// If endBlink > timeStamp, change the color
ctx.fillStyle = (endBlink > timeStamp ? "#338" : "#114");
ctx.fillRect(0, 0, canvas.width, canvas.height);
drawBall();
x += dx;
y += dy;
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
body { margin: 0 }
<canvas id="canvas" width="630" height="190"></canvas>
I want to create a collision region around a canvas element that enables me to interact with that element using mouse events width vanilla javascript.
To elaborate more on my problem here is the following:
at first I make an arc segment constructor with x, y, radius, beginAngle, endAngle, and a color arguments
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
/* arc class constructor */
function ArcSegment(x, y, radius, beginAngle, endAngle, segColor) {
this.x = x;
this.y = y;
this.radius = radius;
this.beginAngle = beginAngle;
this.endAngle = endAngle;
this.segColor = segColor;
this.update = function() {
this.draw();
}
this.draw = function(){
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, this.beginAngle, this.endAngle, false);
ctx.lineWidth = 20;
ctx.strokeStyle = this.segColor;
ctx.stroke();
}
}
Secondly, i add some value to create those arc segments
/* x, y, radius, startAngle, endAngle and color */
var centerX = canvas.width/2;
var centerY = canvas.height/2;
var radiuses = [
100,
120
];
var pi = Math.PI;
var segmentStart = [
pi/2,
0
];
var segmentRotation = [
1.4*pi,
0.2*pi
];
var segmentColors = [
"#133046",
"#15959F"
];
Then, i draw Them on the canvas.
var segment1 = new ArcSegment(centerX, centerY, radiuses[0], segmentStart[0], segmentStart[0]+segmentRotation[0], segmentColors[0]);
segment1.update();
var segment2 = new ArcSegment(centerX, centerY, radiuses[1], segmentStart[1], segmentStart[1]+segmentRotation[1], segmentColors[1]);
segment2.update();
and here is the result:
What i want now is a way to create a collision detection on top of each arc segment created, so when a mouse is clicked or moved on top of that specific arc segment
a sequence of events can occur (like a rotation animation for example or so...).
all the research i've done suggest to get the x and y value of a rectangle and calculate the distance of mouse position (mouse.x, mouse.y) and the length of the rectangle, but that method doesn't work with an arc segment with a lineWidth property.
Any help on the subject would be very appreciated.
Below is a pure mathematical approach, the key here is the code isPointInside
// Classes
function Arc(x, y, angle, arc, radius, colour, highlightColour) {
this.x = x;
this.y = y;
this.angle = angle;
this.arc = arc;
this.radius = radius;
this.colour = colour;
this.highlightColour = highlightColour;
this.highlighted = false;
this.lineWidth = 20;
}
Arc.prototype = {
isPointInside: function(x, y) {
var _x = x - this.x;
var _y = y - this.y;
var distance = Math.sqrt(_x * _x + _y * _y);
var invDistance = 1.0 / distance;
var angle = Math.acos(
_x * Math.cos(this.angle) * invDistance +
_y * Math.sin(this.angle) * invDistance
);
return distance > (this.radius - this.lineWidth/2) &&
distance < (this.radius + this.lineWidth/2) &&
angle < this.arc/2;
},
render: function(ctx) {
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.highlighted ? this.highlightColour : this.colour;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, this.angle - this.arc/2, this.angle + this.arc/2, false );
ctx.stroke();
}
};
// Variables
var canvas = null;
var ctx = null;
var arcs = [];
// Functions
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(0, 0, 999, 999);
for (var i = 0; i < arcs.length; ++i) {
arcs[i].render(ctx);
}
}
// Event Listeners
function onMouseMove(e) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
for (var i = 0; i < arcs.length; ++i) {
arcs[i].highlighted = arcs[i].isPointInside(x, y);
}
draw();
}
// Entry Point
onload = function() {
canvas = document.getElementById("canvas");
canvas.onmousemove = onMouseMove;
ctx = canvas.getContext("2d");
arcs.push(new Arc(190, 75, 0.2, 1.8, 60, "blue", "lime"));
arcs.push(new Arc(90, 75, 3.5, 4.2, 60, "red", "lime"));
draw();
}
<canvas id="canvas"></canvas>
I am trying to make a circle moving animation with mousemove event, each time the circle will moving from my mouse.x and mouse.ycoordinates on the screen. so I declare my mouse coordinates object and drawCricleobject constructor:
var mouse = {
x:canvas.width/2,
y:canvas.height/2
}
function Circle (x,y,r,dy){
this.x = x;
this.y = y;
this.r = r;
this.dy = dy;
this.update = function(){
ctx.beginPath();
ctx.arc(this.x,this.y,this.r,0,Math.PI*2);
ctx.fillStyle = 'blue';
ctx.fill();
this.y+=this.dy;
if(this.y<this.r || this.y+this.r>canvas.height){
this.dy=-this.dy;
}
}
}
and after i add the mousemoveevent so i am thinking i can assign the mouse x/y coordinate through my mouvemove eventListenter:
var myCircle = new Circle(mouse.x,mouse.y,30,2);
function animate(){
ctx.clearRect(0,0,canvas.width,canvas.height);
myCircle.update();
requestAnimationFrame(animate);
}
window.addEventListener("mousemove",function(e){
mouse.x = e.clientX;
mouse.y = e.clientY;
animate();
});
the problem is the mouse.xandmouse.yvalue won't change from the original canvas.width/2value, so i have tried to wrapper my animation()function inside the window.addEventListener instead just calling it within, just like:
window.addEventListener("mousemove",function(e){
mouse.x = e.clientX;
mouse.y = e.clientY;
var myCircle = new Circle(mouse.x,mouse.y,30,2);
function animate(){
ctx.clearRect(0,0,canvas.width,canvas.height);
myCircle.update();
requestAnimationFrame(animate);
}
animate();
});
this could work a bit but it looks really stupid and makes my browser comes huge laggy spikes, is there any other way to do this?
You would also need to pass the mouse coordinates when calling the update function ...
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var mouse = {
x: canvas.width / 2,
y: canvas.height / 2
};
function Circle(x, y, r, dy) {
this.r = r;
this.dy = dy;
this.update = function(x, y) {
ctx.beginPath();
ctx.arc(x, y, this.r, 0, Math.PI * 2);
ctx.fillStyle = 'blue';
ctx.fill();
this.y += this.dy;
if (this.y < this.r || this.y + this.r > canvas.height) {
this.dy = -this.dy;
}
};
}
var myCircle = new Circle(mouse.x, mouse.y, 30, 2);
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
myCircle.update(mouse.x, mouse.y);
requestAnimationFrame(animate);
}
canvas.addEventListener("mousemove", function(e) {
mouse.x = e.offsetX;
mouse.y = e.offsetY;
});
animate();
body{margin:10px 0 0 0;overflow:hidden}canvas{border:1px solid #e4e6e8}
<canvas id="canvas" width="635" height="208"></canvas>