Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

メッセージウィンドウのオーバーフロー問題を修正 #25

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,18 @@
"ecmaVersion": 2020,
"sourceType": "module"
},
"env": {
"browser": true,
"es6": true
},
"rules": {
"semi": ["error", "never"],
"semi-spacing": ["error", { "after": true, "before": false }],
"semi-style": ["error", "last"],
"no-extra-semi": "error",
"no-unexpected-multiline": "error",
"no-unreachable": "error",
"camelcase": ["error", { "properties": "always" }]
"camelcase": ["error", { "properties": "always" }],
"no-global-assign": ["error", { "exceptions": ["engineConfig"] }]
}
}
3 changes: 3 additions & 0 deletions example/src/js/message_overflow_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// メッセージウィンドウのオーバーフロー問題を検証するためのテストシーン
// このファイルは自動生成されます - 編集しないでください
export const scenario = []
21 changes: 21 additions & 0 deletions example/src/scene/message_overflow_test.scene
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<scene>
<scenario>
<text>これは短いテキストです。</text>
<text>これは非常に長いテキストです。メッセージウィンドウの幅を超えるような長さのテキストを表示した場合に、自動的に改行されるかどうかを確認します。このテキストは自動的に改行されるはずです。</text>
<text>これは複数行にわたる長いテキストです。
1行目
2行目
3行目
改行コードが適切に処理されるかどうかを確認します。</text>
<text>これはメッセージウィンドウの高さを超えるような非常に長いテキストです。このテキストはメッセージウィンドウの高さを超えるため、自動的にスクロールされるか、または次のページに表示されるはずです。このテキストはメッセージウィンドウの高さを超えるため、自動的にスクロールされるか、または次のページに表示されるはずです。このテキストはメッセージウィンドウの高さを超えるため、自動的にスクロールされるか、または次のページに表示されるはずです。このテキストはメッセージウィンドウの高さを超えるため、自動的にスクロールされるか、または次のページに表示されるはずです。このテキストはメッセージウィンドウの高さを超えるため、自動的にスクロールされるか、または次のページに表示されるはずです。</text>
</scenario>

<script>
export const sceneConfig = {
name: 'メッセージオーバーフローテスト',
template: './src/screen/title.html',
background: './src/resource/background/bg_01.jpg',
bgm: './src/resource/sound/bgm_01.mp3'
}
</script>
</scene>
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"lint": "eslint . --ext .ts --ext .js",
"test": "jest",
"test:e2e": "playwright test",
"build": "rimraf ./dist/ && tsc && cp -r package.json README.md engineConfig.json parser/ dist/ && cp src/core/index.js dist/src/core/"
},
"bin": {
Expand All @@ -21,6 +22,7 @@
"storejs": "^2.1.0"
},
"devDependencies": {
"@playwright/test": "^1.51.0",
"@types/jest": "^29.5.12",
"@types/node": "^20.11.28",
"@types/storejs": "^2.0.3",
Expand Down
37 changes: 37 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:8080',
trace: 'on-first-retry',
// ヘッドレスモードを無効にする
headless: false,
// テスト実行時にブラウザを遅くする(デバッグ用)
launchOptions: {
slowMo: 1000,
},
// スクリーンショットを自動的に撮影
screenshot: 'on',
},
// テストのタイムアウトを延長
timeout: 60000,
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
command: 'cd example && npm run dev',
url: 'http://localhost:8080',
reuseExistingServer: !process.env.CI,
// Webサーバーの起動を待つ時間を延長
timeout: 120000,
},
});
67 changes: 66 additions & 1 deletion src/core/drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,23 +72,88 @@ export class Drawer {
this.messageText.appendChild(containerElement)
element = containerElement
}

// メッセージウィンドウの幅と高さを取得
const messageWindow = document.getElementById('messageWindow') as HTMLElement
const messageWindowWidth = messageWindow.clientWidth - 60 // パディングを考慮
const messageWindowHeight = messageWindow.clientHeight - 60 // パディングを考慮

// 現在の行の文字数をカウント
let currentLineLength = element.innerHTML.length > 0 ?
element.innerHTML.split('<br>').pop()?.length || 0 : 0;

// 1文字あたりの平均幅(ピクセル)を推定
const charWidth = 16; // 平均的な日本語フォントの幅

// 1行に表示できる最大文字数を計算
const maxCharsPerLine = Math.floor(messageWindowWidth / charWidth);

