🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 自由落体须知 当一个篮球下落时会受重力作用和与空气的摩擦力,每次弹起落下篮球都会消耗能量,最终直到小球停止地面。 在 canvas中我们需要模拟重力加速度和摩擦力(也就是小球消耗的能量来源)。 在canvas中主要思路: 1. 点击鼠标时记录鼠标坐标,并绘制小球,将生成的小球存储起来 2. 小球下落时,根据重力加速度计算小球位置,并将canvas清除,重新绘制。(没一帧绘制一次) 3. 检测小球是否到达底部,如果到达底部让小球位置向上移动,速度慢慢减少,同时因为要模拟能力损失,所以小球向上的位置不能回到上次下落时的初始位置 ### 自由落体代码 ```HTML <canvas width="900" height="800"> 您的浏览器不支持canvas </canvas> <script> let canvas = document.querySelector('canvas') let ctx=canvas.getContext("2d"); let ballAry = []; // 存储小球 let ballId = 0; // 小球id let g = .5 // 定义重力加速度 let bounce = 0.8;//定义反弹系数; let Flag = true; // 是否开启 requestAnimationFrame 标识,防止多次启动 canvas.onmouseup = function(e){ let x = e.pageX - canvas.getBoundingClientRect().left; // 获取小球初始x坐标 let y = e.pageY - canvas.getBoundingClientRect().top; // 获取小球初始y坐标 let radius = Math.floor(Math.random()*10 + 15) // 随机获取小球半径 new Ball(x,y,radius) } function Ball(x,y,radius){ this.x = x; this.y = y; this.vx = 0; // 小球初始x轴速率为0 自由下落时用不到,左右弹跳时用的到 this.vy = 0; // 小球初始x轴速率为0 this.radius = radius; ballAry[ballId] = this // 将小球存在ballAry 数组中 this.id = ballId++; this.init() } Ball.prototype.init = function(){ // 初始绘制小球并开启定时 this.draw() if(Flag){ this.updata() Flag = false; } } Ball.prototype.draw = function(){ // 绘制小球 ctx.beginPath(); ctx.arc(this.x,this.y,this.radius,0,2*Math.PI); ctx.fillStyle = "red" ctx.fill(); ctx.closePath(); } Ball.prototype.updataPosition = function(){ // 更新小球位置 if(this.y >= 800 - this.radius){ // 碰撞底部时 this.y = 800 - this.radius // 小球与底部碰撞时,将速率变成负值,并减小0.8,这样模拟消失的能量 this.vy *= -bounce; } this.vy += g; // 当小球向下时加速度逐渐增加,向上时逐渐减小 this.y += this.vy // 小球y坐标位置,其实vy 是加速度,这里的加速度是每帧小球移动的距离 } Ball.prototype.updata = function(){ ctx.clearRect(0, 0, 900, 800); // 清除canvas ballAry.map(item=>{ // 对每一个小球进行定位,并绘制 item.updataPosition() item.draw() }) let animateFrame = requestAnimationFrame(arguments.callee); // 定时,每帧刷新一次 } </script> ``` ### 自由落体加左右碰撞 我们上边实现了小球的自由落体,下来我们要实现小球碰到四周进行弹跳的效果, 和自由落体不同的是,我们考虑到了x轴方向的速度,并且在鼠标移动时给小球添加一个横向的初始速度,该初始速度与鼠标开始时移动的距离成正比。 ```HTML <canvas width="900" height="800"> 您的浏览器不支持canvas </canvas> <script> let canvas = document.querySelector('canvas') let ctx=canvas.getContext("2d"); let ballAry = []; let ballId = 0; let g = .5 // 定义重力加速度 let bounce = 0.7;//定义反弹系数; let Flag = true; // 标识,只添加一次定时 let startPosition = [] // 存储鼠标开始坐标位置 let endPosition = [] // 存储鼠标结束坐标位置 let current = [] // 当前鼠标坐标 canvas.onmousedown = function(e){ startPosition[0] = e.pageX - canvas.getBoundingClientRect().left; startPosition[1] = e.pageY - canvas.getBoundingClientRect().top; } canvas.onmousemove = function(e){ current[0] = e.pageX - canvas.getBoundingClientRect().left; current[1] = e.pageY - canvas.getBoundingClientRect().top; } canvas.onmouseup = function(e){ let speed = [] endPosition[0] = e.pageX - canvas.getBoundingClientRect().left; endPosition[1] = e.pageY - canvas.getBoundingClientRect().top; speed[0] = (startPosition[0] - endPosition[0])/10 speed[1] = (startPosition[1] - endPosition[1])/10 let radius = Math.floor(Math.random()*10 + 15) new Ball(current,speed,radius) } function Ball(position,speed,radius){ this.x = position[0]; this.y = position[1]; this.speedX = speed[0]; // 运动的x轴初始速度 this.speedY = speed[1]; // 运动的y轴初始速度 this.radius = radius; ballAry[ballId] = this this.id = ballId++; this.init() } Ball.prototype.init = function(){ this.draw() if(Flag){ this.updata() Flag = false; } } Ball.prototype.draw = function(){ ctx.beginPath(); ctx.arc(this.x,this.y,this.radius,0,2*Math.PI); ctx.fillStyle = "red" ctx.fill(); ctx.closePath(); } Ball.prototype.checkCollide = function(){ if(this.y >= 800 - this.radius){ // 碰撞底部时 this.y = 800 - this.radius this.speedY *= -bounce; }else if(this.y <= this.radius){ this.y = this.radius this.speedY *= -bounce } this.speedY += g; // x轴左右碰撞 if(this.x >= 900 - this.radius){ this.x = 900 - this.radius this.speedX *= -bounce }else if(this.x <= this.radius){ this.x = this.radius this.speedX *= -bounce } } Ball.prototype.updataPosition = function(){ this.checkCollide() this.x += this.speedX this.y += this.speedY } Ball.prototype.updata = function(){ ctx.clearRect(0, 0, 900, 800); ballAry.map(item=>{ item.updataPosition() item.draw() }) requestAnimationFrame(arguments.callee); } </script> ```