import { Cast } from "../utils/cast"; import { Runtime } from "./runtime"; import { KEYBOARD_KEYS } from "../../type/engine/keyboad"; import { InputMedia } from "../utils/inputMedia"; import { ScratchElement } from "../gui/scratchElement"; declare type POST_DATA = { isDown: boolean, key : string, }; const KEY_NAME_LIST:string[] = Object.keys(KEYBOARD_KEYS).map((name:string) => KEYBOARD_KEYS[name]); /** * @hidden */ const KeysToBlock = new Set([ 'ArrowUp', // 上向き矢印 'ArrowDown', // 下向き矢印 'ArrowLeft', // 左向き矢印 (横スクロール防止) 'ArrowRight', // 右向き矢印 (横スクロール防止) ' ', // スペースキー (ページダウン防止) 'PageUp', // ページアップ 'PageDown', // ページダウン 'Home', // ホーム 'End' // エンド ]); export class Keyboard { private _keysPressed: string[] = []; private _runtime : Runtime; private _spaceStopPropagation!: boolean; constructor ( runtime: Runtime ) { this._runtime = runtime; const me = this; const keyDown = (e: KeyboardEvent) => { // 押されたキーがブロック対象のリストに含まれているか判定 if (KeysToBlock.has(e.key)) { // ブラウザのデフォルト挙動(スクロールなど)をキャンセル e.preventDefault(); } const scratchKey = this._keyStringToScratchKey(e.key); this.pressKey(scratchKey); } const keyUp = (e:KeyboardEvent) => { const scratchKey = this._keyStringToScratchKey(e.key); this.releaseKey(scratchKey); } document.addEventListener('keydown', keyDown, { passive: false }); // preventDefaultを確実に動作させるためのオプション document.addEventListener('keyup', keyUp, {passive: false}); this._spaceStopPropagation = true; } pressKey(keyName:string) { const data: POST_DATA = { isDown: true, key : keyName, }; this.postData(data); } releaseKey(keyName:string) { const data: POST_DATA = { isDown: false, key : keyName, }; this.postData(data); } postData(data: POST_DATA) { if (!data.key) return; //const scratchKey = this._keyStringToScratchKey(data.key); const scratchKey = data.key; if (scratchKey === '') return; const index = this._keysPressed.indexOf(scratchKey); if (data.isDown) { this._runtime.emit('KEY_PRESSED', scratchKey); if (index < 0) { this._keysPressed.push(scratchKey); } } else if (index > -1) { // If already present, remove from the list. this._keysPressed.splice(index, 1); } } get spaceStopPropagation() : boolean { return this._spaceStopPropagation; } set spaceStopPropagation(spaceStopPropagation: boolean) { this._spaceStopPropagation = spaceStopPropagation; } /** * キーが押されているかを判定する * @param {string|number} keyArg key argument. * @return {boolean} Is the specified key down? */ keyIsDown(keyArg?: string): boolean { if (!keyArg || keyArg === 'any') { return this._keysPressed.length > 0; } const scratchKey = this._keyArgToScratchKey(keyArg); return this._keysPressed.indexOf(scratchKey) > -1; } _scratchKeyToKeyString( scratchKey: string ): string { switch (scratchKey) { case KEYBOARD_KEYS.SPACE: return ' '; case KEYBOARD_KEYS.LEFT: return 'ArrowLeft'; case KEYBOARD_KEYS.UP: return 'ArrowUp'; case KEYBOARD_KEYS.RIGHT: return 'ArrowRight'; case KEYBOARD_KEYS.DOWN: return 'ArrowDown'; case KEYBOARD_KEYS.ENTER: return 'Enter'; case KEYBOARD_KEYS.ESCAPE: return 'Escape'; } if(scratchKey.length == 0) { // 1文字の場合は、空文字 return ''; }else if(scratchKey.length == 1) { // 1文字のキーは大文字にする return scratchKey.toUpperCase(); }else { // 2文字以上の場合は、先頭の文字にする(大文字) const firstCharacter = scratchKey.charAt(0); return firstCharacter.toUpperCase(); } } /** * Convert from a keyboard event key name to a Scratch key name. * @param {string} keyString the input key string. * @return {string} the corresponding Scratch key, or an empty string. */ _keyStringToScratchKey (keyString: string) : string { keyString = Cast.toString(keyString); // Convert space and arrow keys to their Scratch key names. switch (keyString) { case ' ': return KEYBOARD_KEYS.SPACE; case 'ArrowLeft':return KEYBOARD_KEYS.LEFT; case 'Left': return KEYBOARD_KEYS.LEFT; case 'ArrowUp':return KEYBOARD_KEYS.UP; case 'Up': return KEYBOARD_KEYS.UP; case 'Right': return KEYBOARD_KEYS.RIGHT; case 'ArrowRight': return KEYBOARD_KEYS.RIGHT; case 'Down': return KEYBOARD_KEYS.DOWN; case 'ArrowDown': return KEYBOARD_KEYS.DOWN; case 'Enter': return KEYBOARD_KEYS.ENTER; case 'Escape': return KEYBOARD_KEYS.ESCAPE; } // 上記以外の 2文字以上のキーは空文字に変える if (keyString.length > 1) { return ''; } // 上記以外の 1文字のキーは大文字にする return keyString.toUpperCase(); } /** * Convert from a block argument to a Scratch key name. * * @param {string|number} keyArg the input arg. * @return {string} the corresponding Scratch key. */ _keyArgToScratchKey (keyArg: string|number) : string { // If a number was dropped in, try to convert from ASCII to Scratch key. if (typeof keyArg === 'number') { // Check for the ASCII range containing numbers, some punctuation, // and uppercase letters. if (keyArg >= 48 && keyArg <= 90) { return String.fromCharCode(keyArg); } switch (keyArg) { case 32: return KEYBOARD_KEYS.SPACE; case 37: return KEYBOARD_KEYS.LEFT; case 38: return KEYBOARD_KEYS.UP; case 39: return KEYBOARD_KEYS.RIGHT; case 40: return KEYBOARD_KEYS.DOWN; } } keyArg = Cast.toString(keyArg); // If the arg matches a special key name, return it. if (KEY_NAME_LIST.includes(keyArg)) { return keyArg; } // Use only the first character. if (keyArg.length > 1) { keyArg = keyArg[0]; } // Check for the space character. if (keyArg === ' ') { return KEYBOARD_KEYS.SPACE; } return keyArg.toUpperCase(); } }