for (const char of text) {
//prettier-ignore
setTimeout(() => { this.readySkip = true, wait });

// 改行文字の処理
if (char === '\n') {
element.innerHTML += '<br>'
currentLineLength = 0
continue
}

// 行の長さが最大文字数を超える場合、自動改行
if (currentLineLength >= maxCharsPerLine) {
element.innerHTML += '<br>'
currentLineLength = 0
}

// メッセージウィンドウの高さを超える場合の処理
if (element.scrollHeight > messageWindowHeight) {
// 自動スクロールを行う
messageWindow.scrollTop = messageWindow.scrollHeight
}

// 100ミリ秒待ってから、スキップボタンが押されたら即座に表示
if (!this.isSkip) {
element.innerHTML += char
currentLineLength++
await sleep(wait)
} else {
if (this.readySkip) {
element.innerHTML += text.slice(element.textContent!.length)
// スキップ時は残りのテキストを一度に表示
const remainingText = text.slice(element.textContent!.length)

// 残りのテキストを適切な長さで改行しながら表示
let processedText = ''
let tempLineLength = currentLineLength

for (const remainingChar of remainingText) {
if (remainingChar === '\n') {
processedText += '<br>'
tempLineLength = 0
} else {
if (tempLineLength >= maxCharsPerLine) {
processedText += '<br>'
tempLineLength = 0
}
processedText += remainingChar
tempLineLength++
}
}

element.innerHTML += processedText

// スクロール位置を調整
messageWindow.scrollTop = messageWindow.scrollHeight

this.readySkip = false
this.isSkip = false
break
}
}
await sleep(wait)
}

// 表示完了後、スクロール位置を最下部に調整
messageWindow.scrollTop = messageWindow.scrollHeight
}

async drawLineBreak() {
Expand Down
78 changes: 53 additions & 25 deletions src/core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,31 @@ import { outputLog } from '../utils/logger'
import { sleep } from '../utils/waitUtil'

export class Core {
bgm = null
isAuto = false
isNext = false
isSkip = false
onNextHandler = null
sceneFile = {}
sceneConfig = {}
commandList = {
text: this.textHandler,
choice: this.choiceHandler,
show: this.showHandler,
newpage: this.newpageHandler,
hide: this.hideHandler,
jump: this.jumpHandler,
sound: this.soundHandler,
say: this.sayHandler,
if: this.ifHandler,
call: this.callHandler,
moveto: this.moveToHandler,
route: this.routeHandler,
wait: this.waitHandler,
}

constructor() {
// プロパティの初期化
this.bgm = null
this.isAuto = false
this.isNext = false
this.isSkip = false
this.onNextHandler = null
this.sceneFile = {}
this.sceneConfig = {}
this.commandList = {
text: this.textHandler,
choice: this.choiceHandler,
show: this.showHandler,
newpage: this.newpageHandler,
hide: this.hideHandler,
jump: this.jumpHandler,
sound: this.soundHandler,
say: this.sayHandler,
if: this.ifHandler,
call: this.callHandler,
moveto: this.moveToHandler,
route: this.routeHandler,
wait: this.waitHandler,
}

// gameContainerの初期化(HTMLのgameContainerを取得する)
this.gameContainer = document.getElementById('gameContainer')
// Drawerの初期化(canvasタグのサイズを設定する)
Expand Down Expand Up @@ -169,11 +170,36 @@ export class Core {
//prettier-ignore
this.onNextHandler = () => { this.drawer.isSkip = true }
this.drawer.clearText() // テキスト表示領域をクリア

// メッセージウィンドウの要素を取得
const messageWindow = document.getElementById('messageWindow')
const messageView = document.getElementById('messageView')

// 表示する文章を1行ずつ表示する
for (const text of scenarioObject.content) {
outputLog('textSpeed', 'debug', text)

// メッセージウィンドウが一定の高さを超えた場合、クリックを待って次のページに進む
if (messageView.scrollHeight > messageWindow.clientHeight * 0.8) {
// 「続く」のような表示を追加
const continueElement = document.createElement('div')
continueElement.style.textAlign = 'right'
continueElement.style.marginRight = '20px'
continueElement.style.marginTop = '10px'
continueElement.innerHTML = '▼'
messageView.appendChild(continueElement)

// クリック待ち
await this.clickWait()

// 次のページのためにテキストをクリア
this.drawer.clearText()
}

if (typeof text === 'string') {
await this.drawer.drawText(this.expandVariable(text), scenarioObject.speed || 25)
// 改行コードを適切に処理
const processedText = this.expandVariable(text).replace(/\\n/g, '\n')
await this.drawer.drawText(processedText, scenarioObject.speed || 25)
} else {
if (text.type === 'br' || text.type === 'wait') {
outputLog('text', 'debug', text)
Expand All @@ -183,7 +209,9 @@ export class Core {
}
} else {
const container = this.drawer.createDecoratedElement(text)
await this.drawer.drawText(this.expandVariable(text.content[0]), text.speed || 25, container)
// 改行コードを適切に処理
const processedContent = this.expandVariable(text.content[0]).replace(/\\n/g, '\n')
await this.drawer.drawText(processedContent, text.speed || 25, container)
}
}
}
Expand Down
Loading