我正在用JavaScript编写记忆游戏。我制作了一个用于卡片的Web组件<memory-card>
,并制作了一个包含卡片并处理游戏状态<memory-game>
的Web组件。 <memory-card>
类包含其翻转时的图像路径,显示在卡背面的默认图像,翻转状态以及onclick
函数,用于处理状态和图像之间的切换。
<memory-game>
类具有一个setter,该setter接收要从中生成<memory-cards>
的图像数组。在<memory-game>
类中处理游戏状态更新的最佳方法是什么?我应该在此处的<memory-card>
元素上附加一个事件侦听器,还是有更好的解决方法?我希望<memory-card>
元素像现在一样仅处理其自身的功能,即根据单击时的状态更改图像。
memory-game.js
class memoryGame extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
this.cards = []
this.turnedCards = 0
}
flipCard () {
if (this.turnedCards < 2) {
this.turnedCards++
} else {
this.turnedCards = 0
this.cards.forEach(card => {
card.flipCard(true)
})
}
}
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.image = path
this.cards.push(card)
})
}
connectedCallback () {
this.cards.forEach(card => {
this.root.append(card)
})
}
}
customElements.define('memory-game', memoryGame)
memory-card.js
class memoryCard extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
// set default states
this.turned = false
this.path = 'image/0.png'
this.root.innerHTML = `<img src="${this.path}"/>`
this.img = this.root.querySelector('img')
}
set image (path) {
this.path = path
}
flipCard (turnToBack = false) {
if (this.turned || turnToBack) {
this.turned = false
this.img.setAttribute('src', 'image/0.png')
} else {
this.turned = true
this.img.setAttribute('src', this.path)
}
}
connectedCallback () {
this.addEventListener('click', this.flipCard())
}
}
customElements.define('memory-card', memoryCard)
在Supersharp回答后实施自定义事件
memory-card.js(提取)
connectedCallback () {
this.addEventListener('click', (e) => {
this.flipCard()
const event = new CustomEvent('flippedCard')
this.dispatchEvent(event)
})
}
memory-game.js(提取)
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.addEventListener('flippedCard', this.flipCard.bind(this))
card.image = path
this.cards.push(card)
})
}
答案 0 :(得分:5)
在<memory-card>
中:
CustomEvent()
创建并使用dispatchEvent()
调度自定义事件在<memory-game>
中:
addEventListener()
收听您的自定义事件由于纸牌嵌套在游戏中,因此该事件自然会冒泡到容器中。
这样,这两个自定义元素将保持松散耦合。
答案 1 :(得分:1)
非常很有帮助,以查看一些现有代码以了解您尝试过的内容。但是如果没有它,您可以执行@Supersharp提出的建议,或者可以让<memory-game>
类处理所有事件。
如果您采用这种方式,那么<memory-card>
的代码将在整个字段上监听click
事件。它将检查您是否单击了仍然朝下的卡片,如果是,则告诉卡片翻转。 (通过设置属性或属性,或通过在<memory-card>
元素上调用函数。)
所有其他逻辑将存在于<memory-game>
类中,以确定所选择的两个卡是否相同并分配点等。
如果您希望卡处理click
事件,那么您将使该代码生成一个新的CustomEvent
来指示卡已翻转。可能包括卡在网格内的坐标和要翻转的卡的类型。
然后,<memory-game>
类将侦听flipped
事件并根据该信息采取行动。
但是,这并不是一个真正的问题。这就是您要编写代码的方式以及代码的绑定方式。如果您从不打算在任何其他游戏中使用此代码,那就没关系了。
答案 2 :(得分:1)
Supersharps答案不是100%正确。
click
事件使DOM泛滥,
但是CustomEvents 不要
Why firing a defined event with dispatchEvent doesn't obey the bubbling behavior of events?
因此,您必须自己添加bubbles:true
:
[yoursender].dispatchEvent(new CustomEvent([youreventName], {
bubbles: true,
detail: [yourdata]
}));
更多:https://javascript.info/dispatch-events
this.cards.forEach(card => {
card.flipCard(true)
})
首先不需要this.cards
,因为[...this.children]
中所有卡都可用
!请记住,在JavaScript对象中是通过引用传递的,因此您的this.cards
指向确切的相同 DOM子级
您在这里有依赖性,
游戏需要了解Card中的.flipCard
方法。
►让您的记忆游戏发送一张每张卡都会收到的事件
提示:每张卡都需要在游戏DOM级别“监听”才能接收冒泡事件
在我的代码中,整个循环是:
game.emit('allCards','close');
卡负责监听正确的EventListener
(附加到card.parentNode
)
这样一来,游戏中有多少张(或什么)卡都没有关系
如果您的游戏不再关心它拥有多少个DOM子代,
而且它不会对已有元素进行任何记账,
洗牌变成小菜一碟:
shuffle() {
console.log('► Shuffle DOM children');
let game = this,
cards = [...game.children],//create Array from a NodeList
idx = cards.length;
while (idx--) game.insertBefore(rand(cards), rand(cards));//swap 2 random DOM elements
}
我的全局rand函数,从数组或数字中产生一个随机值
rand = x => Array.isArray(x) ? x[rand(x.length)] : 0 | x * Math.random(),
如果正确编写了基于事件的程序,
然后用三张匹配卡创建记忆游戏是另一回事
..或4 ...或N个匹配卡