网站首页 > 知识剖析 正文
因为用到了一点点物理知识, 我们可以称这为极简化的Javascript物理引擎
大叔惯例,先上效果
本例通过canvas画布来实现
虽然很简单, 也是先把舞台准备一下
1.准备个HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
</head>
<body></body>
</html>
2.加个基础样式
<style>
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
main {
width: 100vw;height: 100vh;
background: hsl(0deg, 0%, 10%);
}
</style>
样式的作用是去掉所有元素的外边距、内间距,并把 <main> 元素的宽高设置为与浏览器可视区域相同,背景色为深灰色。
hsl(hue, saturation, brightness) 为 css 颜色表示法之一,参数分别为色相,饱和度和亮度
3.添加 canvas 元素
<main>
<canvas id="gamecanvas"></canvas>
</main>
4.然后就可以用JS来画图了
const canvas = document.getElementById("gamecanvas"); //通过 canvas 的 id 获取 canvas 元素对象。
const ctx = canvas.getContext("2d"); // getContext() 需要一个参数,用于表明是绘制 2d 图像,还是使用 webgl 绘制 3d 图象,这里选择 2d
canvas.width = window.innerWidth; //宽高设置为浏览器可视区域的宽高
canvas.height = window.innerHeight;
let _width = canvas.width;
let _height = canvas.height;
ctx.fillStyle = "hsl(170, 100%, 50%)"; //给 context 设置颜色
ctx.beginPath(); //开始绘图
ctx.arc(150, 100, 50, 0, 2 * Math.PI); //绘制圆形,它接收 5 个参数,前两个为圆心的 x、y 坐标,第 3 个为半径长度, 第 4 个和第 5 个分别是起始角度和结束角度
ctx.fill(); //给圆形填上颜色
试运行看看
这个时候小球还是处于静止状态, 要让它动起来, 就要通过程序修改它的圆心坐标
让小球移动过程其实就是: 画圆 > 擦除 > 在新坐标1上画圆 > 擦除 > 在新坐标2上画圆...
因为人眼的视觉停留效应, 只要这个过程足够快, 那么在人眼看来这个球就是在做连续的运动而不会看到闪动.
需要达到多快呢?
画圆 > 擦除 > 再画圆 这么一个过程可以看作"一帧"
然后每秒超过24帧就可以, 帧数越高看上去运动就越平滑.
在 JavaScript 中,浏览器提供了 window.requestAnimationFrame() 方法,它接收一个回调函数作为参数,每一次执行回调函数就相当于 1 帧动画,我们需要通过递归或循环连续调用它,浏览器会尽可能地在 1 秒内执行 60 次回调函数。那么利用它,我们就可以对 canvas 进行重绘,以实现小球的移动效果。
基础代码结构看上去的样子:
function drawBall() {
window.requestAnimationFrame(drawBall);
}
window.requestAnimationFrame(drawBall);
这个drawBall()函数, 就是60次/秒的函数
把刚才的代码重构一下
let x = 150; //坐标x
let y = 100; //坐标y
let r = 60; //半径
function drawBall(now) {
ctx.fillStyle = "hsl(170, 100%, 50%)";
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.fill();
window.requestAnimationFrame(drawBall);
}
window.requestAnimationFrame(drawBall);
计算圆心坐标 x、y 的移动距离,我们需要速度和时间, 速度就是vy, 还需要有时间
window.requestAnimationFrame() 会把当前时间的毫秒数(即时间戳)传递给回调函数,我们可以把本次调用的时间戳保存起来,然后在下一次调用时计算出执行这 1 帧动画消耗了多少秒,然后根据这个秒数和 x、y 轴方向上的速度去计算移动距离,分别加到 x 和 y 上,以获得最新的位置。
改进代码如下
let x = 100; //坐标
let y = 100;
let r = 60; //半径
let vy = 25; //移动Y轴的速度
let startTime;
function drawBall(now) {
if (!startTime) {
startTime = now;
}
let seconds = (now - startTime) / 1000;
startTime = now;
y += vy * seconds; // 更新Y坐标
ctx.clearRect(0, 0, width, height); // 清除画布
ctx.fillStyle = "hsl(170, 100%, 50%)";
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.fill();
window.requestAnimationFrame(drawBall); //每次执行完毕后继续调用 window.requestAnimationFrame(process)进行下一次循环
}
window.requestAnimationFrame(drawBall);
目前只是个匀速运动, 我们要为它加上重力效果
重力加速度的公式: v=gt2
加速度常量g是个恒定值9.8
//先在函数外添加一个常量
const gravity = 9.80;
...
//函数内部, 在计算y坐标前面加一行:
vy += gravity * (seconds^2); // 重力加速度 v=gt2
y += vy * seconds;
这里我们不希望球跑到屏幕外面去, 同时加个边界判断
//边界检查
let oy = y + r; //y+r
if(oy<height){
window.requestAnimationFrame(drawBall); //每次执行完毕后继续调用 window.requestAnimationFrame(process)进行下一次循环
}
这样就可以达成文章最开头的运动效果了
很简单, 同时其实也很有意思
比如加个半径变化控制, 就会看到球越往下掉就越小/大
r = r - 0.8;
下面是全部代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<style>
* {
box-sizing: border-box;
padding: 0;
margin: 0;
font-family: sans-serif;
}
main {
width: 100vw;
height: 100vh;
background: hsl(0deg, 0%, 10%);
}
</style>
</head>
<body>
<main>
<canvas id="gamecanvas"></canvas>
</main>
</body>
<script>
const canvas = document.getElementById("gamecanvas"); //通过 canvas 的 id 获取 canvas 元素对象。
const ctx = canvas.getContext("2d"); // getContext() 需要一个参数,用于表明是绘制 2d 图像,还是使用 webgl 绘制 3d 图象,这里选择 2d
canvas.width = window.innerWidth; //宽高设置为浏览器可视区域的宽高
canvas.height = window.innerHeight;
let width = canvas.width;
let height = canvas.height;
const gravity = 9.80;
let x = 100; //坐标
let y = 100;
let r = 60; //半径
let vy = 25; //移动Y轴的速度
let startTime;
function drawBall(now) {
if (!startTime) {
startTime = now;
}
let seconds = (now - startTime) / 1000;
startTime = now;
vy += gravity * (seconds^2); // 重力加速度 v=gt2
y += vy * seconds;
r = r - 0.8;
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = "hsl(170, 100%, 50%)";
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.fill();
//边界检查
let oy = y + r;
if(oy<height){
window.requestAnimationFrame(drawBall); //每次执行完毕后继续调用 window.requestAnimationFrame(process)进行下一次循环
}
}
window.requestAnimationFrame(drawBall);
</script>
这期就酱,下期再见[酷拽]
复杂的问题简单化
每次只关注一个知识点
对技术有兴趣的小伙伴可以关注我, 我经常分享各种奇奇怪怪的技术知识
猜你喜欢
- 2024-12-15 这个图片压缩神器,直接可以在前端用
- 2024-12-15 十款代码表白特效,一个比一个浪漫
- 2024-12-15 HarmonyOS实现Material风格的下拉刷新
- 2024-12-15 QML Canvas基础概念 qml mvvm
- 2024-12-15 uniapp如何实现canvas动画 uniapp页面动画
- 2024-12-15 HTML5教程关于canvas的线条知识,可以这样总结方法
- 2024-12-15 多边形扫描线填充算法及TypeScript示例
- 2024-12-15 HTML+JavaScript案例分享: 打造经典俄罗斯方块,详解实现全过程
- 2024-12-15 第76节 Canvas绘图(下)-前端开发之JavaScript-王唯
- 2024-12-15 用canvas画简单的“我的世界”人物头像
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)