网站首页 > 技术文章 正文
因为用到了一点点物理知识, 我们可以称这为极简化的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-11-24 六种设计难题的CSS实用技巧
- 2024-11-24 前端入门教程:CSS标准盒模型和怪异盒模型区别
- 2024-11-24 WEB前端-CSS盒子
- 2024-11-24 手把手教你css 中多种边框的实现小窍门【实践】
- 2024-11-24 纯CSS实现轮播图
- 2024-11-24 「干货」移动端Web页面适配
- 2024-11-24 《Web前端技术H5+CSS3》笔记--第六章 盒子模型「云图智联」
- 2024-11-24 深入浅出超好用的 CSS 阴影技巧
- 2024-11-24 掌握Flex布局的这几个常用属性,搞定弹性布局不在话下
- 2024-11-24 用 CSS Grid 布局制作一个响应式柱状图
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-