Js游戏01-Memory_Card

来自codepen,学习游戏制作的思想。
链接
01

简介

点击,匹配相同的卡片。

思路,css动画处理翻转,点击匹配是否有相同的id,有就固定住,背景变色提示。

预备知识

使用jquery

this.$game = $(".game");// 获取dom节点

this.cardsArray = $.merge(cards, cards);
合并两个数组
返回的结果会修改第一个数组的内容——第一个数组的元素后面跟着第二个数组的元素。要去除重复项,请使用$.unique()

this.$game.html(this.html);
使用 .html() 方法来获取任意一个元素的内容。 如果选择器匹配多于一个的元素,那么只有第一个匹配元素的 HTML 内容会被获取
写入内容!!

this.$memoryCards.on("click", this.cardClicked);
on()方法绑定事件处理程序到当前选定的jQuery对象中的元素。

this.$restartButton.on("click", $.proxy(this.reset, this));
当有事件处理函数要附加到元素上,但他们的作用域实际是指向另一个对象时,这个方法最有用了
第一个参数是要设定的作用域对象。第二个参数是将要设置作用域的函数名(必须是第一个作用域对象的一个属性)。

$card.find(".inside")
搜索所有与指定表达式匹配的元素。这个函数是找出正在处理的元素的后代元素的好方法。

hasClass("matched")
检查当前的元素是否含有某个特定的类,如果有,则返回true。
这其实就是 is(“.” + class)。

$(".picked").addClass("matched");
为每个匹配的元素添加指定的类名。

$(".picked").removeClass("picked");
从所有匹配的元素中删除全部或者指定的类。

$(this).attr("data-id")
获取匹配的元素集合中的第一个元素的属性的值 或 设置每一个匹配元素的一个或多个属性。

this.$overlay.show();
显示隐藏的匹配元素。
如果选择的元素是可见的,这个方法将不会改变任何东西。
无论这个元素是通过hide()方法隐藏的还是在CSS里设置了display:none;,这个方法都将有效。

this.$overlay.hide();
隐藏显示的元素
这个就是 ‘hide( speed, [callback] )’ 的无动画版。如果选择的元素是隐藏的,这个方法将不会改变任何东西。

this.$modal.fadeIn("slow");
通过不透明度的变化来实现所有匹配元素的淡入效果,并在动画完成后可选地触发一个回调函数。
这个动画只调整元素的不透明度,也就是说所有匹配的元素的高度和宽度不会发生变化。

Memory.$game.fadeOut();
通过不透明度的变化来实现所有匹配元素的淡出效果,并在动画完成后可选地触发一个回调函数。
这个动画只调整元素的不透明度,也就是说所有匹配的元素的高度和宽度不会发生变化。

总结:
获取dom节点 - 可以处理页面
.html - 获取节点信息
.on - 处理交互

.find - 查找元素 - 找出正在处理的元素的后代元素
.attr - 获取匹配的元素集合中的第一个元素的属性的值

.hasClass - 检查当前的元素是否含有某个特定的类 - 两个卡片的匹配 - 一个match 一个picked,就可以继续比较id了。
.addClass - 给当前元素添加类 - 动画效果
.removeClass - 移出动画效果。

.show - 显示其他div盒子。
.hide - 隐藏盒子
.fadeIn - 优化细节。淡入
.fadeOut - 淡出


实现细节

结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="wrap">
<div class="game"></div> // 写入页面

// js创建 - 根据数组的长度 - 返回字符串 - 然后解析 - 写入页面。
// 每个创建一个div
// 结构 div.card - div.inside - div.front + div.back - img

// 翻转 - 正面(有图片) / 背面(盖牌)

buildHTML: function() {
var frag = '';
// 每个创建一个div
// 结构 div.card - div.inside - div.front + div.back - img
this.$cards.each(function(k, v) {
frag += '<div class="card" data-id="' + v.id + '"><div class="inside">\
<div class="front"><img src="' + v.img + '"\
alt="' + v.name + '" /></div>\
<div class="back"><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/codepen-logo.png"\
alt="Codepen" /></div></div>\
</div>';
});
return frag;
}

样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* // 总 */
.wrap {
position: relative;
height: 100%;
min-height: 500px; /*最小高度*/
padding-bottom: 20px;
}

/* // 存放卡片 */
.game {
transform-style: preserve-3d;
perspective: 500px;
min-height: 100%;
height: 100%;
}

