So i am doing a flappy bird clone as coding exercise... and now i got to the part where i have to detect the collision bettwen the bird and the pipes.
I would to know how would you suggest be the best way of detecting the collision of the bird with the pipes, taking into account that i am pushing the pipes into an array
this are the main parts of the code
//Pipes proto that the bird most avoid
function Pipes(x1, y1, height1, width1, dx1, dy1, x2, y2, height2,
width2, dx2, dy2) {
this.x1 = x1;
this.y1 = y1;
this.height1 = height1;
this.width1 = width1;
this.dx1 = dx1;
this.dy1 = dy1;
this.x2 = x2;
this.y2 = y2;
this.height2 = height2;
this.width2 = width2;
this.dx2 = dx2;
this.dy2 = dy2;
this.draw = function () {
c.fillStyle = "green";
c.fillRect(x1, y1, height1, width1)
c.fillStyle = "green";
c.fillRect(x2, y2, height2, width2)
}
this.update = function () {
x1 += -dx1;
x2 += -dx2;
this.draw();
}
}
function Bird(x, y, dx, dy, radius, color, flap) {
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
this.radius = radius;
this.color = color;
this.draw = function () {
c.beginPath();
c.arc(x, y, radius, 0, 2 * Math.PI, false);
c.fillStyle = color;
c.fill();
};
this.update = function (){
flap = false;
x += dx
//gravity manager
var gravity = 0.4;
y += dy
dy += gravity;
//Screen collision manager
if (y + dy > innerHeight) {
y = innerHeight - radius;
}
if(radius < crashObj.x1 || radius < crashObj.x2 || radius <
crashObj.x1){
}
this.draw()
}
};
//Main GameLoop
function animate() {
c.clearRect(0, 0, innerWidth, innerHeight);
//canvas Color
c.fillStyle = "#C5D3E2";
c.fillRect(0, 0, window.innerWidth, window.innerHeight);
//Updates pipes
for (var i = 0; i < pipesArr.length; i++) {
pipesArr[i].update();
}
//draw and update bird into the screen
bird.update();
requestAnimationFrame(animate);
}
animate();
thanks in advance fo the answer!
What you are looking for is a circle-rectangle collision.
User markE has posted an in-depth answer on how to achieve it, along with a JS Fiddle demonstrating it.
Then on the update function, run through all the Pipes and check each of them for collision. For example:
for(var i = 0; i < pipesArr.length; i++){
...
if(RectCircleColliding(bird, pipe)){
die()
}
}
I was working on the paddle part of my code and was fixing other errors and after it was fixed, it gave me this error "p5practice.js:97 Uncaught TypeError: Cannot read property 'y' of undefined" Also, if that error can be fixed, how do I properly get my paddle to work? I've looked at several other pong like examples and have had no luck with mine working.
//variables
var canvas;
var ctx;
var w;
var h;
var ball;
var paddle;
var score1;
//ball object
var BALL = function(x, y) {
this.x = x;
this.y = y;
this.color = "white";
this.radius = 12;
this.vx = 3;
this.vy = -3;
};
//paddle 1
var PADDLE = function(x, y) {
this.x = 10;
this.y = h/2 - 50;
this.color = "white";
this.width = 5;
this.height = 100;
};
window.addEventListener("keydown", movePaddle);
//keydown event
//down
function movePaddle(e) {
switch (event.keyCode){
case 38: //up
console.log(paddle.y)
if (paddle.y - 30 >= -10) {
paddle.y -= 30;
}
break;
case 40: // down
console.log(paddle.y)
if (paddle.y + 30 < 305) {
paddle.y += 30;
}
break;
}
}
//DOM load
window.onload = function() {
canvas = document.getElementById('canvas');
w = canvas.width;
h = canvas.height;
ctx = canvas.getContext('2d');
ball = new BALL (w/2, h/2);
paddle = new PADDLE(w-100, h/2);
drawScore();
startGame();
movePaddle();
};
function startGame() {
requestAnimationFrame(startGame);
ctx.clearRect(0, 0, w, h);
ball.x += ball.vx;
ball.y += ball.vy;
ctx.fillStyle = ball.color;
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, 2*Math.PI, true);
ctx.closePath();
ctx.save();
ctx.fill();
ctx.restore();
ctx.fillStyle = paddle.color;
ctx.beginPath();
ctx.save();
ctx.shadowBlur = 20;
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
ctx.shadowColor = "red";
ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
ctx.closePath();
ctx.restore();
}
//collision with walls
//https://www.khanacademy.org/computer-programming/paddle-ball/830543654
//bottom
if(ball.y >= 390) {
ball.vy = -ball.vy;
console.log("bottom hit");
}
//top
if(ball.y <= 10) {
ball.vy = -ball.vy;
console.log("top hit");
}
//right
if (ball.x >= 590){
ball.vx = -ball.vx;
console.log("right hit");
}
//left
if(ball.x <= 10){
score++;
ball.x = w/2;
ball.y = h/2;
console.log("left hit");
}
//collision with paddle
if(ball.x<15){
if(ball.y>paddle.y && ball.y<paddle.y + paddle.height){
ball.vy = -ball.vy;
console.log("paddle");
}
}
//reset
//https://bl.ocks.org/ruffrey/1e242222aebbcd102a53
//not finished
function reset(){
ball.x;
ball.y;
}
//score
var score = 0;
function drawScore(){
ctx.font ="16px Arial";
ctx.fillStyle ="#0095DD";
ctx.fillText("Score: "+score,w/2, 20);
}
var leftScore = this.x > 10;
if (leftScore){
playerCount.score++;
}
this.reset();
canvas {
border: 3px solid yellow;
background-color: black;
}
<!doctype html>
<html>
<head>
<title>Pong</title>
<script src="p5practice.js"></script>
<link rel= 'stylesheet' href="p5practice.css">
</head>
<body>
<canvas id="canvas" width="600" height="400"></canvas>
</script>
</body>
</html>
You need to move your if statements at the bottom into the onload or startGame function. They are running before the variable ball is getting initialized. So you are getting the error that is saying ball is undefined
You will also need to move this or it will complain about x:
var leftScore = this.x > 10;
//variables
var canvas;
var ctx;
var w;
var h;
var ball;
var paddle;
var score1;
//ball object
var BALL = function(x, y) {
this.x = x;
this.y = y;
this.color = "white";
this.radius = 12;
this.vx = 3;
this.vy = -3;
};
//paddle 1
var PADDLE = function(x, y) {
this.x = 10;
this.y = h / 2 - 50;
this.color = "white";
this.width = 5;
this.height = 100;
};
window.addEventListener("keydown", movePaddle);
//keydown event
//down
function movePaddle(e) {
switch (event.keyCode) {
case 38: //up
console.log(paddle.y)
if (paddle.y - 30 >= -10) {
paddle.y -= 30;
}
break;
case 40: // down
console.log(paddle.y)
if (paddle.y + 30 < 305) {
paddle.y += 30;
}
break;
}
}
//DOM load
window.onload = function() {
canvas = document.getElementById('canvas');
w = canvas.width;
h = canvas.height;
ctx = canvas.getContext('2d');
ball = new BALL(w / 2, h / 2);
paddle = new PADDLE(w - 100, h / 2);
drawScore();
startGame();
movePaddle();
//bottom
if (ball.y >= 390) {
ball.vy = -ball.vy;
console.log("bottom hit");
}
//top
if (ball.y <= 10) {
ball.vy = -ball.vy;
console.log("top hit");
}
//right
if (ball.x >= 590) {
ball.vx = -ball.vx;
console.log("right hit");
}
//left
if (ball.x <= 10) {
score++;
ball.x = w / 2;
ball.y = h / 2;
console.log("left hit");
}
//collision with paddle
if (ball.x < 15) {
if (ball.y > paddle.y && ball.y < paddle.y + paddle.height) {
ball.vy = -ball.vy;
console.log("paddle");
}
}
};
function startGame() {
requestAnimationFrame(startGame);
ctx.clearRect(0, 0, w, h);
ball.x += ball.vx;
ball.y += ball.vy;
ctx.fillStyle = ball.color;
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, 2 * Math.PI, true);
ctx.closePath();
ctx.save();
ctx.fill();
ctx.restore();
ctx.fillStyle = paddle.color;
ctx.beginPath();
ctx.save();
ctx.shadowBlur = 20;
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
ctx.shadowColor = "red";
ctx.fillRect(paddle.x, paddle.y, paddle.width, paddle.height);
ctx.closePath();
ctx.restore();
}
//collision with walls
//https://www.khanacademy.org/computer-programming/paddle-ball/830543654
//reset
//https://bl.ocks.org/ruffrey/1e242222aebbcd102a53
//not finished
function reset() {
ball.x;
ball.y;
}
//score
var score = 0;
function drawScore() {
ctx.font = "16px Arial";
ctx.fillStyle = "#0095DD";
ctx.fillText("Score: " + score, w / 2, 20);
var leftScore = this.x > 10;
if (leftScore) {
playerCount.score++;
}
this.reset();
}
canvas {
border: 3px solid yellow;
background-color: black;
}
<!doctype html>
<html>
<head>
<title>Pong</title>
<script src="p5practice.js"></script>
<link rel='stylesheet' href="p5practice.css">
</head>
<body>
<canvas id="canvas" width="600" height="400"></canvas>
</script>
</body>
</html>
I'm still new to javascript.
I'm working on an javascript tutorial from here https://developer.mozilla.org/en-US/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript/Bounce_off_the_walls
One of the more challenging exercise is to make the ball change color when it bounce off from a wall. So far i manage to do this:
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var x = canvas.width/2;
var y = canvas.height-30;
var dx = 2;
var dy = -2;
var ballRadius = 10;
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function ballColor(){
var newColor = getRandomColor();
return newColor;
}
function drawBall(){
ctx.beginPath();
ctx.arc(x, y, ballRadius, 0, Math.PI*2);
ctx.fillStyle=ballColor();
ctx.fill();
ctx.closePath();}
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
drawBall();
x += dx;
y += dy;
if(y+dy>canvas.height-ballRadius||y+dy<ballRadius){
dy = -dy;
}
if(x+dx>canvas.width-ballRadius||x+dx<ballRadius){
dx = -dx;}
};
setInterval(draw,30);
If i run the function draw(), the ball will change color for every frame, making it very "flashing".
How do i get a fix color from getRandomColor() and store it in a variable (let say fixedColorOnly), so that the ball will only display fixedColorOnly until it hit another wall? By then the fixedColorOnly will store another color from getRandomNumber() and bounce and so on.
Thanks in advance.
Just create another global variable for the color. Here's a quick refactor:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="myCanvas" style="border: 1px solid black"></canvas>
<script>
var canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d"),
x = canvas.width / 2,
y = canvas.height - 30,
dx = 2,
dy = -2,
ballRadius = 10,
color = getRandomColor();
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
function drawBall() {
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 (y + dy > canvas.height - ballRadius || y + dy < ballRadius) {
dy = -dy;
color = getRandomColor();
}
if (x + dx > canvas.width - ballRadius || x + dx < ballRadius) {
dx = -dx;
color = getRandomColor();
}
};
setInterval(draw, 30);
</script>
</body>
</html>
I would like to draw a spring in a HTML5 canvas, and show if that spring is at its rest length or not.
My spring is attached to a rectangular shape to some X-Y coordinates and defined as follows:
function Spring(restLenght, width, numRounds){
this.x1 = 0;
this.y1 = 0;
this.x2 = 0;
this.y2 = 0;
this.restLenght = restLenght;
this.width = width;
this.numRounds = numRounds;
this.color = "green";
this.lineWidth = 6;
}
The parameters are explained in the picture below:
When the spring is at its rest length, the lines shall be parallel to each other, otherwise this means the spring is stretched or compressed. Then it will be immediately clear what state the spring is.
I'm stuck now with the bezierCurveTo() Method:
Here is my Fiddle: https://jsfiddle.net/df3mm8kz/1/
var cv = document.getElementById('cv'),
ctx = cv.getContext('2d'),
mouse = capture(cv),
box = new Box(120, 80, 0, 16),
spring = new Spring(160, 20, 2, 0.03, 0.9),
vx = 0,
vy = 0;
function Spring(restLenght, width, numRounds, k, f){
this.x1 = 0;
this.y1 = 0;
this.x2 = 0;
this.y2 = 0;
this.restLenght = restLenght;
this.width = width;
this.numRounds = numRounds;
this.k = k;
this.f = f;
this.color = "green";
this.lineWidth = 6;
}
Spring.prototype.draw = function(ctx) {
var sPX, sPY, cP1X, cP1Y, cP2X, cP2Y, ePX, ePY;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.color;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.moveTo(this.x1, this.y1);
// length of one spring's round
var l = this.restLenght/(this.numRounds + 2);
// Initial segment, from spring anchor point to the first round
sPX = this.x1+l; sPY = this.y2;
ctx.lineTo(sPX, sPY);
// half width of spring's rounds
var hw = 0.5*this.width;
// half length of one spring's round
var hl = 0.5*l;
for(var i=0, n=this.numRounds; i<n; i++) {
cP1X = sPX + hl*i; cP1Y = sPY + hw;
cP2X = sPX + l*i; cp2Y = sPY + hw;
ePX = sPX + l*i; ePY = sPY;
ctx.bezierCurveTo(cP1X,cP1Y,cP2X,cp2Y,ePX,ePY);
cP1X = sPX + hl*i; cP1Y = sPY - hw;
cP2X = sPX + l*i; cp2Y = sPY - hw;
ePX = sPX + l*i; ePY = sPY;
ctx.bezierCurveTo(cP1X,cP1Y,cP2X,cp2Y,ePX,ePY);
}
// Final segment, from last springs round to the center of mass
ctx.lineTo(this.x2, this.y2);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
};
function Box(w, h, mx, my) {
this.x = 0;
this.y = 0;
this.w = w;
this.h = h;
this.mx = mx;
this.my = my;
this.vx = 0;
this.vy = 0;
this.rotation = 0;
this.color = "red";
this.lineWidth = 1;
}
Box.prototype.draw = function(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = "black";
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.rect(-0.5*this.w, -0.5*this.h, this.w, this.h);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = "yellow";
ctx.fillStyle = "yellow";
ctx.arc(this.mx, 0.5*this.h-this.my, 6, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.closePath();
ctx.fill();
ctx.restore();
};
window.requestAnimFrame = (
function(callback) {
return window.setTimeout(callback, 1000/30);
});
(function drawFrame() {
window.requestAnimFrame(drawFrame, cv);
ctx.clearRect(0, 0, cv.width, cv.height);
var dx = box.x - mouse.x,
dy = box.y - mouse.y,
angle = Math.atan2(dy, dx),
boxAngle = angle + 0.5*Math.PI,
targetX = mouse.x + Math.cos(angle) * spring.restLenght,
targetY = mouse.y + Math.sin(angle) * spring.restLenght;
vx += (targetX - box.x) * spring.k;
vy += (targetY - box.y) * spring.k;
vx *= spring.f;
vy *= spring.f;
box.rotation = boxAngle;
box.x += vx;
box.y += vy;
box.draw(ctx);
spring.x1 = mouse.x;
spring.y1 = mouse.y;
spring.x2 = box.x;
spring.y2 = box.y;
spring.draw(ctx);
}());
function capture(element) {
var mouse = {
x: 0,
y: 0,
event: null
},
body_scrollLeft = document.body.scrollLeft,
element_scrollLeft = document.documentElement.scrollLeft,
body_scrollTop = document.body.scrollTop,
element_scrollTop = document.documentElement.scrollTop,
offsetLeft = element.offsetLeft,
offsetTop = element.offsetTop;
element.addEventListener('mousemove', function(event) {
var x, y;
if (event.pageX || event.pageY) {
x = event.pageX;
y = event.pageY;
} else {
x = event.clientX + body_scrollLeft + element_scrollLeft;
y = event.clientY + body_scrollTop + element_scrollTop;
}
x -= offsetLeft;
y -= offsetTop;
mouse.x = x;
mouse.y = y;
mouse.event = event;
}, false);
return mouse;
}
<canvas id="cv" width="600" height="400"></canvas>
Drawing a spring
Rather than use bezier curves which do not actually fit the curve of a spring (but close) I just use a simple path and use trig functions to draw each winding. the function has a start x1,y1 and end x2, y2, windings (should be an integer), width of spring, the offset (bits at ends), Dark colour, and light colour, and the stroke width (width of the wire).
The demo draws an extra highlight to give the spring a little more depth. It can easily be removed.
The code came from this answer that has a simpler version of the same function
function drawSpring(x1, y1, x2, y2, windings, width, offset, col1, col2, lineWidth){
var x = x2 - x1;
var y = y2 - y1;
var dist = Math.sqrt(x * x + y * y);
var nx = x / dist;
var ny = y / dist;
ctx.strokeStyle = col1
ctx.lineWidth = lineWidth;
ctx.lineJoin = "round";
ctx.lineCap = "round";
ctx.beginPath();
ctx.moveTo(x1,y1);
x1 += nx * offset;
y1 += ny * offset;
x2 -= nx * offset;
y2 -= ny * offset;
var x = x2 - x1;
var y = y2 - y1;
var step = 1 / (windings);
for(var i = 0; i <= 1-step; i += step){ // for each winding
for(var j = 0; j < 1; j += 0.05){
var xx = x1 + x * (i + j * step);
var yy = y1 + y * (i + j * step);
xx -= Math.sin(j * Math.PI * 2) * ny * width;
yy += Math.sin(j * Math.PI * 2) * nx * width;
ctx.lineTo(xx,yy);
}
}
ctx.lineTo(x2, y2);
ctx.lineTo(x2 + nx * offset, y2 + ny * offset)
ctx.stroke();
ctx.strokeStyle = col2
ctx.lineWidth = lineWidth - 4;
var step = 1 / (windings);
ctx.beginPath();
ctx.moveTo(x1 - nx * offset, y1 - ny * offset);
ctx.lineTo(x1, y1);
ctx.moveTo(x2, y2);
ctx.lineTo(x2 + nx * offset, y2 + ny * offset)
for(var i = 0; i <= 1-step; i += step){ // for each winding
for(var j = 0.25; j <= 0.76; j += 0.05){
var xx = x1 + x * (i + j * step);
var yy = y1 + y * (i + j * step);
xx -= Math.sin(j * Math.PI * 2) * ny * width;
yy += Math.sin(j * Math.PI * 2) * nx * width;
if(j === 0.25){
ctx.moveTo(xx,yy);
}else{
ctx.lineTo(xx,yy);
}
}
}
ctx.stroke();
}
function display() {
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0, 0, w, h);
ctx.lineWidth = 8;
drawSpring(canvas.width / 2,10, mouse.x,mouse.y,8,100,40,"green","#0C0",15);
}
// Boiler plate code from here down and not part of the answer
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0, firstRun = true;
;(function(){
const RESIZE_DEBOUNCE_TIME = 100;
var createCanvas, resizeCanvas, setGlobals, resizeCount = 0;
createCanvas = function () {
var c,
cs;
cs = (c = document.createElement("canvas")).style;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
resizeCanvas = function () {
if (canvas === undefined) {
canvas = createCanvas();
}
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") {
setGlobals();
}
if (typeof onResize === "function") {
if(firstRun){
onResize();
firstRun = false;
}else{
resizeCount += 1;
setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
}
}
}
function debounceResize() {
resizeCount -= 1;
if (resizeCount <= 0) {
onResize();
}
}
setGlobals = function () {
cw = (w = canvas.width) / 2;
ch = (h = canvas.height) / 2;
}
mouse = (function () {
function preventDefault(e) {
e.preventDefault();
}
var mouse = {
x : 0,
y : 0,
w : 0,
alt : false,
shift : false,
ctrl : false,
buttonRaw : 0,
over : false,
bm : [1, 2, 4, 6, 5, 3],
active : false,
bounds : null,
crashRecover : null,
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.bounds = m.element.getBoundingClientRect();
m.x = e.pageX - m.bounds.left;
m.y = e.pageY - m.bounds.top;
m.alt = e.altKey;
m.shift = e.shiftKey;
m.ctrl = e.ctrlKey;
if (t === "mousedown") {
m.buttonRaw |= m.bm[e.which - 1];
} else if (t === "mouseup") {
m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") {
m.buttonRaw = 0;
m.over = false;
} else if (t === "mouseover") {
m.over = true;
} else if (t === "mousewheel") {
m.w = e.wheelDelta;
} else if (t === "DOMMouseScroll") {
m.w = -e.detail;
}
if (m.callbacks) {
m.callbacks.forEach(c => c(e));
}
if ((m.buttonRaw & 2) && m.crashRecover !== null) {
if (typeof m.crashRecover === "function") {
setTimeout(m.crashRecover, 0);
}
}
e.preventDefault();
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === undefined) {
m.callbacks = [callback];
} else {
m.callbacks.push(callback);
}
}
}
m.start = function (element) {
if (m.element !== undefined) {
m.removeMouse();
}
m.element = element === undefined ? document : element;
m.mouseEvents.forEach(n => {
m.element.addEventListener(n, mouseMove);
});
m.element.addEventListener("contextmenu", preventDefault, false);
m.active = true;
}
m.remove = function () {
if (m.element !== undefined) {
m.mouseEvents.forEach(n => {
m.element.removeEventListener(n, mouseMove);
});
m.element.removeEventListener("contextmenu", preventDefault);
m.element = m.callbacks = undefined;
m.active = false;
}
}
return mouse;
})();
// Clean up. Used where the IDE is on the same page.
var done = function () {
window.removeEventListener("resize", resizeCanvas)
mouse.remove();
document.body.removeChild(canvas);
canvas = ctx = mouse = undefined;
}
function update(timer) { // Main update loop
if(ctx === undefined){
return;
}
globalTime = timer;
display(); // call demo code
if (!(mouse.buttonRaw & 2)) {
requestAnimationFrame(update);
} else {
done();
}
}
setTimeout(function(){
resizeCanvas();
mouse.start(canvas, true);
mouse.crashRecover = done;
window.addEventListener("resize", resizeCanvas);
requestAnimationFrame(update);
},0);
})();
/** SimpleFullCanvasMouse.js end **/
To make drawing easier, use .translate() and .rotate() to move into an aligned coordinate system.
ctx.translate(this.x1, this.y1);
ctx.rotate(Math.atan2(this.y2 - this.y1, this.x2 - this.x1));
You can then draw the spring along the local x-axis, and it will appear in the correct place and rotation.
Your spacing of the segments were wrong. hl*i is half the distance from the spring's starting point, not the segment's starting point.
var cv = document.getElementById('cv'),
ctx = cv.getContext('2d'),
mouse = capture(cv),
box = new Box(120, 80, 0, 16),
spring = new Spring(160, 50, 2, 0.03, 0.9),
vx = 0,
vy = 0;
function Spring(restLenght, width, numRounds, k, f) {
this.x1 = 0;
this.y1 = 0;
this.x2 = 0;
this.y2 = 0;
this.restLenght = restLenght;
this.width = width;
this.numRounds = numRounds;
this.k = k;
this.f = f;
this.color = "green";
this.lineWidth = 6;
}
Spring.prototype.draw = function(ctx) {
var sPX, sPY, cP1X, cP1Y, cP2X, cP2Y, ePX, ePY;
ctx.save();
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.color;
ctx.fillStyle = this.color;
var vx = this.x2 - this.x1;
var vy = this.y2 - this.y1;
var vm = Math.sqrt(vx * vx + vy * vy);
ctx.translate(this.x1, this.y1);
ctx.rotate(Math.atan2(vy, vx));
ctx.beginPath();
ctx.moveTo(0, 0);
// length of one spring's round
var l = vm / (this.numRounds + 2);
// Initial segment, from spring anchor point to the first round
sPX = l;
sPY = 0;
ctx.lineTo(sPX, sPY);
// half width of spring's rounds
var hw = 0.5 * this.width;
for (var i = 0, n = this.numRounds; i < n; i++) {
cP1X = sPX + l * (i + 0.0);
cP1Y = sPY + hw;
cP2X = sPX + l * (i + 0.5);
cp2Y = sPY + hw;
ePX = sPX + l * (i + 0.5);
ePY = sPY;
ctx.bezierCurveTo(cP1X, cP1Y, cP2X, cp2Y, ePX, ePY);
cP1X = sPX + l * (i + 0.5);
cP1Y = sPY - hw;
cP2X = sPX + l * (i + 1.0);
cp2Y = sPY - hw;
ePX = sPX + l * (i + 1.0);
ePY = sPY;
ctx.bezierCurveTo(cP1X, cP1Y, cP2X, cp2Y, ePX, ePY);
}
// Final segment, from last springs round to the center of mass
ctx.lineTo(vm, 0);
//ctx.closePath();
//ctx.fill();
ctx.stroke();
ctx.restore();
};
function Box(w, h, mx, my) {
this.x = 0;
this.y = 0;
this.w = w;
this.h = h;
this.mx = mx;
this.my = my;
this.vx = 0;
this.vy = 0;
this.rotation = 0;
this.color = "red";
this.lineWidth = 1;
}
Box.prototype.draw = function(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = "black";
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.rect(-0.5 * this.w, -0.5 * this.h, this.w, this.h);
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = "yellow";
ctx.fillStyle = "yellow";
ctx.arc(this.mx, 0.5 * this.h - this.my, 6, 0, 2 * Math.PI, false);
ctx.stroke();
ctx.closePath();
ctx.fill();
ctx.restore();
};
window.requestAnimFrame = (
function(callback) {
return window.setTimeout(callback, 1000 / 30);
});
(function drawFrame() {
window.requestAnimFrame(drawFrame, cv);
ctx.clearRect(0, 0, cv.width, cv.height);
var dx = box.x - mouse.x,
dy = box.y - mouse.y,
angle = Math.atan2(dy, dx),
boxAngle = angle + 0.5 * Math.PI,
targetX = mouse.x + Math.cos(angle) * spring.restLenght,
targetY = mouse.y + Math.sin(angle) * spring.restLenght;
vx += (targetX - box.x) * spring.k;
vy += (targetY - box.y) * spring.k;
vx *= spring.f;
vy *= spring.f;
box.rotation = boxAngle;
box.x += vx;
box.y += vy;
box.draw(ctx);
spring.x1 = mouse.x;
spring.y1 = mouse.y;
spring.x2 = box.x;
spring.y2 = box.y;
spring.draw(ctx);
}());
function capture(element) {
var mouse = {
x: 0,
y: 0,
event: null
},
body_scrollLeft = document.body.scrollLeft,
element_scrollLeft = document.documentElement.scrollLeft,
body_scrollTop = document.body.scrollTop,
element_scrollTop = document.documentElement.scrollTop,
offsetLeft = element.offsetLeft,
offsetTop = element.offsetTop;
element.addEventListener('mousemove', function(event) {
var x, y;
if (event.pageX || event.pageY) {
x = event.pageX;
y = event.pageY;
} else {
x = event.clientX + body_scrollLeft + element_scrollLeft;
y = event.clientY + body_scrollTop + element_scrollTop;
}
x -= offsetLeft;
y -= offsetTop;
mouse.x = x;
mouse.y = y;
mouse.event = event;
}, false);
return mouse;
}
<canvas id="cv" width="600" height="400"></canvas>