From afca83bbcc9f9121463f84ec5940ed8280fbab1d Mon Sep 17 00:00:00 2001 From: Arianrhod Sandlot Date: Wed, 11 Oct 2023 21:03:58 +0800 Subject: [PATCH] feat: add support for setting style, update default style --- demo/demo.css | 4 ++-- docs/src/content/docs/apis/launch.md | 22 ++++++++++++++++++ src/emulator.ts | 20 ++++++++++++---- src/nostalgist.ts | 34 ++++++++++++++++++++-------- src/types/emulator-options.ts | 9 ++++++++ src/types/nostalgist-options.ts | 28 ++++++++++++++++++++++- tests/integration/nostalgist.spec.ts | 11 +++++++++ 7 files changed, 111 insertions(+), 17 deletions(-) diff --git a/demo/demo.css b/demo/demo.css index 49ad966..536c48c 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -8,6 +8,6 @@ } canvas { - width: 800px; - height: 600px; + /* width: 800px; + height: 600px; */ } diff --git a/docs/src/content/docs/apis/launch.md b/docs/src/content/docs/apis/launch.md index 9d08182..39aa58f 100644 --- a/docs/src/content/docs/apis/launch.md +++ b/docs/src/content/docs/apis/launch.md @@ -80,6 +80,28 @@ const nostalgist = await Nostalgist.launch({ }) ``` + + #### `style` + + **type:** `Object` + + If the canvas element is created automatically, the style will be + + ```js + { + position: 'fixed', + top: '0', + left: '0', + width: '100%', + height: '100%', + backgroundColor: 'black', + zIndex: '1', + } + ``` + + otherwise it will be `undefined`. + + The CSS rule name should be "camelCase" instead of "kebab-case". For example, `{ backgroundColor: 'black' }` is valid, but `{ background-color: '' }` is not. + + #### `size` **type:** `'auto' | { width: number, height: number }` **default:** `'auto'` diff --git a/src/emulator.ts b/src/emulator.ts index f213ec6..bc59484 100644 --- a/src/emulator.ts +++ b/src/emulator.ts @@ -5,7 +5,7 @@ import { coreFullNameMap } from './constants' import { createEmscriptenFS, getEmscriptenModuleOverrides } from './emscripten' import type { EmulatorOptions } from './types/emulator-options' import type { RetroArchCommand } from './types/retroarch-command' -import { blobToBuffer } from './utils' +import { blobToBuffer, updateStyle } from './utils' const encoder = new TextEncoder() @@ -56,8 +56,15 @@ export class Emulator { this.setupRaConfigFile() this.setupRaCoreConfigFile() - if (this.options.waitForInteraction) { - this.options.waitForInteraction({ + const { element, style, waitForInteraction } = this.options + updateStyle(element, style) + if (!element.isConnected) { + document.body.append(element) + } + const size = this.getElementSize() + + if (waitForInteraction) { + waitForInteraction({ done: () => { this.runMain() }, @@ -65,7 +72,7 @@ export class Emulator { } else { this.runMain() } - const { size } = this.options + if (typeof size === 'object') { this.resize(size) } @@ -143,6 +150,11 @@ export class Emulator { Module.setCanvasSize(width, height) } + private getElementSize() { + const { element, size } = this.options + return !size || size === 'auto' ? { width: element.offsetWidth, height: element.offsetHeight } : size + } + private async setupFileSystem() { const { Module, FS, PATH, ERRNO_CODES } = this.getEmscripten() diff --git a/src/nostalgist.ts b/src/nostalgist.ts index accde88..2b1bc1b 100644 --- a/src/nostalgist.ts +++ b/src/nostalgist.ts @@ -331,13 +331,13 @@ export class Nostalgist { } private async loadEmulatorOptions() { - const { waitForInteraction } = this.options + const { size = 'auto', waitForInteraction } = this.options const element = this.getElementOption() - const size = this.getSizeOption(element) + const style = this.getStyleOption() const retroarch = this.getRetroarchOption() const retroarchCore = this.getRetroarchCoreOption() const [core, rom, bios] = await Promise.all([this.getCoreOption(), this.getRomOption(), this.getBiosOption()]) - const emulatorOptions = { element, size, core, rom, bios, retroarch, retroarchCore, waitForInteraction } + const emulatorOptions = { element, style, size, core, rom, bios, retroarch, retroarchCore, waitForInteraction } this.emulatorOptions = emulatorOptions } @@ -345,7 +345,6 @@ export class Nostalgist { if (typeof document !== 'object') { throw new TypeError('document must be an object') } - let { element } = this.options if (typeof element === 'string' && element) { const canvas = document.body.querySelector(element) @@ -362,9 +361,6 @@ export class Nostalgist { } if (element instanceof HTMLCanvasElement) { - if (!element.isConnected) { - document.body.append(element) - } element.id = 'canvas' return element } @@ -372,9 +368,27 @@ export class Nostalgist { throw new TypeError('invalid element') } - private getSizeOption(element: HTMLCanvasElement) { - const { size } = this.options - return !size || size === 'auto' ? { width: element.offsetWidth, height: element.offsetHeight } : size + private getStyleOption() { + const { element, style } = this.options + const defaultStyle: Partial = { + position: 'fixed', + top: '0', + left: '0', + width: '100%', + height: '100%', + backgroundColor: 'black', + zIndex: '1', + } + if (!element) { + return { + ...defaultStyle, + ...style, + } + } + if (style) { + return style + } + return {} } private async getCoreOption() { diff --git a/src/types/emulator-options.ts b/src/types/emulator-options.ts index c4f7747..a580c44 100644 --- a/src/types/emulator-options.ts +++ b/src/types/emulator-options.ts @@ -2,7 +2,16 @@ import type { RetroArchConfig } from './retroarch-config' export interface EmulatorOptions { element: HTMLCanvasElement + + style: Partial + + /** + * + * The size of the canvas element. + * If it's `'auto'`, the canvas element will keep its original size, or it's width and height will be updated as specified. + */ size?: 'auto' | { width: number; height: number } + core: { /** the name of core */ name: string diff --git a/src/types/nostalgist-options.ts b/src/types/nostalgist-options.ts index 220f0ea..4315c2e 100644 --- a/src/types/nostalgist-options.ts +++ b/src/types/nostalgist-options.ts @@ -28,7 +28,33 @@ export interface NostalgistOptions { */ element: string | HTMLCanvasElement - size?: 'auto' | { width: number; height: number } + /** + * The style of the canvas element. + * + * The CSS rule name should be "camelCase" instead of "kebab-case". For example, `{ backgroundColor: 'black' }` is valid, but `{ background-color: '' }` is not. + * + * If the canvas element is created automatically, the style will be + * ```js + * { + * position: 'fixed', + * top: '0', + * left: '0', + * width: '100%', + * height: '100%', + * backgroundColor: 'black', + * zIndex: '1', + * } + * ``` + * otherwise it will be `undefined`. + */ + style?: Partial + + /** + * + * The size of the canvas element. + * If it's `'auto'`, the canvas element will keep its original size, or it's width and height will be updated as specified. + */ + size: 'auto' | { width: number; height: number } core: string | NostalgistCoreDict diff --git a/tests/integration/nostalgist.spec.ts b/tests/integration/nostalgist.spec.ts index 93abdc7..f1af877 100644 --- a/tests/integration/nostalgist.spec.ts +++ b/tests/integration/nostalgist.spec.ts @@ -177,4 +177,15 @@ describe('nostalgist', () => { expect(emulatorOptions.bios[1].fileName).toEqual('FinalBurn Neo (hiscore).zip') expect(emulatorOptions.bios[1].fileContent.constructor.name).toBe('Blob') }) + + test.only('Nostalgist.launch with custom style and size', async () => { + const nostalgist = await Nostalgist.launch({ + size: { width: 100, height: 100 }, + core: 'fceumm', + rom: 'flappybird.nes', + }) + + const options = nostalgist.getOptions() + expect(options.size).toEqual({ width: 100, height: 100 }) + }) })