const ctx = canvas.getContext("2d");
const mouse = {x : 0, y : 0, button : false}
function mouseEvents(e){
mouse.x = e.pageX;
mouse.y = e.pageY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;
var ch = h / 2;
const gravity = 1;
const PI2 = Math.PI * 2;
const setStyle = (ctx,style) => { Object.keys(style).forEach(key=> ctx[key] = style[key] ) };
const ball = {
r : 50,
x : 50,
y : 50,
dx : 0.2,
dy : 0.2,
maxSpeed : 8,
style : {
lineWidth : 12,
strokeStyle : "green",
},
draw(ctx){
setStyle(ctx,this.style);
ctx.beginPath();
ctx.arc(this.x,this.y,this.r-this.style.lineWidth * 0.45,0,PI2);
ctx.stroke();
},
update(){
this.dy += gravity;
var speed = Math.sqrt(this.dx * this.dx + this.dy * this.dy);
var x = this.x + this.dx;
var y = this.y + this.dy;
if(y > canvas.height - this.r){
y = (canvas.height - this.r) - (y - (canvas.height - this.r));
this.dy = -this.dy;
}
if(y < this.r){
y = this.r - (y - this.r);
this.dy = -this.dy;
}
if(x > canvas.width - this.r){
x = (canvas.width - this.r) - (x - (canvas.width - this.r));
this.dx = -this.dx;
}
if(x < this.r){
x = this.r - (x - this.r);
this.dx = -this.dx;
}
this.x = x;
this.y = y;
if(speed > this.maxSpeed){
var reduceSpeed = this.maxSpeed + (speed-this.maxSpeed) * 0.9;
this.dx = (this.dx / speed) * reduceSpeed;
this.dy = (this.dy / speed) * reduceSpeed;
}
}
}
const ballShadow = {
r : 50,
x : 50,
y : 50,
dx : 0.2,
dy : 0.2,
}
const bat = {
x : 100,
y : 250,
dx : 0,
dy : 0,
width : 140,
height : 10,
style : {
lineWidth : 2,
strokeStyle : "black",
},
draw(ctx){
setStyle(ctx,this.style);
ctx.strokeRect(this.x - this.width / 2,this.y - this.height / 2, this.width, this.height);
},
update(){
this.dx = mouse.x - this.x;
this.dy = mouse.y - this.y;
var x = this.x + this.dx;
var y = this.y + this.dy;
x < this.width / 2 && (x = this.width / 2);
y < this.height / 2 && (y = this.height / 2);
x > canvas.width - this.width / 2 && (x = canvas.width - this.width / 2);
y > canvas.height - this.height / 2 && (y = canvas.height - this.height / 2);
this.dx = x - this.x;
this.dy = y - this.y;
this.x = x;
this.y = y;
}
}
function doBatBall(bat,ball){
var mirrorX = 1;
var mirrorY = 1;
const s = ballShadow;
s.x = ball.x;
s.y = ball.y;
s.dx = ball.dx;
s.dy = ball.dy;
s.x -= s.dx;
s.y -= s.dy;
const batW2 = bat.width / 2;
const batH2 = bat.height / 2;
var batH = batH2 + ball.r;
var batW = batW2 + ball.r;
s.x -= bat.x;
s.y -= bat.y;
s.dx -= bat.dx;
s.dy -= bat.dy;
if(s.x < 0){
mirrorX = -1;
s.x = -s.x;
s.dx = -s.dx;
}
if(s.y < 0){
mirrorY = -1;
s.y = -s.y;
s.dy = -s.dy;
}
var distY = (batH - s.y);
var distX = (batW - s.x);
if(s.dx > 0 && s.dy > 0){ return }
var ballSpeed = Math.sqrt(s.dx * s.dx + s.dy * s.dy);
var bottomX = s.x +(s.dx / s.dy) * distY;
var rightY = s.y +(s.dy / s.dx) * distX;
var distB = Math.hypot(bottomX - s.x, batH - s.y);
var distR = Math.hypot(batW - s.x, rightY - s.y);
var hit = false;
if(s.dy < 0 && bottomX <= batW2 && distB <= ballSpeed && distB < distR){
hit = true;
s.y = batH - s.dy * ((ballSpeed - distB) / ballSpeed);
s.dy = -s.dy;
}
if(! hit && s.dx < 0 && rightY <= batH2 && distR <= ballSpeed && distR <= distB){
hit = true;
s.x = batW - s.dx * ((ballSpeed - distR) / ballSpeed);;
s.dx = -s.dx;
}
if(!hit){
const u = ((batW2 - s.x) * s.dx + (batH2 - s.y) * s.dy)/(ballSpeed * ballSpeed);
var cpx = s.x + s.dx * u;
var cpy = s.y + s.dy * u;
const radSqr = ball.r * ball.r;
const dist = (cpx - batW2) * (cpx - batW2) + (cpy - batH2) * (cpy - batH2);
if(dist > radSqr){ return }
var d = Math.sqrt(radSqr - dist) / ballSpeed;
cpx -= s.dx * d;
cpy -= s.dy * d;
d = Math.hypot(cpx - s.x,cpy - s.y);
if(d > ballSpeed){ return }
s.x = cpx;
s.y = cpy;
const ty = (cpx - batW2) / ball.r;
const tx = -(cpy - batH2) / ball.r;
const bsx = s.dx / ballSpeed;
const bsy = s.dy / ballSpeed;
const dot = (bsx * tx + bsy * ty) * 2;
d = ballSpeed - d;
s.dx = (tx * dot - bsx);
s.dy = (ty * dot - bsy);
s.x += s.dx * d;
s.y += s.dy * d;
s.dx *= ballSpeed;
s.dy *= ballSpeed;
hit = true;
}
if(hit){
s.x *= mirrorX;
s.dx *= mirrorX;
s.y *= mirrorY;
s.dy *= mirrorY;
s.x += bat.x;
s.y += bat.y;
s.dx += bat.dx;
s.dy += bat.dy;
ball.x = s.x;
ball.y = s.y;
ball.dx = s.dx;
ball.dy = s.dy;
}
}
function update(timer){
if(w !== innerWidth || h !== innerHeight){
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
}
ctx.setTransform(1,0,0,1,0,0);
ctx.globalAlpha = 1;
ctx.clearRect(0,0,w,h);
bat.update();
ball.update();
doBatBall(bat,ball);
bat.draw(ctx);
ball.draw(ctx);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { position : absolute; top : 0px; left : 0px; }
body {font-family : arial; }
Use the mouse to move the bat and hit the ball.
<canvas id="canvas"></canvas>
rect
对象,但我有一种感觉,在计算NearestY
时,你想要高度而不是宽度,就像rect.h
而不是rect.w
? - arbuthnott