Решение проблемы управления эмуляцией в EmulatorJS с помощью iframe в Nuxt.js
Привет! Меня зовут Василий, я Frontend-разработчик с опытом работы на Vue.js и Nuxt.js, основатель игрового сервиса JoystickLab. Эта статья посвящена решению технической задачи, возникшей при работе над проектом JoystickLab.ru — платформой для эмуляции игр, реализованной на Nuxt.js. В процессе интеграции библиотеки EmulatorJS была выявлена проблема: эмуляция не останавливалась при переходе между страницами. Здесь описан способ, как удалось решить данную проблему, с использованием iframe, а также приведён пример кода.
Постановка проблемы
При использовании EmulatorJS на сайте JoystickLab.ru возникала следующая ситуация: после запуска игры на одной странице и последующего перехода на другую страницу эмуляция не останавливалась. Это проявлялось следующим образом:
Игра оставалась активной: персонажи продолжали двигаться, враги атаковали, а игровой процесс шёл своим чередом, несмотря на то, что страница с игрой больше не была открыта.
Звук не отключался: музыка и звуковые эффекты из игры продолжали играть в фоновом режиме.
Процессы эмуляции не прерывались: рендеринг графики, обработка ввода и другие связанные с эмуляцией задачи оставались активными.
Такое поведение создавало впечатление, что игра "живёт своей жизнью" даже после того, как пользователь покинул страницу. Это особенно нежелательно в одностраничных приложениях (Single Page Application (SPA)), таких как Nuxt.js, где переходы между страницами не сопровождаются полной перезагрузкой браузера, и пользователь ожидает, что эмуляция завершится при уходе со страницы игры.
Причина проблемы
Проблема связана с особенностями работы EmulatorJS. Библиотека запускает эмуляцию через JavaScript с использованием WebAssembly и canvas, но не имеет встроенного механизма для её остановки при уходе со страницы. В контексте Nuxt.js, который использует компоненты Vue.js с их жизненным циклом (onMounted, onUnmounted), это приводит к следующим сложностям:
Компоненты управляются через жизненный цикл, но эмуляция не привязана к этим хукам напрямую.
При навигации компонент уничтожается, однако процессы эмуляции остаются активными в глобальной области браузера.
Таким образом, EmulatorJS не отслеживает, что страница больше не активна, и продолжает работать в фоне.
Подход к решению
Для решения задачи был выбран метод с использованием iframe. Этот элемент позволяет изолировать эмуляцию и управлять её жизненным циклом. Основные преимущества такого подхода:
Изоляция: iframe создаёт отдельный контекст выполнения, отделяя эмуляцию от основного приложения.
Управление: при удалении iframe из DOM (например, при уходе со страницы) все процессы внутри него завершаются.
Универсальность: метод работает с Nuxt.js без необходимости сложных изменений.
Реализация
Решение было реализовано следующим образом:
В компонент добавлен элемент <iframe>.
В iframe динамически внедряется HTML-код и скрипты для работы EmulatorJS.
При уничтожении компонента iframe удаляется, что останавливает эмуляцию.
Пример кода
Вот упрощённый пример реализации в Nuxt.js:
Как это работает
<iframe>: Добавляется в шаблон и управляется через ref.
Инъекция кода: Функция injectGameScript создаёт структуру внутри iframe, включая настройки EmulatorJS.
Жизненный цикл: При уходе со страницы компонент уничтожается, iframe удаляется, и эмуляция останавливается.
Преимущества
Этот подход показал свою эффективность:
Изоляция процессов: Эмуляция не влияет на основное приложение.
Автоматическая остановка: Удаление iframe завершает все связанные процессы.
Гибкость: Метод подходит для разных платформ эмуляции (NES, SNES, PSX, N64 и т.д.).
Интеграция в проект
В JoystickLab.ru решение дополнено:
- Динамическими настройками для разных платформ (например, BIOS или fullscreen-режим).
Поддержкой мобильных устройств через открытие эмуляции в новом окне.
Стилизацией интерфейса внутри iframe.
Заключение
Использование iframe позволило решить проблему управления эмуляцией в EmulatorJS при работе с Nuxt.js. Такой подход обеспечивает контроль над жизненным циклом ресурсов и удобство для пользователей. Если у вас есть вопросы или идеи по улучшению, буду рад обсудить!