抛球模拟器主要介绍
受到canvas官网介绍的启发,做了一个带重力和摩擦力效果的抛球模拟器,类似高中物理的平抛运动模型。这里增加了重力效果,可以很好的模拟小球从不同位置,不同的水平速度、垂直速度以及不同的小球半径的抛物运动,碰撞到边界时会反弹,小球在运动时附带拖尾效果,水平运动附带摩擦力系数效果。
白球代表抛球起始位置,黄球为实际运动位置。
代码中包含详细注释,希望对大家的前端学习有帮助,大家有其他想法可以一起交流。
界面效果
HTML及其CSS代码(canvas.html):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/homework/static/third-party/bootstrap-5.3.0-alpha1-dist/css/bootstrap.min.css">
<script src="https://cdn.acwing.com/static/jquery/js/jquery-3.3.1.min.js"></script>
<title>canvas应用</title>
<!-- CSS部分 -->
<style>
canvas {
border: 1px solid saddlebrown;
background-color: #000;
display: block;
margin: 0 auto;
border-radius: 30px;
}
.inbox {
width: 600px;
margin: 10px;
}
.container {
width: 700px;
height: 300px;
}
</style>
</head>
<body>
<!-- html部分: 绘制一个小球 -->
<canvas id="canvas" width="1000" height="600">
</canvas>
<div class="container">
<div class="input-group inbox">
<span class="input-group-text">初始坐标</span>
<input type="text" aria-label="X" id="input1" class="form-control input1" placeholder="请输入x坐标(0 ~ 850)" value="100">
<input type="text" aria-label="Y" class="form-control input2" placeholder="请输入y坐标(0 ~ 550)" value="100">
</div>
<div class="input-group inbox">
<span class="input-group-text">初始速度</span>
<input type="text" aria-label="X" class="form-control input3" placeholder="水平速度(-5 ~ 5)" value="5">
<input type="text" aria-label="Y" class="form-control input4" placeholder="竖直速度(-5 ~ 5)" value="2">
</div>
<div class="input-group inbox">
<span class="input-group-text">设置半径</span>
<input type="text" aria-label="R" class="form-control input5" placeholder="小球半径(10 ~ 30)" value="15">
</div>
<button type="button" class="btn btn-primary" style="margin: 10px;">开始模拟</button>
</div>
<!-- type=“module“的script标签内部默认是严格模式 -->
<script type="module">
import {main} from "/homework/static/js/canvas1.js";
// 引入js文件,放在html尾部确保执行时前面的html已经渲染完毕
main();
</script>
</body>
</html>
js代码(canvas1.js)
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
let raf;
let ball = {
sx: 100,
sy: 100,
x: 100,
y: 100,
vx: 5,
vy: 2,
radius: 15,
color: 'orange',
draw: function() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
ctx.fillStyle = 'rgba(0,0,0,0.1)';
ctx.fillRect(0,0,canvas.width,canvas.height);
},
drawstart: function() {
// 渲染起始点
ctx.beginPath();
ctx.arc(this.sx, this.sy, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = "white";
ctx.fill();
},
};
let stop = false;
let last_stamp = 0;
// 递归函数
function draw(timestamp) {
let t = timestamp - last_stamp;
last_stamp = timestamp;
ctx.clearRect(0,0, canvas.width, canvas.height);
// 渲染运动效果
ball.draw();
// 渲染起点
ball.drawstart();
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fillRect(0,0,canvas.width,canvas.height);
ball.x += ball.vx;
ball.y += ball.vy;
raf = window.requestAnimationFrame(draw);
// 添加加速度
ball.vy = ball.vy + 19.8 * t / 1000;
// if (ball.vy < 0)
// ball.vy = ball.vy + 10 * t / 1000; // 上升过程中的阻力大一些
// console.log("vy:", ball.vy);
// 上下碰撞
if (ball.y + ball.vy + ball.radius > canvas.height || ball.y + ball.vy - ball.radius < 0) {
ball.vy = -ball.vy * 0.8; // 每次碰撞速度损失
ball.vx *= .99;
if (Math.abs(ball.vy) < 1.7) ball.vy *= 0.9;
if (Math.abs(ball.vy) < 1 && ball.vy < 0) {
// console.log("临界值");
ball.vy = 0;
}
}
// 左右碰撞
if (ball.x + ball.vx + ball.radius > canvas.width || ball.x + ball.vx - ball.radius < 0) {
ball.vx = -ball.vx; // 速度反向
ball.vx *= .99; // 地面摩擦系数
ball.vy = ball.vy * 0.95;
}
if (stop) ball.vx *= .99; // 在地面滚动时加上滚动摩擦系数
if (stop && Math.abs(ball.vx) <= 0.1) ball.vx =0;
}
window.requestAnimationFrame(draw);
// button点击事件
$("button").click(function() {
let $x = $(".input1");
let $y = $('.input2');
let $vx = $('.input3');
let $vy = $('.input4');
let $r = $('.input5');
ball.x = parseFloat($x.val()) + 25;
ball.y = parseFloat($y.val()) + 25 ;
ball.sx = ball.x;
ball.sy = ball.y;
ball.vx = parseFloat($vx.val());
ball.vy = parseFloat($vy.val());
ball.radius = parseFloat($r.val());
});
function main() {
}
export {
main
}