danoniplus 50.0.0 → 50.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/css/danoni_main.css +6 -1
- package/js/danoni_main.js +232 -34
- package/js/lib/danoni_constants.js +18 -4
- package/package.json +1 -1
package/css/danoni_main.css
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
本体cssファイル
|
|
5
5
|
|
|
6
6
|
Created : 2018/10/08
|
|
7
|
-
Revised : 2026/
|
|
7
|
+
Revised : 2026/08/29 (v50.1.0)
|
|
8
8
|
|
|
9
9
|
https://github.com/cwtickle/danoniplus
|
|
10
10
|
------------------------------------------ */
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
overflow: hidden;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
/* タブのバックグラウンド化に伴う一時停止(danoni_main.js側でdivRootにgamePausedクラスを付け外し) */
|
|
28
|
+
#divRoot.gamePaused * {
|
|
29
|
+
animation-play-state: paused !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
27
32
|
input[type="range"] {
|
|
28
33
|
appearance: none;
|
|
29
34
|
background: transparent;
|
package/js/danoni_main.js
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Source by tickle
|
|
6
6
|
* Created : 2018/10/08
|
|
7
|
-
* Revised : 2026/08/
|
|
7
|
+
* Revised : 2026/08/29
|
|
8
8
|
*
|
|
9
9
|
* https://github.com/cwtickle/danoniplus
|
|
10
10
|
*/
|
|
11
|
-
const g_version = `Ver 50.
|
|
12
|
-
const g_revisedDate = `2026/08/
|
|
11
|
+
const g_version = `Ver 50.1.0`;
|
|
12
|
+
const g_revisedDate = `2026/08/29`;
|
|
13
13
|
|
|
14
14
|
// カスタム用バージョン (danoni_custom.js 等で指定可)
|
|
15
15
|
let g_localVersion = ``;
|
|
@@ -123,6 +123,12 @@ const waitUntilLoaded = () => {
|
|
|
123
123
|
await loadScript2(`${g_rootPath}../js/lib/danoni_constants.js?${g_versionForUrl}`);
|
|
124
124
|
await loadScript2(`${g_rootPath}../js/lib/legacy_functions.js?${g_versionForUrl.split(`.`)[0]}`, false);
|
|
125
125
|
initialControl();
|
|
126
|
+
|
|
127
|
+
// プレイ画面(g_currentPage === `main`)でのみ有効化される、タブ非表示検知(常時1個だけ登録)
|
|
128
|
+
g_handler.addListener(document, `visibilitychange`, () => {
|
|
129
|
+
if (g_currentPage !== `main`) return;
|
|
130
|
+
document.hidden ? g_timelineHooks.pause() : g_timelineHooks.resume();
|
|
131
|
+
});
|
|
126
132
|
})();
|
|
127
133
|
|
|
128
134
|
/*-----------------------------------------------------------*/
|
|
@@ -269,6 +275,11 @@ let g_audioForMS = null;
|
|
|
269
275
|
let g_timeoutEvtId = 0;
|
|
270
276
|
let g_timeoutEvtTitleId = 0;
|
|
271
277
|
let g_timeoutEvtResultId = 0;
|
|
278
|
+
|
|
279
|
+
// タブのバックグラウンド化に伴う一時停止/再開のフック
|
|
280
|
+
let g_timelineHooks = { pause: () => { }, resume: () => { } };
|
|
281
|
+
let g_timelineSessionId = 0; // mainInit()呼び出しごとに採番。古いセッションのfinishResume無効化に使用
|
|
282
|
+
|
|
272
283
|
let g_inputKeyBuffer = {};
|
|
273
284
|
|
|
274
285
|
// 音楽ファイル エンコードフラグ
|
|
@@ -606,9 +617,9 @@ const formatObject = (_obj, _indent = 0, { colorFmt = true, rootKey = `` } = {})
|
|
|
606
617
|
// scrollDirXのスクロール方向表示処理
|
|
607
618
|
return _value === 1 ? `1|<span style="color:#ff9999">↑</span>` : `-1|<span style="color:#66ff66">↓</span>`;
|
|
608
619
|
|
|
609
|
-
} else if (listMatching(_rootKey, [`keyCtrl`, `keyRetry`, `keyTitleBack`], { prefix: `^` })
|
|
620
|
+
} else if (listMatching(_rootKey, [`keyCtrl`, `keyRetry`, `keyTitleBack`, `keyPause`], { prefix: `^` })
|
|
610
621
|
&& !_rootKey.startsWith(`keyCtrlPtn`)) {
|
|
611
|
-
// keyCtrlX, keyRetryX, keyTitleBackX の対応キー表示処理
|
|
622
|
+
// keyCtrlX, keyRetryX, keyTitleBackX, keyPauseX の対応キー表示処理
|
|
612
623
|
return (g_kCd[_value] && _value !== 0) ? `${_value}|<span style="color:#ffff66">${g_kCd[_value]}</span>` : `----`;
|
|
613
624
|
}
|
|
614
625
|
} else if (isObj(_value)) {
|
|
@@ -2640,7 +2651,7 @@ class AudioPlayer {
|
|
|
2640
2651
|
}
|
|
2641
2652
|
|
|
2642
2653
|
get elapsedTime() {
|
|
2643
|
-
return this._context.currentTime - this.
|
|
2654
|
+
return (this._context.currentTime - this._scheduledTime) * this.playbackRate + g_scheduleLead + this._fadeinPosition;
|
|
2644
2655
|
}
|
|
2645
2656
|
|
|
2646
2657
|
/** AudioContextの現在時刻(秒) */
|
|
@@ -4232,6 +4243,9 @@ const headerConvert = _dosObj => {
|
|
|
4232
4243
|
obj.keyTitleBack = setIntVal(getKeyCtrlVal(_dosObj.keyTitleBack), C_KEY_TITLEBACK);
|
|
4233
4244
|
obj.keyTitleBackDef = obj.keyTitleBack;
|
|
4234
4245
|
obj.keyTitleBackDef2 = obj.keyTitleBack;
|
|
4246
|
+
obj.keyPause = setIntVal(getKeyCtrlVal(_dosObj.keyPause), C_KEY_PAUSE);
|
|
4247
|
+
obj.keyPauseDef = obj.keyPause;
|
|
4248
|
+
obj.keyPauseDef2 = obj.keyPause;
|
|
4235
4249
|
|
|
4236
4250
|
// フリーズアローの許容フレーム数設定
|
|
4237
4251
|
obj.frzAttempt = setIntVal(_dosObj.frzAttempt, C_FRM_FRZATTEMPT);
|
|
@@ -5702,6 +5716,9 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
|
|
|
5702
5716
|
// プレイ中ショートカット:タイトルバック (keyTitleBackX_Y)
|
|
5703
5717
|
newKeySingleParam(newKey, `keyTitleBack`, C_TYP_STRING, C_KEY_TITLEBACK);
|
|
5704
5718
|
|
|
5719
|
+
// プレイ中ショートカット:タイトルバック (keyPauseX_Y)
|
|
5720
|
+
newKeySingleParam(newKey, `keyPause`, C_TYP_STRING, C_KEY_PAUSE);
|
|
5721
|
+
|
|
5705
5722
|
// 別キーフラグ (transKeyX_Y)
|
|
5706
5723
|
newKeySingleParam(newKey, `transKey`, C_TYP_STRING, ``);
|
|
5707
5724
|
|
|
@@ -5740,8 +5757,9 @@ const keysConvert = (_dosObj, { keyExtraList = _dosObj.keyExtraList?.split(`,`)
|
|
|
5740
5757
|
// カスタムキーで定義されたtransKeyPtnを補完
|
|
5741
5758
|
completeTransKeyPtn([newKey]);
|
|
5742
5759
|
|
|
5743
|
-
// keyRetry, keyTitleBackのキー名をキーコードに変換
|
|
5744
|
-
const keyTypePatterns = Object.keys(g_keyObj).filter(val =>
|
|
5760
|
+
// keyRetry, keyTitleBack, keyPauseのキー名をキーコードに変換
|
|
5761
|
+
const keyTypePatterns = Object.keys(g_keyObj).filter(val =>
|
|
5762
|
+
val.startsWith(`keyRetry${newKey}`) || val.startsWith(`keyTitleBack${newKey}`) || val.startsWith(`keyPause${newKey}`));
|
|
5745
5763
|
keyTypePatterns.forEach(name => g_keyObj[name] = getKeyCtrlVal(g_keyObj[name]));
|
|
5746
5764
|
} catch (e) {
|
|
5747
5765
|
g_headerObj.undefinedKeyListFinal.push(newKey);
|
|
@@ -9858,7 +9876,7 @@ const buildPreviewUI = (_frame, _playW, _playH) => {
|
|
|
9858
9876
|
// ============================================================
|
|
9859
9877
|
const scGroup = createEmptySprite(_frame, `previewScGroup`, {
|
|
9860
9878
|
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
9861
|
-
y: _playH -
|
|
9879
|
+
y: _playH - 100 + g_diffObj.shortcutY, w: 80, h: 100, pointerEvents: C_DIS_AUTO,
|
|
9862
9880
|
});
|
|
9863
9881
|
multiAppend(scGroup,
|
|
9864
9882
|
createDivCss2Label(`lblRetry`, `[${g_lblNameObj.l_retry}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 0 }),
|
|
@@ -9867,15 +9885,18 @@ const buildPreviewUI = (_frame, _playW, _playH) => {
|
|
|
9867
9885
|
createDivCss2Label(`lblTitleBack`, `[${g_lblNameObj.l_titleBack}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 35 }),
|
|
9868
9886
|
createDivCss2Label(`lblTitleBackSc`, g_isMac ? `Shift+${g_kCd[g_headerObj.keyRetry]}` : g_kCd[g_headerObj.keyTitleBack],
|
|
9869
9887
|
{ ...g_lblPosObj.lblMainScKey, x: 0, y: 50, fontWeight: g_headerObj.keyTitleBack === C_KEY_TITLEBACK ? `normal` : `bold` }),
|
|
9888
|
+
createDivCss2Label(`lblPause`, `[${g_lblNameObj.l_pause}]`, { ...g_lblPosObj.lblMainScHeader, x: 0, y: 70 }),
|
|
9889
|
+
createDivCss2Label(`lblPauseSc`, g_kCd[g_headerObj.keyPause],
|
|
9890
|
+
{ ...g_lblPosObj.lblMainScKey, x: 0, y: 85, fontWeight: g_headerObj.keyPause === C_KEY_PAUSE ? `normal` : `bold` }),
|
|
9870
9891
|
);
|
|
9871
9892
|
const scConfig = {
|
|
9872
9893
|
toastTitle: g_lblNameObj.shortcutUpdate,
|
|
9873
9894
|
getStdPos: () => ({
|
|
9874
9895
|
x: g_sWidth + g_headerObj.scAreaWidth - 85,
|
|
9875
|
-
y: _playH -
|
|
9896
|
+
y: _playH - 100
|
|
9876
9897
|
}),
|
|
9877
9898
|
};
|
|
9878
|
-
makeElementDraggable(scGroup, `shortcut`, _playW, _playH, { w: 80, h:
|
|
9899
|
+
makeElementDraggable(scGroup, `shortcut`, _playW, _playH, { w: 80, h: 100 }, scConfig);
|
|
9879
9900
|
|
|
9880
9901
|
// ユーザカスタムイベント(プレビュー表示用)
|
|
9881
9902
|
safeExecuteCustomHooks(`g_customJsObj.displayPreview`, g_customJsObj.displayPreview, _frame, _playW, _playH);
|
|
@@ -10900,13 +10921,22 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
|
|
|
10900
10921
|
|
|
10901
10922
|
// リトライのショートカットキー変更
|
|
10902
10923
|
createCss2Button(`scRetry`, getScMsg.Retry(), () => {
|
|
10903
|
-
cursor.style.left = wUnit(g_btnX(
|
|
10924
|
+
cursor.style.left = wUnit(g_btnX(1 / 2) + kcSubX);
|
|
10904
10925
|
cursor.style.top = wUnit(g_sHeight - 160 + kcSubY);
|
|
10905
10926
|
selectedKc = `Retry`;
|
|
10906
10927
|
}, g_lblPosObj.scRetry, g_cssObj.button_Default_NoColor,
|
|
10907
10928
|
g_headerObj.keyRetry === g_headerObj.keyRetryDef2 ?
|
|
10908
10929
|
g_cssObj.title_base : g_cssObj.keyconfig_Changekey),
|
|
10909
10930
|
|
|
10931
|
+
// ポーズのショートカットキー変更
|
|
10932
|
+
createCss2Button(`scPause`, getScMsg.Pause(), () => {
|
|
10933
|
+
cursor.style.left = wUnit(g_btnX(3 / 4) + kcSubX);
|
|
10934
|
+
cursor.style.top = wUnit(g_sHeight - 160 + kcSubY);
|
|
10935
|
+
selectedKc = `Pause`;
|
|
10936
|
+
}, g_lblPosObj.scPause, g_cssObj.button_Default_NoColor,
|
|
10937
|
+
g_headerObj.keyPause === g_headerObj.keyPauseDef2 ?
|
|
10938
|
+
g_cssObj.title_base : g_cssObj.keyconfig_Changekey),
|
|
10939
|
+
|
|
10910
10940
|
// 別キーモード警告メッセージ
|
|
10911
10941
|
createDivCss2Label(
|
|
10912
10942
|
`kcMsg`,
|
|
@@ -11414,8 +11444,8 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
|
|
|
11414
11444
|
} else if ((keyIsDown(g_kCdNameObj.metaLKey) || keyIsDown(g_kCdNameObj.metaRKey)) && keyIsShift()) {
|
|
11415
11445
|
return;
|
|
11416
11446
|
}
|
|
11417
|
-
if (selectedKc === `TitleBack` || selectedKc === `Retry`) {
|
|
11418
|
-
//
|
|
11447
|
+
if (selectedKc === `TitleBack` || selectedKc === `Retry` || selectedKc === `Pause`) {
|
|
11448
|
+
// タイトルバックキー、リトライキー、ポーズキーはプレイ中の操作キーと重複しないようにする(簡易的)
|
|
11419
11449
|
if (g_keyObj[`keyCtrl${keyCtrlPtn}`].flat().includes(setKey)) {
|
|
11420
11450
|
makeInfoWindow(g_msgInfoObj.I_0002, `fadeOut0`);
|
|
11421
11451
|
return;
|
|
@@ -11425,10 +11455,10 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
|
|
|
11425
11455
|
g_headerObj[`key${selectedKc}Def`] = setKey;
|
|
11426
11456
|
document.getElementById(`sc${selectedKc}`).textContent = getScMsg[selectedKc]();
|
|
11427
11457
|
document.getElementById(`sc${selectedKc}`).style.fontSize =
|
|
11428
|
-
wUnit(getFontSize2(getScMsg[selectedKc](), g_btnWidth(
|
|
11458
|
+
wUnit(getFontSize2(getScMsg[selectedKc](), g_btnWidth(1 / 4) - 40, { maxSiz: g_limitObj.mainSiz }));
|
|
11429
11459
|
if (g_isMac) {
|
|
11430
11460
|
scTitleBack.textContent = getScMsg.TitleBack();
|
|
11431
|
-
scTitleBack.style.fontSize = wUnit(getFontSize2(getScMsg.TitleBack(), g_btnWidth(
|
|
11461
|
+
scTitleBack.style.fontSize = wUnit(getFontSize2(getScMsg.TitleBack(), g_btnWidth(1 / 4) - 40, { maxSiz: g_limitObj.mainSiz }));
|
|
11432
11462
|
}
|
|
11433
11463
|
changeConfigColor(document.getElementById(`sc${selectedKc}`),
|
|
11434
11464
|
g_headerObj[`key${selectedKc}`] === g_headerObj[`key${selectedKc}Def2`] ?
|
|
@@ -12159,9 +12189,9 @@ const keyconfigKeyboardPreview = (() => {
|
|
|
12159
12189
|
_state.mappedSet = new Set(ctrl.map(arr => toCodeStr(arr[0])).filter(v => v !== ``));
|
|
12160
12190
|
_state.altSet = new Set(ctrl.flatMap(arr => arr.slice(1)).map(toCodeStr).filter(v => v !== ``));
|
|
12161
12191
|
|
|
12162
|
-
// プレイ中ショートカット: keyRetry / keyTitleBack は g_headerObj から取得、PgDn(34) / PgUp(33) は固定
|
|
12192
|
+
// プレイ中ショートカット: keyRetry / keyTitleBack / keyPause は g_headerObj から取得、PgDn(34) / PgUp(33) は固定
|
|
12163
12193
|
_state.shortcutSet = new Set(
|
|
12164
|
-
[g_headerObj.keyRetry, g_headerObj.keyTitleBack, 34, 33].map(toCodeStr).filter(v => v !== ``)
|
|
12194
|
+
[g_headerObj.keyRetry, g_headerObj.keyTitleBack, g_headerObj.keyPause, 34, 33].map(toCodeStr).filter(v => v !== ``)
|
|
12165
12195
|
);
|
|
12166
12196
|
|
|
12167
12197
|
if (_state.visible) drawMap();
|
|
@@ -12243,6 +12273,9 @@ const setPlayingShortcut = () => {
|
|
|
12243
12273
|
if (g_headerObj.keyTitleBackDef === C_KEY_TITLEBACK) {
|
|
12244
12274
|
g_headerObj.keyTitleBack = setIntVal(getKeyCtrlVal(g_keyObj[`keyTitleBack${keyCtrlPtn}`]), g_headerObj.keyTitleBackDef);
|
|
12245
12275
|
}
|
|
12276
|
+
if (g_headerObj.keyPauseDef === C_KEY_PAUSE) {
|
|
12277
|
+
g_headerObj.keyPause = setIntVal(getKeyCtrlVal(g_keyObj[`keyPause${keyCtrlPtn}`]), g_headerObj.keyPauseDef);
|
|
12278
|
+
}
|
|
12246
12279
|
};
|
|
12247
12280
|
|
|
12248
12281
|
/**
|
|
@@ -14756,8 +14789,12 @@ const getArrowSettings = () => {
|
|
|
14756
14789
|
g_workObj.arrowReturnVal = 0;
|
|
14757
14790
|
g_gameOverFlg = false;
|
|
14758
14791
|
g_finishFlg = true;
|
|
14759
|
-
g_workObj.nonDefaultSc = g_headerObj.keyRetry !== C_KEY_RETRY || g_headerObj.keyTitleBack !== C_KEY_TITLEBACK;
|
|
14760
|
-
if (g_headerObj.scAreaWidth === 0 && (
|
|
14792
|
+
g_workObj.nonDefaultSc = g_headerObj.keyRetry !== C_KEY_RETRY || g_headerObj.keyTitleBack !== C_KEY_TITLEBACK || g_headerObj.keyPause !== C_KEY_PAUSE;
|
|
14793
|
+
if (g_headerObj.scAreaWidth === 0 && (
|
|
14794
|
+
g_headerObj.keyRetry !== g_headerObj.keyRetryDef2 ||
|
|
14795
|
+
g_headerObj.keyTitleBack !== g_headerObj.keyTitleBackDef2 ||
|
|
14796
|
+
g_headerObj.keyPause !== g_headerObj.keyPauseDef2
|
|
14797
|
+
)) {
|
|
14761
14798
|
g_workObj.nonDefaultSc = false;
|
|
14762
14799
|
}
|
|
14763
14800
|
if (g_diffObj.shortcutX !== 0 || g_diffObj.shortcutY !== 0) {
|
|
@@ -15104,7 +15141,7 @@ const mainInit = () => {
|
|
|
15104
15141
|
|
|
15105
15142
|
// 開始位置、楽曲再生位置の設定
|
|
15106
15143
|
const firstFrame = g_scoreObj.frameNum;
|
|
15107
|
-
|
|
15144
|
+
let musicStartFrame = firstFrame + g_headerObj.blankFrame;
|
|
15108
15145
|
const fadeFlgs = { fadein: [`In`, `Out`], fadeout: [`Out`, `In`] };
|
|
15109
15146
|
g_audio.volume = (firstFrame === 0 ? g_stateObj.volume / 100 : 0);
|
|
15110
15147
|
|
|
@@ -15114,6 +15151,12 @@ const mainInit = () => {
|
|
|
15114
15151
|
let musicStartTime;
|
|
15115
15152
|
let musicStartCtxTime;
|
|
15116
15153
|
let musicStartFlg = false;
|
|
15154
|
+
let isPaused = false; // 一時停止中かどうか(経路によらず共通のフラグ)
|
|
15155
|
+
let manualPauseFlg = false; // 手動ポーズによる一時停止かどうか(true時はvisibilitychangeでの自動再開をスキップ)
|
|
15156
|
+
let pausedElapsedTime = null; // [AudioPlayer/開始後のみ] 一時停止時点の再生位置(秒)
|
|
15157
|
+
let pausedStartAdjustment = null; // [AudioPlayer/開始前のみ] 開始予定までの残り時間(秒)
|
|
15158
|
+
let countdownTimeoutId = null; // 再開前カウントダウンのタイマーID。null=カウントダウン中でない
|
|
15159
|
+
const mySessionId = ++g_timelineSessionId; // このmainInit()実行を識別するID
|
|
15117
15160
|
|
|
15118
15161
|
g_inputKeyBuffer = {};
|
|
15119
15162
|
|
|
@@ -15235,29 +15278,30 @@ const mainInit = () => {
|
|
|
15235
15278
|
}
|
|
15236
15279
|
|
|
15237
15280
|
if (g_workObj.nonDefaultSc) {
|
|
15281
|
+
const diffX = g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX;
|
|
15282
|
+
const diffY = g_headerObj.playingHeight + g_diffObj.shortcutY;
|
|
15238
15283
|
multiAppend(infoSprite,
|
|
15239
15284
|
createDivCss2Label(`lblRetry`, `[${g_lblNameObj.l_retry}]`, {
|
|
15240
|
-
...g_lblPosObj.lblMainScHeader,
|
|
15241
|
-
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
15242
|
-
y: g_headerObj.playingHeight - 65 + g_diffObj.shortcutY,
|
|
15285
|
+
...g_lblPosObj.lblMainScHeader, x: diffX, y: diffY - 95,
|
|
15243
15286
|
}),
|
|
15244
15287
|
createDivCss2Label(`lblRetrySc`, g_kCd[g_headerObj.keyRetry], {
|
|
15245
|
-
...g_lblPosObj.lblMainScKey,
|
|
15246
|
-
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
15247
|
-
y: g_headerObj.playingHeight - 50 + g_diffObj.shortcutY,
|
|
15288
|
+
...g_lblPosObj.lblMainScKey, x: diffX, y: diffY - 80,
|
|
15248
15289
|
fontWeight: g_headerObj.keyRetry === C_KEY_RETRY ? `normal` : `bold`,
|
|
15249
15290
|
}),
|
|
15250
15291
|
createDivCss2Label(`lblTitleBack`, `[${g_lblNameObj.l_titleBack}]`, {
|
|
15251
|
-
...g_lblPosObj.lblMainScHeader,
|
|
15252
|
-
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
15253
|
-
y: g_headerObj.playingHeight - 35 + g_diffObj.shortcutY,
|
|
15292
|
+
...g_lblPosObj.lblMainScHeader, x: diffX, y: diffY - 65,
|
|
15254
15293
|
}),
|
|
15255
15294
|
createDivCss2Label(`lblTitleBackSc`, g_isMac ? `Shift+${g_kCd[g_headerObj.keyRetry]}` : g_kCd[g_headerObj.keyTitleBack], {
|
|
15256
|
-
...g_lblPosObj.lblMainScKey,
|
|
15257
|
-
x: g_sWidth + g_headerObj.scAreaWidth - 85 + g_diffObj.shortcutX,
|
|
15258
|
-
y: g_headerObj.playingHeight - 20 + g_diffObj.shortcutY,
|
|
15295
|
+
...g_lblPosObj.lblMainScKey, x: diffX, y: diffY - 50,
|
|
15259
15296
|
fontWeight: g_headerObj.keyTitleBack === C_KEY_TITLEBACK ? `normal` : `bold`,
|
|
15260
15297
|
}),
|
|
15298
|
+
createDivCss2Label(`lblPause`, `[${g_lblNameObj.l_pause}]`, {
|
|
15299
|
+
...g_lblPosObj.lblMainScHeader, x: diffX, y: diffY - 35,
|
|
15300
|
+
}),
|
|
15301
|
+
createDivCss2Label(`lblPauseSc`, g_kCd[g_headerObj.keyPause], {
|
|
15302
|
+
...g_lblPosObj.lblMainScKey, x: diffX, y: diffY - 20,
|
|
15303
|
+
fontWeight: g_headerObj.keyPause === C_KEY_PAUSE ? `normal` : `bold`,
|
|
15304
|
+
}),
|
|
15261
15305
|
);
|
|
15262
15306
|
}
|
|
15263
15307
|
|
|
@@ -15412,6 +15456,13 @@ const mainInit = () => {
|
|
|
15412
15456
|
if (evt.repeat && !g_mainRepeatObj.key.includes(setCode)) {
|
|
15413
15457
|
return blockCode(setCode);
|
|
15414
15458
|
}
|
|
15459
|
+
if (setCode === g_kCdN[g_headerObj.keyPause]) {
|
|
15460
|
+
isPaused ? resumeTimeline(true) : pauseTimeline(true);
|
|
15461
|
+
return blockCode(setCode);
|
|
15462
|
+
}
|
|
15463
|
+
if (isPaused) {
|
|
15464
|
+
return blockCode(setCode);
|
|
15465
|
+
}
|
|
15415
15466
|
g_inputKeyBuffer[setCode] = true;
|
|
15416
15467
|
mainKeyDownActFunc[g_stateObj.autoAll](setCode);
|
|
15417
15468
|
|
|
@@ -16374,6 +16425,149 @@ const mainInit = () => {
|
|
|
16374
16425
|
Math.min(Math.max(1000 / g_fps - buffTime, 0), g_maxFrameWait));
|
|
16375
16426
|
}
|
|
16376
16427
|
};
|
|
16428
|
+
|
|
16429
|
+
/*
|
|
16430
|
+
* タブのバックグラウンド化に伴う一時停止/再開
|
|
16431
|
+
* - 【既知の制限】movLock/initManual設定(movArrowYがCSS animation任せになるケース)では、
|
|
16432
|
+
* 矢印移動そのものがCSS animation駆動になるため、この一時停止機能はバックグラウンド中の
|
|
16433
|
+
* 一時停止を保証できない(演出効果と異なり判定位置に直結するため実プレイには不適)。
|
|
16434
|
+
* 対処は行わず、既知の制限として明記するに留める。
|
|
16435
|
+
*/
|
|
16436
|
+
const cancelResumeCountdown = () => {
|
|
16437
|
+
if (countdownTimeoutId !== null) {
|
|
16438
|
+
clearTimeout(countdownTimeoutId);
|
|
16439
|
+
countdownTimeoutId = null;
|
|
16440
|
+
}
|
|
16441
|
+
document.getElementById(`lblResumeCountdown`)?.remove();
|
|
16442
|
+
};
|
|
16443
|
+
|
|
16444
|
+
let nativeAudioWasPlaying = false;
|
|
16445
|
+
|
|
16446
|
+
const pauseTimeline = (_manual = false) => {
|
|
16447
|
+
// カウントダウン中に再度バックグラウンド化した場合は、カウントダウンのみ中断する
|
|
16448
|
+
// (音源は既に停止済み・gamePausedも付与済みのため、以降の処理は不要)
|
|
16449
|
+
cancelResumeCountdown();
|
|
16450
|
+
if (isPaused) {
|
|
16451
|
+
if (_manual) {
|
|
16452
|
+
// カウントダウン中に手動ポーズキーが押された場合、手動扱いに切り替える
|
|
16453
|
+
manualPauseFlg = true;
|
|
16454
|
+
}
|
|
16455
|
+
return;
|
|
16456
|
+
}
|
|
16457
|
+
isPaused = true;
|
|
16458
|
+
manualPauseFlg = _manual;
|
|
16459
|
+
pausedElapsedTime = null;
|
|
16460
|
+
pausedStartAdjustment = null;
|
|
16461
|
+
if (document.getElementById(`lblPauseMark`) == null) {
|
|
16462
|
+
const countdownLabel = createDivCss2Label(`lblPauseMark`, `Pause`, {
|
|
16463
|
+
...g_lblPosObj.lblPauseMark, x: g_workObj.playingX,
|
|
16464
|
+
});
|
|
16465
|
+
divRoot.appendChild(countdownLabel);
|
|
16466
|
+
}
|
|
16467
|
+
|
|
16468
|
+
if (g_audio instanceof AudioPlayer) {
|
|
16469
|
+
if (g_audio.contextTime < g_audio.scheduledTime) {
|
|
16470
|
+
// 音源開始前は、再生位置ではなく開始予定時刻までの残り時間を保持する。
|
|
16471
|
+
// g_audio.play() は g_scheduleLead を加算するため、その分を除く。
|
|
16472
|
+
pausedStartAdjustment = g_audio.scheduledTime
|
|
16473
|
+
- g_audio.contextTime - g_scheduleLead;
|
|
16474
|
+
} else {
|
|
16475
|
+
// 音源開始後: 再生位置を保存する。
|
|
16476
|
+
pausedElapsedTime = g_audio.elapsedTime - g_scheduleLead;
|
|
16477
|
+
}
|
|
16478
|
+
} else {
|
|
16479
|
+
// `paused` が false の場合だけ、再開時に play() を呼ぶ
|
|
16480
|
+
nativeAudioWasPlaying = !g_audio.paused;
|
|
16481
|
+
}
|
|
16482
|
+
clearTimeout(g_timeoutEvtId);
|
|
16483
|
+
g_audio.pause();
|
|
16484
|
+
|
|
16485
|
+
// フォーカスを失うとkeyupが届かなくなり、押しっぱなし判定・表示が残り得るため、
|
|
16486
|
+
// ここで強制的に全キー「離した」状態に戻す
|
|
16487
|
+
g_inputKeyBuffer = {};
|
|
16488
|
+
g_workObj.keyHitFlg.forEach(lane => lane.fill(false));
|
|
16489
|
+
mainKeyUpActFunc[g_stateObj.autoAll]();
|
|
16490
|
+
divRoot.classList.add(`gamePaused`);
|
|
16491
|
+
};
|
|
16492
|
+
|
|
16493
|
+
const resumeTimeline = (_manual = false) => {
|
|
16494
|
+
if (!isPaused || countdownTimeoutId !== null) {
|
|
16495
|
+
return;
|
|
16496
|
+
}
|
|
16497
|
+
if (!_manual && manualPauseFlg) {
|
|
16498
|
+
// 手動ポーズ中は、タブ復帰による自動再開をスキップする
|
|
16499
|
+
if (document.getElementById(`lblPauseMark`) == null) {
|
|
16500
|
+
const countdownLabel = createDivCss2Label(`lblPauseMark`, `Pause`, {
|
|
16501
|
+
...g_lblPosObj.lblPauseMark, x: g_workObj.playingX,
|
|
16502
|
+
});
|
|
16503
|
+
divRoot.appendChild(countdownLabel);
|
|
16504
|
+
}
|
|
16505
|
+
return;
|
|
16506
|
+
}
|
|
16507
|
+
deleteDiv(divRoot, `lblPauseMark`);
|
|
16508
|
+
const countdownLabel = createDivCss2Label(`lblResumeCountdown`, ``, {
|
|
16509
|
+
...g_lblPosObj.lblPauseMark, x: g_workObj.playingX,
|
|
16510
|
+
});
|
|
16511
|
+
divRoot.appendChild(countdownLabel);
|
|
16512
|
+
|
|
16513
|
+
const finishResume = () => {
|
|
16514
|
+
countdownTimeoutId = null;
|
|
16515
|
+
countdownLabel.remove();
|
|
16516
|
+
|
|
16517
|
+
// カウントダウン中に曲中リトライ等でmainInit()が再実行されたりプレイ画面外に移動した場合、
|
|
16518
|
+
// 古いセッションのfinishResumeが後から発火して二重再生・二重ループを起こすのを防ぐ
|
|
16519
|
+
if (mySessionId !== g_timelineSessionId || g_currentPage !== `main`) {
|
|
16520
|
+
return;
|
|
16521
|
+
}
|
|
16522
|
+
|
|
16523
|
+
if (g_audio instanceof AudioPlayer) {
|
|
16524
|
+
// バックグラウンド中にsuspendされていた場合に備え、先に明示的にresumeを試みる
|
|
16525
|
+
getSharedAudioContext();
|
|
16526
|
+
|
|
16527
|
+
if (pausedStartAdjustment !== null) {
|
|
16528
|
+
// 開始前: 音源と譜面の残り開始時間を両方維持する。
|
|
16529
|
+
g_audio.play(pausedStartAdjustment);
|
|
16530
|
+
musicStartCtxTime = g_audio.scheduledTime
|
|
16531
|
+
+ (g_audioLatencyCompensation ? g_audio.outputLatency : 0);
|
|
16532
|
+
// musicStartFrame は変更しない。
|
|
16533
|
+
} else {
|
|
16534
|
+
// 開始後: 再生位置から再開し、現在フレームを基準に再アンカーする。
|
|
16535
|
+
g_audio.currentTime = pausedElapsedTime;
|
|
16536
|
+
g_audio.play();
|
|
16537
|
+
musicStartCtxTime = g_audio.scheduledTime
|
|
16538
|
+
+ (g_audioLatencyCompensation ? g_audio.outputLatency : 0);
|
|
16539
|
+
musicStartFrame = g_scoreObj.frameNum;
|
|
16540
|
+
}
|
|
16541
|
+
|
|
16542
|
+
} else {
|
|
16543
|
+
if (nativeAudioWasPlaying) {
|
|
16544
|
+
g_audio.play();
|
|
16545
|
+
}
|
|
16546
|
+
}
|
|
16547
|
+
// バックグラウンド経過時間をフレーム計算から除外する
|
|
16548
|
+
musicStartTime = performance.now() - (g_scoreObj.frameNum - musicStartFrame) * 1000 / g_fps;
|
|
16549
|
+
|
|
16550
|
+
divRoot.classList.remove(`gamePaused`);
|
|
16551
|
+
isPaused = false;
|
|
16552
|
+
manualPauseFlg = false;
|
|
16553
|
+
pausedElapsedTime = null;
|
|
16554
|
+
pausedStartAdjustment = null;
|
|
16555
|
+
g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
|
|
16556
|
+
};
|
|
16557
|
+
|
|
16558
|
+
const tick = _remaining => {
|
|
16559
|
+
if (_remaining <= 0) {
|
|
16560
|
+
finishResume();
|
|
16561
|
+
return;
|
|
16562
|
+
}
|
|
16563
|
+
countdownLabel.innerHTML = String(_remaining);
|
|
16564
|
+
countdownTimeoutId = setTimeout(() => tick(_remaining - 1), 1000);
|
|
16565
|
+
};
|
|
16566
|
+
tick(3);
|
|
16567
|
+
};
|
|
16568
|
+
|
|
16569
|
+
g_timelineHooks.pause = pauseTimeline;
|
|
16570
|
+
g_timelineHooks.resume = resumeTimeline;
|
|
16377
16571
|
safeExecuteCustomHooks(`g_skinJsObj.main`, g_skinJsObj.main);
|
|
16378
16572
|
|
|
16379
16573
|
g_audio.currentTime = firstFrame / g_fps * g_headerObj.playbackRate;
|
|
@@ -16391,7 +16585,11 @@ const mainInit = () => {
|
|
|
16391
16585
|
+ (g_audioLatencyCompensation ? g_audio.outputLatency : 0);
|
|
16392
16586
|
}
|
|
16393
16587
|
|
|
16394
|
-
|
|
16588
|
+
if (document.hidden) {
|
|
16589
|
+
pauseTimeline();
|
|
16590
|
+
} else {
|
|
16591
|
+
g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
|
|
16592
|
+
}
|
|
16395
16593
|
};
|
|
16396
16594
|
|
|
16397
16595
|
/**
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Source by tickle
|
|
7
7
|
* Created : 2019/11/19
|
|
8
|
-
* Revised : 2026/08/
|
|
8
|
+
* Revised : 2026/08/29 (v50.1.0)
|
|
9
9
|
*
|
|
10
10
|
* https://github.com/cwtickle/danoniplus
|
|
11
11
|
*/
|
|
@@ -186,6 +186,7 @@ const g_lblPosObj = {};
|
|
|
186
186
|
const getScMsg = {
|
|
187
187
|
TitleBack: () => g_lblNameObj.kcShortcutDesc1.split(`{0}`).join(g_isMac ? `Shift+${g_kCd[g_headerObj.keyRetry]}` : g_kCd[g_headerObj.keyTitleBack]),
|
|
188
188
|
Retry: () => g_lblNameObj.kcShortcutDesc2.split(`{1}`).join(g_kCd[g_headerObj.keyRetry]),
|
|
189
|
+
Pause: () => g_lblNameObj.kcShortcutDesc3.split(`{2}`).join(g_kCd[g_headerObj.keyPause]),
|
|
189
190
|
};
|
|
190
191
|
|
|
191
192
|
/**
|
|
@@ -676,11 +677,15 @@ const updateWindowSiz = () => {
|
|
|
676
677
|
},
|
|
677
678
|
scTitleBack: {
|
|
678
679
|
x: g_btnX(1 / 4) + 20, y: g_sHeight - 50, align: C_ALIGN_LEFT,
|
|
679
|
-
w: g_btnWidth(
|
|
680
|
+
w: g_btnWidth(1 / 4) - 40, h: C_KYC_REPHEIGHT, siz: getFontSize2(getScMsg.TitleBack(), g_btnWidth(1 / 4) - 40, { maxSiz: 13 }),
|
|
680
681
|
},
|
|
681
682
|
scRetry: {
|
|
682
|
-
x: g_btnX(
|
|
683
|
-
w: g_btnWidth(
|
|
683
|
+
x: g_btnX(1 / 2) + 20, y: g_sHeight - 50, align: C_ALIGN_LEFT,
|
|
684
|
+
w: g_btnWidth(1 / 4) - 40, h: C_KYC_REPHEIGHT, siz: getFontSize2(getScMsg.Retry(), g_btnWidth(1 / 4) - 40, { maxSiz: 13 }),
|
|
685
|
+
},
|
|
686
|
+
scPause: {
|
|
687
|
+
x: g_btnX(3 / 4) + 20, y: g_sHeight - 50, align: C_ALIGN_LEFT,
|
|
688
|
+
w: g_btnWidth(1 / 4) - 40, h: C_KYC_REPHEIGHT, siz: getFontSize2(getScMsg.Pause(), g_btnWidth(1 / 4) - 40, { maxSiz: 13 }),
|
|
684
689
|
},
|
|
685
690
|
|
|
686
691
|
/** メイン画面 */
|
|
@@ -739,6 +744,10 @@ const updateWindowSiz = () => {
|
|
|
739
744
|
lblMainScKey: {
|
|
740
745
|
x: g_sWidth + g_headerObj.scAreaWidth - 85, w: 80, h: 20, siz: 12, align: C_ALIGN_RIGHT,
|
|
741
746
|
},
|
|
747
|
+
lblPauseMark: {
|
|
748
|
+
y: g_headerObj.playingY + (g_headerObj.playingHeight + g_posObj.stepYR) / 2 - 75,
|
|
749
|
+
w: g_sWidth, h: 50, siz: 40, background: `#000000cc`, align: C_ALIGN_CENTER,
|
|
750
|
+
},
|
|
742
751
|
|
|
743
752
|
/** 結果画面 */
|
|
744
753
|
lblRank: {
|
|
@@ -1126,6 +1135,7 @@ const C_FRM_FRZATTEMPT = 5;
|
|
|
1126
1135
|
/** ショートカットキー */
|
|
1127
1136
|
const C_KEY_RETRY = 8;
|
|
1128
1137
|
const C_KEY_TITLEBACK = 46;
|
|
1138
|
+
const C_KEY_PAUSE = 19;
|
|
1129
1139
|
|
|
1130
1140
|
/** 判定系共通オブジェクト */
|
|
1131
1141
|
const g_judgObj = {
|
|
@@ -4841,6 +4851,7 @@ const g_lang_lblNameObj = {
|
|
|
4841
4851
|
kcShortcutDesc: `プレイ中ショートカット:`,
|
|
4842
4852
|
kcShortcutDesc1: `タイトルバック: {0}`,
|
|
4843
4853
|
kcShortcutDesc2: `リトライ: {1}`,
|
|
4854
|
+
kcShortcutDesc3: `ポーズ: {2}`,
|
|
4844
4855
|
transKeyDesc: `別キーモードではキーコンフィグ、ColorType等は保存されません`,
|
|
4845
4856
|
colorTypeDesc: `現在のColorTypeの設定では、色変化(Display:Color)は自動的にOFFになります`,
|
|
4846
4857
|
sdShortcutDesc: `Hid+/Sud+時ショートカット:「pageUp」カバーを上へ / 「pageDown」下へ`,
|
|
@@ -4888,6 +4899,7 @@ const g_lang_lblNameObj = {
|
|
|
4888
4899
|
|
|
4889
4900
|
l_retry: `リトライ`,
|
|
4890
4901
|
l_titleBack: `タイトルバック`,
|
|
4902
|
+
l_pause: `ポーズ`,
|
|
4891
4903
|
|
|
4892
4904
|
helpUrl: `https://github.com/cwtickle/danoniplus/wiki/AboutGameSystem`,
|
|
4893
4905
|
securityUrl: `https://github.com/cwtickle/danoniplus/security/policy`,
|
|
@@ -4905,6 +4917,7 @@ const g_lang_lblNameObj = {
|
|
|
4905
4917
|
kcShortcutDesc: `Shortcut during play:`,
|
|
4906
4918
|
kcShortcutDesc1: `Return to title: {0}`,
|
|
4907
4919
|
kcShortcutDesc2: `Retry the game: {1}`,
|
|
4920
|
+
kcShortcutDesc3: `Pause: {2}`,
|
|
4908
4921
|
transKeyDesc: `Key config, Color type, etc. are not saved for alternate keymode`,
|
|
4909
4922
|
colorTypeDesc: `With the current ColorType setting, color change (Display:Color) will be automatically turned OFF.`,
|
|
4910
4923
|
sdShortcutDesc: `When "Hidden+" or "Sudden+" select, "pageUp" cover up / "pageDown" cover down`,
|
|
@@ -4952,6 +4965,7 @@ const g_lang_lblNameObj = {
|
|
|
4952
4965
|
|
|
4953
4966
|
l_retry: `Retry`,
|
|
4954
4967
|
l_titleBack: `Go to title`,
|
|
4968
|
+
l_pause: `Pause`,
|
|
4955
4969
|
|
|
4956
4970
|
helpUrl: `https://github.com/cwtickle/danoniplus-docs/wiki/AboutGameSystem`,
|
|
4957
4971
|
securityUrl: `https://github.com/cwtickle/danoniplus-docs/wiki/SecurityPolicy`,
|