中小企业网站建设咨询,营销型网站seo,网站建设 国风网络,网站开发框架参考文献通过仿制 Google Chrome 的恐龙小游戏#xff0c;我们可以掌握如下知识点#xff1a;
灵活使用视口单位掌握绝对定位JavaScript 来操作 CSS 变量requestAnimationFrame 函数的使用无缝动画实现
页面结构 实现页面结构
通过上述的页面结构我们可以知道#xff0c;此游戏中…通过仿制 Google Chrome 的恐龙小游戏我们可以掌握如下知识点
灵活使用视口单位掌握绝对定位JavaScript 来操作 CSS 变量requestAnimationFrame 函数的使用无缝动画实现
页面结构 实现页面结构
通过上述的页面结构我们可以知道此游戏中需要有如下的元素
游戏世界小恐龙分数游戏开始的信息提示地面仙人掌
然后构建对应的页面结构
div classworlddiv classscore0/divdiv classstart-screen按任意键开始/divimg src./image/ground.png classground /img src./image/ground.png classground /img src./image/dino-stationary.png classdino /
/div使用绝对定位完成页面元素的布局
定义好元素后我们可以编写对应的样式
.world {position: relative;overflow: hidden;/* 这里我们先使用固定值来设置随后使用JS来动态设置值 */width: 100%;height: 300px;
}.score {position: absolute;top: 1vmin;right: 1vmin;font-size: 3vmin;
}.start-screen {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);font-size: 3vmin;
}.hine {display: none;
}/* 使用CSS变量进行占位然后使用JS来控制变量计算 */
.ground {--left: 0;position: absolute;width: 300%;bottom: 0;left: calc(var(--left) * 1%);
}.dino {--bottom: 0;position: absolute;left: 0;height: 30%;bottom: calc(var(--bottom) * 1%);
}.cactus {--left: 0;position: absolute;left: calc(var(--left) * 1%);height: 30%;bottom: 0;
}使用 JS 来监听视口大小改变从而修改游戏世界元素的宽高
为了能够很好的适配所有的设备我们需要使用 JS 来监听视口的大小改变从而动态修改页面的元素大小具体的步骤如下。
首先在游戏世界元素中添加如下属性
div classworld data-world/div编写 JS 代码
const WORLD_WIIDTH 100;
const WORLD_HEIGHT 30;const worldElem document.querySelector([data-world]);setPixelToWorldScale(); // 初始化游戏世界的大小
window.addEventListener(resize, setPixelToWorldScale);function setPixelToWorldScale() {let worldToPixeScale 0;/*** 判断视口的大小是否大于我们自定义的常量这样做主要是保证我们的游戏世界的元素大小在视口的中央** 当视口宽度大于高度时判断结果为false就取高度做计算* 当视口宽度小于高度时判断结果为true就取宽度做计算*/if (window.innerWidth / window.innerHeight WORLD_WIIDTH / WORLD_HEIGHT) {worldToPixeScale window.innerWidth / WORLD_WIIDTH;} else {worldToPixeScale window.innerHeight / WORLD_HEIGHT;}worldElem.style.width ${WORLD_WIIDTH * worldToPixeScale}px;worldElem.style.height ${WORLD_HEIGHT * worldToPixeScale}px;
}使用 requestAnimationFrame 编写对应的更新动画函数
整体的页面布局好以后我们就可以开始对恐龙、地面、仙人掌和分数进行动画的渲染。但是首先我们先用编写控制动画运行的函数。
我们要编写动画函数的话可以使用requestAnimationFrame函数来帮我们实现。
window.requestAnimationFrame()函数它会告诉浏览器你希望执行一个动画并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。具体的实现代码如下
let lastTime; // 上一次动画执行时间
function update(time) {// 动画开始执行时lastTime是为null所以需要对齐赋值if (lastTime null) {lastTime time;window.requestAnimationFrame(update);return;}const delta time - lastTime; // 计算上一次动画时间和本次动画时间差console.log(delta);lastTime time;window.requestAnimationFrame(update);
}实现地面动画
说明 以下核心代码中会出现一些辅助函数为了不让篇幅过长所以这里就不在展示辅助函数代码可以在下载完整的代码中进行查看。
实现地面移动的动画其实很简单因为我们地面的样式是使用绝对定位并且地面元素有两个所以只要让两个地面元素交替向左移动就可以实现无缝的动画。
为了提示游戏的难度游戏会随着时间的推移地面的移动速度会越来越快具体核心代码如下
// 地面相关JS
const SPEED 0.05; // 地面移动量移动量越大速度越快
const grounds document.querySelectorAll([data-ground]);// 重置地面位置
export function setupGround() {setCustomProperty(grounds[0], --left, 0);setCustomProperty(grounds[1], --left, 300);
}/*** 修改每帧地面动画* param {number} delta 每帧动画的时间差* param {number} speedScale 难度系数*/
export function updateGround(delta, speedScale) {grounds.forEach((ground) {incrementCustomProperty(ground, --left, delta * speedScale * SPEED * -1); // 向左移动地面// 当地面元素左移到-300像素时需要把对应的地面元素重置到第二个地面元素后if (getCustomProperty(ground, --left) -300) {// 因为有两个地面元素所以长度为两个地面元素的长度incrementCustomProperty(ground, --left, 600);}});
}// 核心游戏JS// 动画执行函数
function update(time) {if (lastTime null) {lastTime time;window.requestAnimationFrame(update);return;}const delta time - lastTime;updateSpeedScale(delta);updateGround(delta, speedScale);lastTime time;window.requestAnimationFrame(update);
}// 修改游戏难度系数随着时间的推移难度系数值越大地面的移动速度越快
function updateSpeedScale(delta) {speedScale delta * SPEED_SCALE_INCREASE;
}实现小恐龙相关动画
实现小恐龙关键帧替换
在这个游戏中我们的小恐龙是由两个关键帧交替实现动画效果的所以我们需要在一帧动画期间交替替换小恐龙的两个关键帧从而绘制小恐龙跑步的动画具体核心代码如下
/*** 修改小恐龙的动画* param {number} delta 每帧动画的时间差* param {number} speedScale 难度系数*/
export function updateDino(delta, speedScale) {handleRun(delta, speedScale);
}/*** 小恐龙运动动画* param {number} delta 每帧动画的时间差* param {number} speedScale 难度系数* returns*/
function handleRun(delta, speedScale) {// 判断小恐龙是否跳起跳起的话关键帧只能固定一个if (isJumping) {dinoElem.src ./imgs/dino-stationary.png;return;}/*** 因为一帧动画可以执行很多次所以我们需要对每帧执行完成后交替更换小恐龙的关键帧图片* FRAME_TIME用于每帧小恐龙交替拆分的关键值*/if (currentFrameTime FRAME_TIME) {dinoFrame (dinoFrame 1) % DINO_FRAME_COUNT;dinoElem.src ./imgs/dino-run-${dinoFrame}.png;currentFrameTime - FRAME_TIME;}currentFrameTime delta * speedScale;
}实现小恐龙跳起动画
小恐龙的跳起主要是在 Y 轴上进行上下运动并且为了达到最好的动画效果我们会声明两个变量用于控制跳起动画的效果。具体核心代码如下
/*** 跳起动画* param {number} delta 每帧动画的时间差* returns 小恐龙跳起跳起的高度*/
function handleJump(delta) {if (!isJumping) return;incrementCustomProperty(dinoElem, --bottom, yVelocity * delta);// 接触到地面后重置相关参数if (getCustomProperty(dinoElem, --bottom) 0) {setCustomProperty(dinoElem, --bottom, 0);isJumping false;}yVelocity - GRAVITY * delta;
}// 监听小恐龙跳起事件
function onJump(e) {if (e.code ! Space || isJumping) return;yVelocity JUMP_SPEED;isJumping true;
}实现仙人掌动画
仙人掌是在一定时间间隔内在游戏世界中创建出来并且动画移动效果跟地面一样。所以我们在实现此功能的时候最核心的业务就是在随机间隔内生成对应的仙人掌并执行相应的动画。具体的核心代码如下
// 创建仙人掌
function createCactus() {const cactus document.createElement(img);cactus.dataset.cactus true;cactus.src ./imgs/cactus.png;cactus.classList.add(cactus);setCustomProperty(cactus, --left, 100);worldElem.append(cactus);
}// 生成仙人掌并执行相应动画
export function updateCactus(delta, speedScale) {document.querySelectorAll([data-cactus]).forEach((cactus) {incrementCustomProperty(cactus, --left, delta * speedScale * SPEED * -1);if (getCustomProperty(cactus, --left) -100) {cactus.remove();}});// 判断是否要生成下一个仙人掌if (nextCactusTime 0) {createCactus();// 生成下一个仙人掌的时间间隔nextCactusTime randomNumberBetween(CACTUS_INTERVAL_MIN, CACTUS_INTERVAL_MAX) /speedScale;}nextCactusTime - delta;
}// 重置仙人掌
export function setupCactus() {nextCactusTime CACTUS_INTERVAL_MIN;// 移除是有仙人掌document.querySelectorAll([data-cactus]).forEach((cactus) {cactus.remove();});
}游戏结束评定
游戏的结束判断就是小恐龙是否碰到仙人掌所以我们首先需要添加获取小恐龙和仙人掌的方法具体函数如下
// 获取仙人掌
export function getCactusRects() {return [...document.querySelectorAll([data-cactus])].map((cactus) {return cactus.getBoundingClientRect();});
}// 获取小恐龙
export function getDinoRect() {return dinoElem.getBoundingClientRect();
}// 判断是否游戏结束
function checkLose() {const dinoRect getDinoRect();return getCactusRects().some((rect) isCollision(rect, dinoRect));
}// 通过判断小恐龙和仙人掌是否碰撞
function isCollision(rect1, rect2) {return (rect1.left rect2.right rect1.top rect2.bottom rect1.right rect2.left rect1.bottom rect2.top);
}完整代码下载
完整代码下载