目录😋
背景介绍
“狼人杀”是一款多人参与的策略类桌面游戏。本题我们一起完成一个简易的狼人杀游戏,让我们找到其中的狼人。
准备步骤
开始答题前,需要先打开本题的项目代码文件夹,目录结构如下:
├── css │ └── style.css ├── images │ └── card.svg ├── index.html └── js └── myarray.js
其中:
css/style.css
是样式文件。index.html
是主页面。images
是图片文件夹。js/myarray.js
是需要补充的 js 文件。注意:打开环境后发现缺少项目代码,请手动键入下述命令进行下载:
cd /home/project wget https://labfile.oss.aliyuncs.com/courses/9791/09.zip && unzip 09.zip && rm 09.zip
在浏览器中预览
index.html
页面效果如下:
目标效果
在本题
index.html
已经给出的数组中,我们可以通过数组的filter
方法:cardList.filter((item) => item.category == "werewolf")
返回一个都是狼人的新数组。但是技术主管为了考验大家的技术,规定了在代码中任何地方都不能出现filter
关键字。所以我们需要封装一个myarray
方法来实现类似数组filter
的功能。
- 狼人比较狡猾,筛选狼人的条件可能会变化,例如
item.name
,请实现一个通用的方法。- 完成封装后,页面效果会自动完成,效果见文件夹下
effect.gif
(请使用 VS Code 或者浏览器打开 gif 图片)。
要求规定
- 禁止在代码中出现
filter
关键字。- 请严格按照考试步骤操作,切勿修改考试默认提供项目中的文件名称、文件夹路径等。
- 请勿修改项目中提供的
id
、class
、函数名等名称,以免造成无法判题通过。
判分标准
- 本题完全实现题目目标得满分,否则得 0 分。
通关代码✔️
// 返回条件为真的新数组
Array.prototype.myarray = function (cb) {
// TODO:待补充代码
let newArr = []
this.forEach((item)=>{
if(cb(item)){newArr.push(item)}
})
return newArr
};
代码解析📑
一、HTML 部分
<!DOCTYPE html> <html> <head> <title>寻找小狼人</title> <meta charset="utf-8" /> <link href="css/style.css" rel="stylesheet" type="text/css" /> </head> <body> <div id="content"> <p id="gameText"></p> <ul></ul> <div class="btnbox"> <button class="btn">寻找狼人</button> </div> </div> <script src="./js/myarray.js"></script> <script> // JavaScript 代码部分 </script> </body> </html>
<!DOCTYPE html>
:声明文档类型为 HTML5。<head>
部分:
<meta charset="utf-8" />
:设置字符编码为 UTF - 8。<title>传送门</title>
:设置网页标题为 “传送门”。<link rel="stylesheet" type="text/css" href="./css/index.css" />
:引入外部 CSS 文件index.css
来设置页面样式。<body>
部分:
<div id="top"></div>
、<div id="middle"></div>
、<div id="foot"></div>
:分别表示页面的顶部、中间和底部区域。<div id="lift">
:侧边栏容器,包含三个链接和分隔线。
<a>
标签:三个链接,分别绑定了toFunction
函数,点击时会调用该函数并传入不同的滚动高度参数。<span class="line"></span>
:分隔线。<script>
标签:引入 jQuery 库和自定义的 JavaScript 文件index.js
。
二、CSS 部分
/* 重置默认样式 */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend { margin: 0; padding: 0; font-size: 100%; border: 0; outline: 0; background: transparent; } ol, ul { list-style: none; } :focus { outline: 0; } /* 页面内容样式 */ #content { padding-top: 40px; } #content ul { width: 1200px; margin: 0 auto; display: flex; flex-wrap: wrap; justify-content: center; } .btnbox { width: 1200px; margin: 0 auto; text-align: center; } #content ul li { margin: 20px; width: 200px; height: 180px; } #content ul li:last-child { margin-right: 0; } #content ul li a { position: relative; display: block; width: 100%; height: 100%; perspective: 800px; } #content ul li a > div { position: absolute; left: 0; height: 0; width: 100%; height: 100%; color: #fff; overflow: hidden; transform-style: preserve-3d; transition: 0.8s ease-in-out; backface-visibility: hidden; } #content ul li a div:first-child { display: flex; justify-content: center; transform: rotateY(0); z-index: 2; border: 1px solid #2e7eee; } #content ul li a div:last-child { background: rgb(0, 188, 212); transform: rotateY(180deg); z-index: 1; } #content ul li a div h1 { text-align: center; line-height: 180px; font-size: 26px; } #content ul li a div img { max-width: 100%; } /* 卡片翻转动画类 */ .rotatey180 { transform: rotateY(-180deg) !important; } .rotateyzero { transform: rotateY(0) !important; } /* 按钮样式 */ .btn, .btn-all { margin-top: 60px; display: inline-block; color: #0099cc; background: #2e7eee; border-radius: 10px; text-decoration: none; text-transform: uppercase; border: none; color: white; padding: 8px 16px; text-align: center; text-decoration: none; display: inline-block; font-size: 16px; transition-duration: 0.4s; cursor: pointer; } /* 卡片编号样式 */ .cardnum { position: absolute; font-size: 20px; left: 10px; top: 10px; } /* 游戏提示信息样式 */ #gameText { text-align: center; }
- 重置默认样式:清除 HTML 元素的默认边距、内边距、边框等样式,确保不同浏览器下页面显示一致。
- 页面布局样式:
#content
:设置页面主要内容区域的顶部内边距。#content ul
:设置卡片列表的宽度、居中显示,并使用 Flexbox 布局实现卡片的多行排列。.btnbox
:设置按钮容器的宽度和居中显示。#content ul li
:设置每个卡片项的外边距、宽度和高度。- 卡片样式:
#content ul li a
:设置卡片链接的定位和透视效果,为卡片翻转动画做准备。#content ul li a > div
:设置卡片正反两面的公共样式,包括绝对定位、颜色、溢出隐藏、3D 变换和过渡效果。#content ul li a div:first-child
:设置卡片正面的样式,初始状态为不旋转。#content ul li a div:last-child
:设置卡片背面的样式,初始状态为旋转 180 度。- 卡片翻转动画类:
.rotatey180
:将元素绕 Y 轴旋转 -180 度。.rotateyzero
:将元素绕 Y 轴旋转 0 度。- 按钮样式:设置按钮的背景颜色、边框、文字颜色、内边距等样式,并添加过渡效果和鼠标指针样式。
- 卡片编号样式:设置卡片编号的位置和字体大小。
- 游戏提示信息样式:设置游戏提示信息的居中显示。
三、JavaScript 部分
// 获取页面元素 let content = document.querySelector("#content ul"); let cardList = [ { id: 1, category: "werewolf", name: "小狼人", }, { id: 2, category: "werewolf", name: "小狼人", }, { id: 3, category: "hunter", name: "猎人", }, { id: 4, category: "poor", name: "平民", }, { id: 5, category: "witch", name: "女巫", }, { id: 6, category: "prophet", name: "预言家", }, { id: 7, category: "poor", name: "平民", }, { id: 8, category: "werewolf", name: "黑狼王", }, { id: 9, category: "poor", name: "平民", }, ]; // 初始渲染所有卡片 for (let index = 0; index < cardList.length; index++) { const element = cardList[index]; content.innerHTML += `<li> <a href="javascript:void(0)"> <div class="z"> <img src="./images/card.svg" alt=""> </div> <div class="b"> <h1>${element.name}</h1> </div> </a> </li>`; } // 获取游戏提示信息元素 let gameText = document.querySelector("#gameText"); let color = ["♠", "❤", "♣", "♦"]; // 筛选并渲染狼人卡片的函数 function newHtml() { content.innerHTML = ""; let newcardList = cardList.myarray((item) => item.category == "werewolf"); for (let index = 0; index < newcardList.length; index++) { let randomColor = color[Math.floor(Math.random() * color.length)]; const element = newcardList[index]; content.innerHTML += `<li> <a href="javascript:void(0)"> <div class="z"> <img src="./images/card.svg" alt=""> </div> <div class="b"> <span class='cardnum'>${randomColor} ${element.id} </span> <h1> ${element.name} </h1> </div> </a> </li>`; } gameText.innerHTML = `恭喜你,成功找出${newcardList.length}个狼人!`; } // 获取按钮元素并添加点击事件监听器 let btnbox = document.querySelector(".btn"); btnbox.addEventListener("click", function () { newHtml(); let domb = document.querySelectorAll(".b"); let domz = document.querySelectorAll(".z"); setTimeout(() => { for (let index = 0; index < domb.length; index++) { const dombelement = domb[index]; const domzelement = domz[index]; dombelement.classList.add("rotateyzero"); domzelement.classList.add("rotatey180"); } }, 200); }); // 自定义数组过滤方法 Array.prototype.myarray = function (cb) { let newArr = []; this.forEach((item) => { if (cb(item)) { newArr.push(item); } }); return newArr; };
- 元素选择和数据定义:
content
:通过document.querySelector
方法获取卡片列表元素。cardList
:定义一个包含多个角色对象的数组,每个对象包含角色的id
、category
和name
。- 初始渲染:
- 使用
for
循环遍历cardList
数组,将每个角色的 HTML 结构添加到content
元素中。- 筛选并渲染狼人卡片的函数
newHtml
:
- 清空
content
元素的内容。- 使用自定义的
myarray
方法筛选出category
为werewolf
的角色。- 遍历筛选后的角色数组,为每个角色生成包含随机花色和编号的 HTML 结构,并添加到
content
元素中。- 更新
gameText
元素的内容,显示找到的狼人的数量。- 按钮点击事件处理:
- 获取 “寻找狼人” 按钮元素并添加点击事件监听器。
- 点击按钮时,调用
newHtml
函数筛选并渲染狼人卡片。- 使用
setTimeout
函数延迟 200 毫秒后,为每个卡片的正反两面添加翻转动画类。- 自定义数组过滤方法
myarray
:
- 为
Array.prototype
添加一个自定义方法myarray
,用于过滤数组元素。- 遍历数组,将满足回调函数条件的元素添加到新数组中并返回。
四、工作流程▶️
1. 页面加载:
- 延迟 200 毫秒后,为每个卡片的正反两面添加翻转动画类,实现卡片翻转效果。
2. 卡片翻转动画:
newHtml
函数清空原有卡片内容。- 使用自定义的
myarray
方法筛选出所有狼人角色。- 为每个狼人角色生成包含随机花色和编号的 HTML 结构,并添加到页面中。
- 更新游戏提示信息,显示找到的狼人的数量。
3. 筛选并渲染狼人卡片:
- 用户点击 “寻找狼人” 按钮。
- 触发按钮的点击事件监听器,调用
newHtml
函数。4. 点击按钮:
- HTML 页面加载,引入 CSS 和 JavaScript 文件。
- JavaScript 代码获取页面元素,定义角色数据。
- 初始渲染所有角色卡片。