// 匹配成功动画 - 背景颜色。从有颜色 - 白色 - 回归原样。
// 兼容性!
@-webkit-keyframes matchAnim {
0% {
background: #bcffcc;
}

100% {
background: white;
}
}

@keyframes matchAnim {
0% {
background: #bcffcc;
}

100% {
background: white;
}


/* 一行6个 */
.card {
float: left;
width: 16.66666%;
height: 25%;
padding: 5px;
text-align: center;
display: block;
perspective: 500px;
position: relative;
cursor: pointer;
z-index: 50;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

/* 小于800 - 4个 */
@media (max-width: 800px) {
.card {
width: 25%;
height: 16.666%;
}
}


// 处理卡片 正面 / 背面
.card .inside {
width: 100%;
height: 100%;
display: block;
transform-style: preserve-3d; // 让子元素有3D效果
transition: 0.4s ease-in-out;
background: white;
}

/* // 旋转180 - 正面的出现。正面原本 -180, 背面0 */
.card .inside.picked,
.card .inside.matched {
transform: rotateY(180deg); // 翻转正面 front
}

/* 匹配成功效果 - 背景变化 - 再变会原样*/
.card .inside.matched {
-webkit-animation: 1s matchAnim ease-in-out;
animation: 1s matchAnim ease-in-out;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}

//卡片的 正面 / 背面
.card .front,
.card .back {
border: 1px solid black;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
padding: 20px;
}

//
.card .front img,
.card .back img {
max-width: 100%;
display: block;
margin: 0 auto;
max-height: 100%;
}

/* 把正面翻转 到 -180 - 隐藏正面 */
.card .front {
transform: rotateY(-180deg);
}

@media (max-width: 800px) {
.card .front {
padding: 5px;
}
}

// 背面显示
.card .back {
transform: rotateX(0);
}

// 小于800px,缩小图片显示。。
@media (max-width: 800px) {
.card .back {
padding: 10px;
}
}

逻辑

引入资源
https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js

定义数据集 - 用于创建card

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
var cards = [{
name: "php",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/php-logo_1.png",
id: 1,
},
{
name: "css3",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/css3-logo.png",
id: 2
},
{
name: "html5",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/html5-logo.png",
id: 3
},
{
name: "jquery",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/jquery-logo.png",
id: 4
},
{
name: "javascript",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/js-logo.png",
id: 5
},
{
name: "node",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/nodejs-logo.png",
id: 6
},
{
name: "photoshop",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/photoshop-logo.png",
id: 7
},
{
name: "python",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/python-logo.png",
id: 8
},
{
name: "rails",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/rails-logo.png",
id: 9
},
{
name: "sass",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sass-logo.png",
id: 10
},
{
name: "sublime",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sublime-logo.png",
id: 11
},
{
name: "wordpress",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/wordpress-logo.png",
id: 12
},
];

定义memory对象

1
var Memory = {}

内置函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
init() - 01
初始化,获取dom,合并数据集(两个重复数组 - 匹配),
处理数据,写入页面,处理获取到的数据。

shuffleCards()
调用shuffle() - 打乱数组顺序。。

setup() - 02
调用buildHTML,写入页面,调用绑定事件,设置paused(是否已选中) 和guess(匹配id)

binding() -03
绑定点击事件 - 卡片(绑定类),重置按钮。

cardClicked()
核心:
主要内容:
paused - 判断是否点击过 - 变量
picked - 判断是否点击过 - 类名 - 和 matched匹配 - 同时包含
guess - 和id匹配

判断情况
- 第一次点击 - 设置guess
- 第二次点击成功 - 比较id 同时 当前的元素没有picked类 - 即不是已选中的。
- 第二次点击失败 - 重置guess / paused,翻转回背面
- 每次判断是否全部匹配 - 成功调用 win()

win()
出现条件 - 选中的类的数目 和 数组的长度相同 - 调用显示动画。

showModel()
显示div + 显示过渡效果

hideMode()
隐藏div

reset()
重置。

shuffle()
改变数组的index,然后交换数组内容。返回数组。

buildHTML()
创建card - 字符串拼接,返回字符串后,解析html,写入页面。

完整Js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
(function() {
var Memory = {
init: function(cards) {
this.$game = $(".game");
this.$modal = $(".modal");
this.$overlay = $(".modal-overlay");
this.$restartButton = $("button.restart");
this.cardsArray = $.merge(cards, cards); // 两个数组。
// this.cardsArray = $.merge(cards, cards); // 四个数组。

this.shuffleCards(this.cardsArray); // 先有数据

this.setup(); // 处理内容
},
shuffleCards: function(cardsArray) {
this.$cards = $(this.shuffle(this.cardsArray)); // 随机
},
<!-- 这里什么时候调用的???调用一次! -->
setup: function() {
this.html = this.buildHTML();

// console.log(this.html, '111')

this.$game.html(this.html); // 这里是写入。。。

this.$memoryCards = $(".card"); // 创建之后获取。全部卡片内容
this.paused = false; // 是否可以翻转
this.guess = null; // 匹配id
this.binding(); // 绑定交互事件
},

binding: function() {
this.$memoryCards.on("click", this.cardClicked);
this.$restartButton.on("click", $.proxy(this.reset, this));
},

<!-- 这里每次调用,paused一直变化 -->
cardClicked: function() {
// 核心
var _ = Memory; // 总的。
var $card = $(this); // 当前
// 如果没有固定 && 没有被匹配 && 没有被选中 - 设置选中
if (!_.paused && !$card.find(".inside").hasClass("matched") && !$card.find(".inside")
.hasClass("picked")) {
$card.find(".inside").addClass("picked");// 添加标志类

// 如果当前的为空 - 设置(第一次选中)
if (!_.guess) {
_.guess = $(this).attr("data-id");

// 第二次选中 - 比较id - 如果对的就给它matched标志 - 不对,就不给。
} else if (_.guess == $(this).attr("data-id") && !$(this).hasClass("picked")) {
$(".picked").addClass("matched");
_.guess = null;

// 重置。
} else {
_.guess = null;
_.paused = true;
// 翻转回去。
setTimeout(function() {
$(".picked").removeClass("picked"); //- 删除标志类
Memory.paused = false; // 0.6秒后,可以翻转
}, 600);
}

// 全部匹配 - 弹出成功页面 - 有是否重新开始的选项
if ($(".matched").length == $(".card").length) {
_.win();
}
}
},

win: function() {
this.paused = true;
setTimeout(function() {
Memory.showModal();
Memory.$game.fadeOut();
}, 1000);
},

showModal: function() {
this.$overlay.show();
this.$modal.fadeIn("slow");
},
hideModal: function() {
this.$overlay.hide();
this.$modal.hide();
},

// 重置
reset: function() {
this.hideModal();
this.shuffleCards(this.cardsArray);
this.setup();
this.$game.show("slow");
},

// Fisher--Yates Algorithm -- https://bost.ocks.org/mike/shuffle/

shuffle: function(array) { // 随机
var counter = array.length,
temp, index;
// While there are elements in the array

while (counter > 0) {
// Pick a random index - 随机下标
index = Math.floor(Math.random() * counter);
// Decrease counter by 1
counter--;
// And swap the last element with it - 交换
temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
},

// 这里像JSX。。
buildHTML: function() {
var frag = '';
// 每个创建一个div
// 结构 div.card - div.inside - div.front + div.back - img
this.$cards.each(function(k, v) {
frag += '<div class="card" data-id="' + v.id + '"><div class="inside">\
<div class="front"><img src="' + v.img + '"\
alt="' + v.name + '" /></div>\
<div class="back"><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/codepen-logo.png"\
alt="Codepen" /></div></div>\
</div>';
});
return frag;
}
};
// 起始数据!
var cards = [{
name: "php",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/php-logo_1.png",
id: 1,
},
{
name: "css3",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/css3-logo.png",
id: 2
},
{
name: "html5",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/html5-logo.png",
id: 3
},
{
name: "jquery",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/jquery-logo.png",
id: 4
},
{
name: "javascript",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/js-logo.png",
id: 5
},
{
name: "node",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/nodejs-logo.png",
id: 6
},
{
name: "photoshop",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/photoshop-logo.png",
id: 7
},
{
name: "python",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/python-logo.png",
id: 8
},
{
name: "rails",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/rails-logo.png",
id: 9
},
{
name: "sass",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sass-logo.png",
id: 10
},
{
name: "sublime",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/sublime-logo.png",
id: 11
},
{
name: "wordpress",
img: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/74196/wordpress-logo.png",
id: 12
},
];

Memory.init(cards); // Js的起点!
})(); // 立即执行

总结

收获:
面向对象的第二次练习,对象内设置方法,调用。
初始化数据的设置,然后根据内容创建 dom元素,添加进页面

通过类,id,变量的组合来判断是否相等,是否点击过。

翻牌的效果主要通过样式来实现,翻转180度,分为正面和背面。
然后通过类设置动画效果,该类也可以用作判断。

符合就添加上,不符合,就移除。