💂 个人网站: 海拥 —— 一个乐于分享技术与快乐的博主 🤟 版权: 本文由【海拥】原创、在CSDN首发、需要转载请联系博主 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 💅 想寻找共同摸鱼的小伙伴,请点击【摸鱼小游戏 】
去评论区领书
这节实验我们将使用 HTML、CSS 和 JavaScript 制作纸牌记忆游戏。
让我们开始吧!
在线演示戳这里👉 https:///lanqiao/17.html
知识点
animation-duration 属性 backface-visibility 属性 visibility 属性 animation-timing-function 属性
HTML 用户界面
HTML 代码用于设计项目的基本结构,其中包含了一个 h1
标题,分数、星级和游戏时间面板,纸牌卡片列表以及获胜时的恭喜面板。
<! doctype html >
< html lang = " en" >
< head>
< meta charset = " utf-8" >
< title> 实验十七 纸牌记忆游戏</ title>
< meta name = " description" content = " " >
< meta name = " viewport" content = " width=device-width, initial-scale=1" >
< link rel = " stylesheet" href = " style.css" >
<!-- 导入bootstrap以及字体图标等样式 -->
< link rel = " stylesheet prefetch" href = " https://maxcdn./font-awesome/4.6.1/css/font-awesome.min.css" >
</ head>
< body>
< div class = " container" >
< header>
< h1> 纸牌记忆游戏</ h1>
</ header>
< section class = " score-panel" >
< ul class = " stars" >
< li> < i class = " fa fa-star" > </ i> </ li>
< li> < i class = " fa fa-star" > </ i> </ li>
< li> < i class = " fa fa-star" > </ i> </ li>
</ ul>
< span class = " moves" > 0</ span>
< div class = " timer" > </ div>
< div class = " restart" onclick = " startGame ( ) " >
< i class = " fa fa-repeat" > </ i>
</ div>
</ section>
< ul class = " deck" id = " card-deck" >
< li class = " card" type = " diamond" > < i class = " fa fa-diamond" > </ i> </ li>
< li class = " card" type = " plane" > < i class = " fa fa-paper-plane-o" > </ i> </ li>
< li class = " card match" type = " anchor" > < i class = " fa fa-anchor" > </ i> </ li>
< li class = " card" type = " bolt" > < i class = " fa fa-bolt" > </ i> </ li>
< li class = " card" type = " cube" > < i class = " fa fa-cube" > </ i> </ li>
< li class = " card match" type = " anchor" > < i class = " fa fa-anchor" > </ i> </ li>
< li class = " card" type = " leaf" > < i class = " fa fa-leaf" > </ i> </ li>
< li class = " card" type = " bicycle" > < i class = " fa fa-bicycle" > </ i> </ li>
< li class = " card" type = " diamond" > < i class = " fa fa-diamond" > </ i> </ li>
< li class = " card" type = " bomb" > < i class = " fa fa-bomb" > </ i> </ li>
< li class = " card" type = " leaf" > < i class = " fa fa-leaf" > </ i> </ li>
< li class = " card" type = " bomb" > < i class = " fa fa-bomb" > </ i> </ li>
< li class = " card open show" type = " bolt" > < i class = " fa fa-bolt" > </ i> </ li>
< li class = " card" type = " bicycle" > < i class = " fa fa-bicycle" > </ i> </ li>
< li class = " card" type = " plane" > < i class = " fa fa-paper-plane-o" > </ i> </ li>
< li class = " card" type = " cube" > < i class = " fa fa-cube" > </ i> </ li>
</ ul>
< div id = " popup1" class = " overlay" >
< div class = " popup" >
< h2> 恭喜 🎉</ h2>
< a class = " close" href = " #" > ×</ a>
< div class = " content-1" >
恭喜你获得了胜利 🎉🎉
</ div>
< div class = " content-2" >
< p> 你在< span id = " totalTime" > </ span> 内 </ p>
< p> 移动了< span id = " finalMove" > </ span> 次 </ p>
< p> 星级: < span id = " starRating" > </ span> </ p>
</ div>
< button id = " play-again" onclick = " playAgain ( ) " >
再玩一次 😄</ a>
</ button>
</ div>
</ div>
</ div>
< script src = " script.js" > </ script>
</ body>
</ html>
CSS 部分
现在我们使用一些 CSS 属性来设置记忆纸牌游戏的样式。
一些基本样式
html {
box-sizing : border-box;
}
*,
*::before,
*::after {
box-sizing : inherit;
}
html,
body {
width : 100%;
height : 100%;
margin : 0;
padding : 0;
font-weight : bolder;
}
body {
background : #ffffff;
font-size : 16px;
}
.container {
display : flex;
justify-content : center;
align-items : center;
flex-direction : column;
}
h1 {
font-family : 'Gloria Hallelujah' , cursive;
}
纸牌的样式
.deck {
width : 85%;
background : #716F71;
padding : 1rem;
border-radius : 4px;
box-shadow : 8px 9px 26px 0 rgba ( 46, 61, 73, 0.5) ;
display : flex;
flex-wrap : wrap;
justify-content : space-around;
align-items : center;
margin : 0 0 3em;
}
.deck .card {
height : 3.7rem;
width : 3.7rem;
margin : 0.2rem 0.2rem;
background : #141214; ;
font-size : 0;
color : #ffffff;
border-radius : 5px;
cursor : pointer;
display : flex;
justify-content : center;
align-items : center;
box-shadow : 5px 2px 20px 0 rgba ( 46, 61, 73, 0.5) ;
}
.deck .card.open {
transform : rotateY ( 0) ;
background : #02b3e4;
cursor : default;
animation-name : flipInY;
-webkit-backface-visibility : visible;
backface-visibility : visible;
animation-duration : .75s;
}
.deck .card.show {
font-size : 33px;
}
.deck .card.match {
cursor : default;
background : #E5F720;
font-size : 33px;
animation-name : rubberBand;
-webkit-backface-visibility : visible;
backface-visibility : visible;
animation-duration : .75s;
}
.deck .card.unmatched {
animation-name : pulse;
-webkit-backface-visibility : visible;
backface-visibility : visible;
animation-duration : .75s;
background : #e2043b;
}
.deck .card.disabled {
pointer-events : none;
opacity : 0.9;
}
animation-duration
属性定义动画完成一个周期需要多少秒或毫秒。这里的.75s
表示 0.75 秒。backface-visibility
属性定义当元素背面向屏幕时是否可见。这里的 visible
值使得背面是可见的。
分数面板的样式
.score-panel {
text-align : left;
margin-bottom : 10px;
}
.score-panel .stars {
margin : 0;
padding : 0;
display : inline-block;
margin : 0 5px 0 0;
}
.score-panel .stars li {
list-style : none;
display : inline-block;
}
.score-panel .restart {
float : right;
cursor : pointer;
}
.fa-star {
color : #FFD700;
}
.timer {
display : inline-block;
margin : 0 1rem;
}
祝贺面板的样式
.overlay {
position : fixed;
top : 0;
bottom : 0;
left : 0;
right : 0;
background : rgba ( 0, 0, 0, 0.7) ;
transition : opacity 500ms;
visibility : hidden;
opacity : 0;
}
.overlay:target {
visibility : visible;
opacity : 1;
}
.popup {
margin : 70px auto;
padding : 20px;
background : #ffffff;
border-radius : 5px;
width : 85%;
position : relative;
transition : all 5s ease-in-out;
}
.popup h2 {
margin-top : 0;
color : #333;
font-family : Tahoma, Arial, sans-serif;
}
.popup .close {
position : absolute;
top : 20px;
right : 30px;
transition : all 200ms;
font-size : 30px;
font-weight : bold;
text-decoration : none;
color : #333;
}
.popup .close:hover {
color : #E5F720;
}
.popup .content-1,
.content-2 {
max-height : 30%;
overflow : auto;
text-align : center;
}
.show {
visibility : visible;
opacity : 100;
}
#starRating li {
display : inline-block;
}
#play-again {
background-color : #141214;
padding : 0.7rem 1rem;
font-size : 1.1rem;
display : block;
margin : 0 auto;
width : 50%;
font-family : 'Gloria Hallelujah' , cursive;
color : #ffffff;
border-radius : 5px;
}
visibility
属性指定一个元素是否是可见的。
动画
/* 卡片打开时的动画 */
@keyframes flipInY {
from {
transform : perspective ( 400px) rotate3d ( 0, 1, 0, 90deg) ;
animation-timing-function : ease-in;
opacity : 0;
}
40% {
transform : perspective ( 400px) rotate3d ( 0, 1, 0, -20deg) ;
animation-timing-function : ease-in;
}
60% {
transform : perspective ( 400px) rotate3d ( 0, 1, 0, 10deg) ;
opacity : 1;
}
80% {
transform : perspective ( 400px) rotate3d ( 0, 1, 0, -5deg) ;
}
to {
transform : perspective ( 400px) ;
}
}
animation-timing-function
指定动画将如何完成一个周期,这里的 ease-in
是让动画以低速开始。
/* 卡片匹配时的动画 */
@keyframes rubberBand {
from {
transform : scale3d ( 1, 1, 1) ;
}
30% {
transform : scale3d ( 1.25, 0.75, 1) ;
}
40% {
transform : scale3d ( 0.75, 1.25, 1) ;
}
50% {
transform : scale3d ( 1.15, 0.85, 1) ;
}
65% {
transform : scale3d ( .95, 1.05, 1) ;
}
75% {
transform : scale3d ( 1.05, .95, 1) ;
}
to {
transform : scale3d ( 1, 1, 1) ;
}
}
/* 卡片不匹配时的动画 */
@keyframes pulse {
from {
transform : scale3d ( 1, 1, 1) ;
}
50% {
transform : scale3d ( 1.2, 1.2, 1.2) ;
}
to {
transform : scale3d ( 1, 1, 1) ;
}
}
媒体查询
/* 适用于 320px 以下的样式*/
@media ( max-width : 320px) {
.deck {
width : 85%;
}
.deck .card {
height : 4.7rem;
width : 4.7rem;
}
}
/* 适用于 768px 以上的样式*/
@media ( min-width : 768px) {
.container {
font-size : 22px;
}
.deck {
width : 660px;
height : 680px;
}
.deck .card {
height : 125px;
width : 125px;
}
.popup {
width : 60%;
}
}
JavaScript 部分
接下来让我们添加 Javascript
首先声明一些我们需要用到的变量:
// 卡片数组包含所有卡片
let card = document. getElementsByClassName ( "card" ) ;
let cards = [ ... card] ;
// 游戏中所有卡片
const deck = document. getElementById ( "card-deck" ) ;
// 声明 moves 变量
let moves = 0 ;
let counter = document. querySelector ( ".moves" ) ;
// 声明星形图标的变量
const stars = document. querySelectorAll ( ".fa-star" ) ;
// 声明 matchedCard 的变量
let matchedCard = document. getElementsByClassName ( "match" ) ;
// 星级列表
let starsList = document. querySelectorAll ( ".stars li" ) ;
// 模板中的关闭图标
let closeicon = document. querySelector ( ".close" ) ;
// 声明 modal
let modal = document. getElementById ( "popup1" )
// 打开卡片的数组
var openedCards = [ ] ;
洗牌功能
function shuffle ( array ) {
var currentIndex = array. length, temporaryValue, randomIndex;
while ( currentIndex !== 0 ) {
randomIndex = Math. floor ( Math. random ( ) * currentIndex) ;
currentIndex -= 1 ;
temporaryValue = array[ currentIndex] ;
array[ currentIndex] = array[ randomIndex] ;
array[ randomIndex] = temporaryValue;
}
return array;
} ;
开始新游戏的功能
// 页面刷新/加载时洗牌
document. body. onload = startGame ( ) ;
// 开始新游戏的功能
function startGame ( ) {
// 清空 openCards 数组
openedCards = [ ] ;
// 洗牌
cards = shuffle ( cards) ;
// 从每张卡片中删除所有现有的类
for ( var i = 0 ; i < cards. length; i++ ) {
deck. innerHTML = "" ;
[ ] . forEach . call ( cards, function ( item ) {
deck. appendChild ( item) ;
} ) ;
cards[ i] . classList. remove ( "show" , "open" , "match" , "disabled" ) ;
}
// 重置 moves
moves = 0 ;
counter. innerHTML = moves;
// 重置 rating
for ( var i= 0 ; i < stars. length; i++ ) {
stars[ i] . style. color = "#FFD700" ;
stars[ i] . style. visibility = "visible" ;
}
// 重置 timer
second = 0 ;
minute = 0 ;
hour = 0 ;
var timer = document. querySelector ( ".timer" ) ;
timer. innerHTML = "0 分 0 秒" ;
clearInterval ( interval) ;
}
显示卡片的功能
var displayCard = function ( ) {
this . classList. toggle ( "open" ) ;
this . classList. toggle ( "show" ) ;
this . classList. toggle ( "disabled" ) ;
} ;
将打开的卡片添加到 OpenedCards 列表并检查卡片是否匹配
function cardOpen ( ) {
openedCards. push ( this ) ;
var len = openedCards. length;
if ( len === 2 ) {
moveCounter ( ) ;
if ( openedCards[ 0 ] . type === openedCards[ 1 ] . type) {
matched ( ) ;
} else {
unmatched ( ) ;
}
}
} ;
当卡片匹配时的功能
function matched ( ) {
openedCards[ 0 ] . classList. add ( "match" , "disabled" ) ;
openedCards[ 1 ] . classList. add ( "match" , "disabled" ) ;
openedCards[ 0 ] . classList. remove ( "show" , "open" , "no-event" ) ;
openedCards[ 1 ] . classList. remove ( "show" , "open" , "no-event" ) ;
openedCards = [ ] ;
}
当卡片不匹配时的功能
function unmatched ( ) {
openedCards[ 0 ] . classList. add ( "unmatched" ) ;
openedCards[ 1 ] . classList. add ( "unmatched" ) ;
disable ( ) ;
setTimeout ( function ( ) {
openedCards[ 0 ] . classList. remove ( "show" , "open" , "no-event" , "unmatched" ) ;
openedCards[ 1 ] . classList. remove ( "show" , "open" , "no-event" , "unmatched" ) ;
enable ( ) ;
openedCards = [ ] ;
} , 1100 ) ;
}
暂时禁用卡片的功能
function disable ( ) {
Array . prototype. filter . call ( cards, function ( card ) {
card. classList. add ( 'disabled' ) ;
} ) ;
}
启用卡片并禁用匹配的卡片的功能
function enable ( ) {
Array . prototype. filter . call ( cards, function ( card ) {
card. classList. remove ( 'disabled' ) ;
for ( var i = 0 ; i < matchedCard. length; i++ ) {
matchedCard[ i] . classList. add ( "disabled" ) ;
}
} ) ;
}
计算玩家的动作的功能
function moveCounter ( ) {
moves++ ;
counter. innerHTML = moves;
// 第一次点击时启动计时器
if ( moves == 1 ) {
second = 0 ;
minute = 0 ;
hour = 0 ;
startTimer ( ) ;
}
// 根据移动次数设置星级
if ( moves > 8 && moves < 12 ) {
for ( i= 0 ; i < 3 ; i++ ) {
if ( i > 1 ) {
stars[ i] . style. visibility = "collapse" ;
}
}
}
else if ( moves > 13 ) {
for ( i= 0 ; i < 3 ; i++ ) {
if ( i > 0 ) {
stars[ i] . style. visibility = "collapse" ;
}
}
}
}
显示游戏的时间
//初始化变量
var second = 0 , minute = 0 ; hour = 0 ;
var timer = document. querySelector ( ".timer" ) ;
var interval;
//计时功能
function startTimer ( ) {
interval = setInterval ( function ( ) {
timer. innerHTML = minute+ " 分" + second+ " 秒" ;
second++ ;
if ( second == 60 ) {
minute++ ;
second= 0 ;
}
if ( minute == 60 ) {
hour++ ;
minute = 0 ;
}
} , 1000 ) ;
}
当所有卡片都匹配正确时展示恭喜界面,显示移动次数时间和等级
function congratulations ( ) {
if ( matchedCard. length == 16 ) {
clearInterval ( interval) ;
finalTime = timer. innerHTML;
// 显示祝贺界面
modal. classList. add ( "show" ) ;
// 声明星级变量
var starRating = document. querySelector ( ".stars" ) . innerHTML;
// 显示移动、评级、时间
document. getElementById ( "finalMove" ) . innerHTML = moves;
document. getElementById ( "starRating" ) . innerHTML = starRating;
document. getElementById ( "totalTime" ) . innerHTML = finalTime;
//界面上的关闭图标
closeModal ( ) ;
} ;
}
// 界面上的关闭图标
function closeModal ( ) {
closeicon. addEventListener ( "click" , function ( e ) {
modal. classList. remove ( "show" ) ;
startGame ( ) ;
} ) ;
}
再次游戏功能
function playAgain ( ) {
modal. classList. remove ( "show" ) ;
startGame ( ) ;
}
// 循环以将事件侦听器添加到每张卡片
for ( var i = 0 ; i < cards. length; i++ ) {
card = cards[ i] ;
card. addEventListener ( "click" , displayCard) ;
card. addEventListener ( "click" , cardOpen) ;
card. addEventListener ( "click" , congratulations) ;
} ;
到这里我们的记忆纸牌游戏就做好了,下面我给出了完整的源代码,同学们可以下载下来玩玩试试:https://download.csdn.net/download/qq_44273429/85083128
实验总结
相信通过上面的教程,大家已经学会了如何使用 JavaScript 构建纸牌记忆游戏。同时我们又学习/复习了一些知识,如:animation-duration 属性、visibility
属性和 animation-timing-function
属性等。
同学们也动起手来做一个纸牌记忆游戏吧
🥇 评论区抽粉丝送书啦
💌 欢迎大家在评论区提出意见和建议! (抽三位幸运儿送书,实物图如下)💌
《Vue.js全家桶零基础入门到进阶项目实战》
【内容简介】
Vue.js 是一套构建用户界面的渐进式框架,本书旨在帮助读者全面掌握 Vue.js 全家桶技术和单页面前后端分离项目开发,理解 MVVM 框架思想,让前端和后端开发人员快速精通 Vue.js 全家桶技术。 本书贯穿入门准备实操、基础核心案例、中级进阶实战、综合进阶项目进行讲解,循序渐进、环环相扣,通俗易懂,并分析为什么这样使用,让你知其所以然。包含的主要技术:NPM/CNPM、VS Code、Vue.js、 MVVM、Axios、Vue Router、webpack、ECMAScript 6、Vue Loader、Vue CLI、Element UI、Vuex、 Mock.js、Easy Mock、ECharts 、Promise、拦截器、组件通信、跨域问题、上线部署等。 本书适合有 HTML、CSS、JavaScript 基础的 Vue.js 零基础小白、前端开发人员、后端开发人员。同时,也适合以下人员阅读:在校生,需要掌握流行的新技术,做到与职场同步;在职人员,工作中需要学习使用Vue;有基础学员,需要系统、全面、高效使用 Vue 技术。
也有不想靠抽,想自己买的同学可以参考下面的链接
京东自营购买链接:
《Vue.js全家桶零基础入门到进阶项目实战》- 京东图书
当当自营购买链接:
《Vue.js全家桶零基础入门到进阶项目实战》- 当当图书
📣 注意:
大家点赞关注,三天后也就是 4月9日 从评论区留言的同学中抽取两位送书
🌊 面试题库:Java、Python、前端核心知识点大全和面试真题资料 🌊 办公用品:精品PPT模板几千套,简历模板一千多套 🌊 学习资料:2300套PHP建站源码,微信小程序入门资料
如果中奖了联系不上则视为放弃,可以从下方卡片里找到作者的联系方式,每周都会送6-9本书,后面送书力度还会加大,一年送几百上千本不是问题,回复【进群】领书不迷路,群内 每位成员 我都会送一本。回复【资源】可获取上面的资料👇🏻👇🏻👇🏻