Вложенные веб-компоненты и обработка событий - PullRequest
0 голосов
/ 02 января 2019

Я пишу игру памяти в javascript.Я сделал веб-компонент для карточек <memory-card> и веб-компонент для карточек и управления состоянием игры <memory-game>.Класс <memory-card> содержит путь к изображению для его перевернутого изображения, изображение по умолчанию для отображения на оборотной стороне карты, его состояние переворачивания и функцию onclick для переключения между состояниями и изображениями.

У класса <memory-game> есть установщик, который получает массив изображений для генерации <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)

реализацияпользовательское событие после ответа Супершарпа

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)
    })
  }

Ответы [ 3 ]

0 голосов
/ 02 января 2019

Было бы очень полезно увидеть часть вашего существующего кода, чтобы узнать, что вы пробовали.Но без этого вы можете делать то, что предложил @Supersharp, или вы можете иметь класс <memory-game> для обработки всех событий.

Если вы пойдете таким образом, тогда ваш код для <memory-card> будет прослушивать события clickна всем поле.Он проверит, нажали ли вы на карту, которая все еще лицевой стороной вниз, и, если так, скажите, чтобы карта перевернулась.(Либо путем установки свойства или атрибута, либо путем вызова функции для элемента <memory-card>.)

Вся остальная логика будет существовать в классе <memory-game>, чтобы определить, выбраны ли двакарты одинаковы и присваивают баллы и т. д.

Если вы хотите, чтобы карты обрабатывали событие click, тогда вы должны сгенерировать новый код CustomEvent, указывающий, что карта перевернулась.Возможно, включая координаты карты в сетке и тип переворачиваемой карты.

Класс <memory-game> будет затем прослушивать событие flipped и действовать на основании этой информации.

Как бы то ни было, это не проблема.Это просто то, как вы хотите закодировать его и как вы хотите связать код вместе.Если вы никогда не планируете использовать этот код в каких-либо других играх, это не имеет большого значения.

0 голосов
/ 09 января 2019

Ответ Супершарпса не на 100% правильный.

click события всплывают в DOM, но CustomEvents (внутри shadowDOM) не

Почему запуск определенного события с dispatchEvent не подчиняется всплывающему поведению событий?

Так что вы должны сами добавить bubbles:true:

[yoursender].dispatchEvent(new CustomEvent([youreventName], {
                                                  bubbles: true,
                                                  detail: [yourdata]
                                                }));

больше: https://javascript.info/dispatch-events

примечание: detail может быть функцией: Как взаимодействовать между веб-компонентами (собственный пользовательский интерфейс)?

Для задач программирования на основе событий

   this.cards.forEach(card => {
    card.flipCard(true)
  })

Прежде всего, this.cards не требуется, так как все карты доступны в [...this.children]

!! Помните, в JavaScript объекты передаются по ссылке, поэтому ваш this.cards указывает на точное то же самое DOM потомков

У вас здесь есть зависимость,
Игра должна знать о методе .flipCard в Карте.

► Заставьте вашу игру памяти отправлять ОДНО событие, полученное КАЖДОЙ картой

подсказка: каждая карта должна «слушать» на уровне Game DOM, чтобы получить всплывающее событие

в моем коде весь цикл:

game.emit('allCards','close');

Карты несут ответственность за прослушивание правильного EventListener
(прилагается к card.parentNode)

Таким образом, не имеет значения, сколько (или что еще) карт в вашей игре

DOM - это ваша структура данных

Если ваша игра больше не заботится о том, сколько у нее детей или у каких детей 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 подходящих карт

0 голосов
/ 02 января 2019

В <memory-card>:

  • Создать с CustomEvent() и отправить пользовательское событие с dispatchEvent()

В <memory-game>:

  • Прослушайте ваше пользовательское событие с помощью addEventListener()

Поскольку карты вложены в игру, событие естественно будет пузыриться в контейнер.

Таким образом2 пользовательских элемента останутся без изменений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...