danoniplus 47.1.0 → 47.2.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/js/danoni_main.js +301 -162
- package/package.json +1 -1
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/04/
|
|
7
|
+
* Revised : 2026/04/22
|
|
8
8
|
*
|
|
9
9
|
* https://github.com/cwtickle/danoniplus
|
|
10
10
|
*/
|
|
11
|
-
const g_version = `Ver 47.
|
|
12
|
-
const g_revisedDate = `2026/04/
|
|
11
|
+
const g_version = `Ver 47.2.0`;
|
|
12
|
+
const g_revisedDate = `2026/04/22`;
|
|
13
13
|
|
|
14
14
|
// カスタム用バージョン (danoni_custom.js 等で指定可)
|
|
15
15
|
let g_localVersion = ``;
|
|
@@ -213,6 +213,7 @@ const g_detailObj = {
|
|
|
213
213
|
speedData: [],
|
|
214
214
|
boostData: [],
|
|
215
215
|
toolDif: [],
|
|
216
|
+
miniMapParams: {},
|
|
216
217
|
scoreMinimap: {},
|
|
217
218
|
scoreMinimapReverse: {},
|
|
218
219
|
scoreMinimapHeader: {},
|
|
@@ -3371,153 +3372,237 @@ const storeBaseData = (_scoreId, _scoreObj, _keyCtrlPtn) => {
|
|
|
3371
3372
|
g_detailObj.playingFrame[_scoreId] = playingFrame;
|
|
3372
3373
|
g_detailObj.playingFrameWithBlank[_scoreId] = lastFrame - startFrame;
|
|
3373
3374
|
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
ctxHeader.fillStyle = '#999';
|
|
3395
|
-
ctxHeader.font = `10px ${getBasicFont()}`;
|
|
3396
|
-
ctxHeader.textAlign = 'center';
|
|
3397
|
-
|
|
3398
|
-
for (let j = 0; j < _keyNum; j++) {
|
|
3399
|
-
const x = timeMargin + j * laneWidth + laneWidth / 2;
|
|
3400
|
-
ctxHeader.fillText(g_kCd[g_keyObj[`keyCtrl${_keyCtrlPtn}`][j][0]], x, 10);
|
|
3401
|
-
ctxHeader.strokeStyle = '#444';
|
|
3402
|
-
}
|
|
3403
|
-
|
|
3404
|
-
g_detailObj.scoreMinimapHeader[_scoreId] = canvasHeader;
|
|
3405
|
-
|
|
3406
|
-
// 譜面全体のキャンバスの作成
|
|
3407
|
-
const MAX_CANVAS_HEIGHT = 10000; // 1枚あたりの高さ制限(安全圏)
|
|
3408
|
-
const canvasCount = Math.ceil((mmHeightTotal + mmMarginY * 2) / MAX_CANVAS_HEIGHT);
|
|
3409
|
-
const canvases = [];
|
|
3410
|
-
|
|
3411
|
-
for (let i = 0; i < canvasCount; i++) {
|
|
3412
|
-
const cvs = document.createElement('canvas');
|
|
3413
|
-
const h = (i === canvasCount - 1)
|
|
3414
|
-
? (mmHeightTotal + mmMarginY * 2) - (MAX_CANVAS_HEIGHT * i)
|
|
3415
|
-
: MAX_CANVAS_HEIGHT;
|
|
3416
|
-
|
|
3417
|
-
cvs.width = logicalWidth * dpr;
|
|
3418
|
-
cvs.height = h * dpr;
|
|
3419
|
-
cvs.style.width = `${logicalWidth}px`;
|
|
3420
|
-
cvs.style.height = `${h}px`;
|
|
3421
|
-
cvs.style.display = 'block'; // 隙間防止
|
|
3422
|
-
const ctx = cvs.getContext('2d');
|
|
3423
|
-
ctx.scale(dpr, dpr);
|
|
3424
|
-
canvases.push({ canvas: cvs, ctx: ctx, offsetTop: i * MAX_CANVAS_HEIGHT });
|
|
3425
|
-
}
|
|
3426
|
-
|
|
3427
|
-
// --- 描画先を振り分けるヘルパー関数 ---
|
|
3428
|
-
const drawOnTarget = (y, height, drawFunc) => {
|
|
3429
|
-
canvases.forEach(item => {
|
|
3430
|
-
// 描画要素がCanvasの範囲内に入っているか判定
|
|
3431
|
-
if (y + height >= item.offsetTop && y <= item.offsetTop + (item.canvas.height / dpr)) {
|
|
3432
|
-
item.ctx.save();
|
|
3433
|
-
item.ctx.translate(0, -item.offsetTop);
|
|
3434
|
-
drawFunc(item.ctx);
|
|
3435
|
-
item.ctx.restore();
|
|
3436
|
-
}
|
|
3437
|
-
});
|
|
3438
|
-
};
|
|
3375
|
+
// --- ミニマップ設定 ---
|
|
3376
|
+
g_detailObj.miniMapParams[_scoreId] = {
|
|
3377
|
+
_scoreId, _scoreObj, _keyNum: keyNum,
|
|
3378
|
+
_playingFrame: playingFrame,
|
|
3379
|
+
_firstArrowFrame: firstArrowFrame,
|
|
3380
|
+
_keyCtrlPtn,
|
|
3381
|
+
config: {
|
|
3382
|
+
scale: 1.5,
|
|
3383
|
+
dpr: window.devicePixelRatio || 1,
|
|
3384
|
+
timeMargin: 35,
|
|
3385
|
+
mmWidthBase: (g_sWidth - 500) / 2 + 290,
|
|
3386
|
+
mmMarginY: 2,
|
|
3387
|
+
get laneWidth() {
|
|
3388
|
+
return Math.min(Math.floor((this.mmWidthBase - this.timeMargin) / keyNum), 40);
|
|
3389
|
+
},
|
|
3390
|
+
get logicalWidth() {
|
|
3391
|
+
return this.timeMargin + (this.laneWidth * keyNum);
|
|
3392
|
+
}
|
|
3393
|
+
},
|
|
3394
|
+
};
|
|
3439
3395
|
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
// リバース:最大値(mmHeight)から上へ向かって減る
|
|
3443
|
-
const getY = (frame) => {
|
|
3444
|
-
const relativeFrame = frame - _firstArrowFrame;
|
|
3445
|
-
const rawY = relativeFrame * scale;
|
|
3446
|
-
return _isReverse ? (mmHeightTotal - rawY + mmMarginY) : (rawY + mmMarginY);
|
|
3447
|
-
};
|
|
3396
|
+
// ヘッダー生成
|
|
3397
|
+
g_detailObj.scoreMinimapHeader[_scoreId] = createMinimapHeader(g_detailObj.miniMapParams[_scoreId].config, _keyCtrlPtn, keyNum);
|
|
3448
3398
|
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
};
|
|
3399
|
+
// Canvas保存用配列を空で初期化
|
|
3400
|
+
g_detailObj.scoreMinimap[_scoreId] = null;
|
|
3401
|
+
g_detailObj.scoreMinimapReverse[_scoreId] = null;
|
|
3402
|
+
};
|
|
3454
3403
|
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
ctx.textBaseline = 'middle';
|
|
3466
|
-
ctx.beginPath();
|
|
3467
|
-
ctx.moveTo(timeMargin, y);
|
|
3468
|
-
ctx.lineTo(timeMargin + laneWidth * _keyNum, y);
|
|
3469
|
-
ctx.stroke();
|
|
3470
|
-
ctx.fillText(formatTime(currentFrame), timeMargin, y);
|
|
3471
|
-
});
|
|
3472
|
-
}
|
|
3404
|
+
/**
|
|
3405
|
+
* 指定された高さに基づいて分割されたCanvasリストを生成する
|
|
3406
|
+
* @param {number} _width
|
|
3407
|
+
* @param {number} _totalHeight
|
|
3408
|
+
* @param {number} _dpr
|
|
3409
|
+
* @return {object[]} 分割されたCanvasとそのコンテキスト、オフセット情報を含むリスト
|
|
3410
|
+
*/
|
|
3411
|
+
const createSplitCanvases = (_width, _totalHeight, _dpr) => {
|
|
3412
|
+
// バックバッファ(実際のピクセル数)の最大値を 8000 に設定(iOS Safari 8192px 対策)
|
|
3413
|
+
const BACKING_STORE_LIMIT = 8000;
|
|
3473
3414
|
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3415
|
+
// 論理上の最大高さ(CSSピクセル)を計算
|
|
3416
|
+
// dpr=2なら4000px、dpr=3なら2666px が1枚の限界になる
|
|
3417
|
+
const maxLogicalHeight = Math.max(1, Math.floor(BACKING_STORE_LIMIT / _dpr));
|
|
3418
|
+
if (_totalHeight <= 0) return [];
|
|
3419
|
+
|
|
3420
|
+
const count = Math.ceil(_totalHeight / maxLogicalHeight);
|
|
3421
|
+
const list = [];
|
|
3422
|
+
|
|
3423
|
+
for (let i = 0; i < count; i++) {
|
|
3424
|
+
const cvs = document.createElement('canvas');
|
|
3425
|
+
// 残りの高さを計算
|
|
3426
|
+
const logicalH = (i === count - 1)
|
|
3427
|
+
? _totalHeight - (maxLogicalHeight * i)
|
|
3428
|
+
: maxLogicalHeight;
|
|
3429
|
+
|
|
3430
|
+
// 実際の描画解像度をセット
|
|
3431
|
+
cvs.width = _width * _dpr;
|
|
3432
|
+
cvs.height = logicalH * _dpr;
|
|
3433
|
+
|
|
3434
|
+
// ブラウザ上の表示サイズをセット
|
|
3435
|
+
cvs.style.width = `${_width}px`;
|
|
3436
|
+
cvs.style.height = `${logicalH}px`;
|
|
3437
|
+
cvs.style.display = 'block';
|
|
3438
|
+
|
|
3439
|
+
const ctx = cvs.getContext('2d');
|
|
3440
|
+
ctx.scale(_dpr, _dpr);
|
|
3441
|
+
|
|
3442
|
+
list.push({
|
|
3443
|
+
canvas: cvs,
|
|
3444
|
+
ctx: ctx,
|
|
3445
|
+
offsetTop: i * maxLogicalHeight,
|
|
3446
|
+
logicalHeight: logicalH
|
|
3447
|
+
});
|
|
3448
|
+
}
|
|
3449
|
+
return list;
|
|
3450
|
+
};
|
|
3451
|
+
|
|
3452
|
+
/**
|
|
3453
|
+
* 描画対象のCanvasを判定して描画を実行する
|
|
3454
|
+
* @param {HTMLCanvasElement[]} _canvases
|
|
3455
|
+
* @param {number} _y
|
|
3456
|
+
* @param {number} _h
|
|
3457
|
+
* @param {number} _dpr
|
|
3458
|
+
* @param {Function} _drawFunc
|
|
3459
|
+
*/
|
|
3460
|
+
const distributeDrawing = (_canvases, _y, _h, _dpr, _drawFunc) => {
|
|
3461
|
+
_canvases.forEach(item => {
|
|
3462
|
+
const canvasHeight = item.logicalHeight;
|
|
3463
|
+
if (_y + _h >= item.offsetTop && _y <= item.offsetTop + canvasHeight) {
|
|
3464
|
+
item.ctx.save();
|
|
3465
|
+
item.ctx.translate(0, -item.offsetTop);
|
|
3466
|
+
_drawFunc(item.ctx);
|
|
3467
|
+
item.ctx.restore();
|
|
3491
3468
|
}
|
|
3469
|
+
});
|
|
3470
|
+
};
|
|
3492
3471
|
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3472
|
+
/**
|
|
3473
|
+
* 譜面ミニマップ:キー名を表示するヘッダーキャンバスを作成する
|
|
3474
|
+
* @param {object} _config ミニマップの基本設定
|
|
3475
|
+
* @param {number} _config.dpr デバイスピクセル比
|
|
3476
|
+
* @param {number} _config.timeMargin 時間軸のマージン
|
|
3477
|
+
* @param {number} _config.laneWidth レーンの幅
|
|
3478
|
+
* @param {number} _config.logicalWidth キャンバスの論理幅
|
|
3479
|
+
* @param {string} _keyCtrlPtn キーコントロールパターン
|
|
3480
|
+
* @param {number} _keyNum キー数
|
|
3481
|
+
* @return {HTMLCanvasElement} ヘッダー用のキャンバス要素
|
|
3482
|
+
*/
|
|
3483
|
+
const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
|
|
3484
|
+
const { dpr, timeMargin, laneWidth, logicalWidth } = _config;
|
|
3485
|
+
const headerHeight = 15; // ヘッダーの固定高
|
|
3486
|
+
|
|
3487
|
+
const canvas = document.createElement('canvas');
|
|
3488
|
+
const ctx = canvas.getContext('2d');
|
|
3489
|
+
|
|
3490
|
+
// 解像度と表示サイズの設定
|
|
3491
|
+
canvas.width = logicalWidth * dpr;
|
|
3492
|
+
canvas.height = headerHeight * dpr;
|
|
3493
|
+
canvas.style.width = `${logicalWidth}px`;
|
|
3494
|
+
canvas.style.height = `${headerHeight}px`;
|
|
3495
|
+
canvas.style.display = 'block';
|
|
3496
|
+
|
|
3497
|
+
ctx.scale(dpr, dpr);
|
|
3498
|
+
|
|
3499
|
+
// テキストのスタイル設定
|
|
3500
|
+
ctx.fillStyle = '#999';
|
|
3501
|
+
ctx.font = `10px ${getBasicFont()}`;
|
|
3502
|
+
ctx.textAlign = 'center';
|
|
3503
|
+
ctx.textBaseline = 'middle';
|
|
3504
|
+
|
|
3505
|
+
// 各レーンのキー名を描画
|
|
3506
|
+
for (let j = 0; j < _keyNum; j++) {
|
|
3507
|
+
// config.laneWidth を使って中央座標を計算
|
|
3508
|
+
const x = timeMargin + j * laneWidth + laneWidth / 2;
|
|
3509
|
+
const keyText = g_kCd[g_keyObj[`keyCtrl${_keyCtrlPtn}`][j][0]];
|
|
3510
|
+
|
|
3511
|
+
ctx.fillText(keyText, x, headerHeight / 2 + 2); // 視覚的な中央調整で +2px
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3514
|
+
return canvas;
|
|
3515
|
+
};
|
|
3516
|
+
|
|
3517
|
+
/**
|
|
3518
|
+
* 譜面ミニマップ:譜面ミニマップ本体生成
|
|
3519
|
+
* @param {object} _params ミニマップ生成のためのパラメータオブジェクト
|
|
3520
|
+
* @param {object} _params._scoreObj 譜面データオブジェクト
|
|
3521
|
+
* @param {number} _params._keyNum キー数
|
|
3522
|
+
* @param {number} _params._playingFrame 演奏時間(フレーム数)
|
|
3523
|
+
* @param {number} _params._firstArrowFrame 最初の矢印のフレーム位置
|
|
3524
|
+
* @param {string} _params._keyCtrlPtn キーコントロールパターン
|
|
3525
|
+
* @param {object} _params.config ミニマップの基本設定
|
|
3526
|
+
* @param {number} _params.config.scale ミニマップの時間軸のスケール
|
|
3527
|
+
* @param {number} _params.config.dpr デバイスピクセル比
|
|
3528
|
+
* @param {number} _params.config.timeMargin 時間軸のマージン
|
|
3529
|
+
* @param {number} _params.config.laneWidth レーンの幅
|
|
3530
|
+
* @param {number} _params.config.logicalWidth キャンバスの論理幅
|
|
3531
|
+
* @param {number} _params.config.mmMarginY ミニマップの上下マージン
|
|
3532
|
+
* @param {boolean} _isReverse ミニマップのリバース表示フラグ
|
|
3533
|
+
* @returns {HTMLCanvasElement[]} ミニマップ用のキャンバス要素の配列
|
|
3534
|
+
*/
|
|
3535
|
+
const generateMinimapData = (_params, _isReverse) => {
|
|
3536
|
+
const { _scoreObj, _keyNum, _playingFrame, _firstArrowFrame, _keyCtrlPtn, config } = _params;
|
|
3537
|
+
const { scale, dpr, timeMargin, laneWidth, logicalWidth, mmMarginY } = config;
|
|
3538
|
+
|
|
3539
|
+
const mmHeightTotal = _playingFrame * scale + mmMarginY * 2;
|
|
3540
|
+
const canvases = createSplitCanvases(logicalWidth, mmHeightTotal, dpr);
|
|
3541
|
+
|
|
3542
|
+
const getY = (frame) => {
|
|
3543
|
+
const relativeFrame = frame - _firstArrowFrame;
|
|
3544
|
+
const rawY = relativeFrame * scale;
|
|
3545
|
+
// mmHeightTotalから引くのではなく、中身の演奏時間部分(_playingFrame * scale)を基準にリバース
|
|
3546
|
+
return _isReverse
|
|
3547
|
+
? (_playingFrame * scale - rawY + mmMarginY)
|
|
3548
|
+
: (rawY + mmMarginY);
|
|
3549
|
+
};
|
|
3550
|
+
|
|
3551
|
+
// 1. 時間軸描画
|
|
3552
|
+
const interval = g_fps;
|
|
3553
|
+
for (let f = Math.ceil(_firstArrowFrame / interval) * interval; f <= _firstArrowFrame + _playingFrame; f += interval) {
|
|
3554
|
+
const y = getY(f);
|
|
3555
|
+
distributeDrawing(canvases, y - 5, 10, dpr, (ctx) => {
|
|
3556
|
+
ctx.strokeStyle = '#444';
|
|
3557
|
+
ctx.fillStyle = '#999';
|
|
3558
|
+
ctx.font = `10px ${getBasicFont()}`;
|
|
3559
|
+
ctx.textAlign = 'right';
|
|
3560
|
+
ctx.textBaseline = 'middle';
|
|
3561
|
+
ctx.beginPath(); ctx.moveTo(timeMargin, y); ctx.lineTo(timeMargin + laneWidth * _keyNum, y); ctx.stroke();
|
|
3562
|
+
const [m, s] = transFrameToTimer(f).split(':');
|
|
3563
|
+
ctx.fillText(`${m.padStart(2, '0')}:${s}`, timeMargin, y);
|
|
3564
|
+
});
|
|
3565
|
+
}
|
|
3566
|
+
|
|
3567
|
+
// 2. フリーズノート
|
|
3568
|
+
for (let j = 0; j < _keyNum; j++) {
|
|
3569
|
+
const frz = _scoreObj.frzData[j];
|
|
3570
|
+
for (let k = 0; k < frz.length; k += 2) {
|
|
3571
|
+
const start = frz[k];
|
|
3572
|
+
const end = frz[k + 1];
|
|
3573
|
+
|
|
3574
|
+
// 終了地点がない、またはどちらかが数値でない場合はスキップ
|
|
3575
|
+
if (end === undefined || isNaN(start) || isNaN(end)) {
|
|
3576
|
+
console.warn(`Minimap: Incomplete freeze note pair at lane ${j}, index ${k}`);
|
|
3577
|
+
continue;
|
|
3578
|
+
}
|
|
3579
|
+
const y1 = getY(start);
|
|
3580
|
+
const y2 = getY(end);
|
|
3581
|
+
const top = Math.min(y1, y2);
|
|
3582
|
+
const h = Math.abs(y2 - y1);
|
|
3583
|
+
const x = timeMargin + j * laneWidth;
|
|
3584
|
+
distributeDrawing(canvases, top, h, dpr, (ctx) => {
|
|
3585
|
+
ctx.fillStyle = 'rgba(0, 200, 255, 0.4)';
|
|
3586
|
+
ctx.fillRect(x + 2, top, laneWidth - 3, h);
|
|
3587
|
+
ctx.strokeStyle = 'rgba(0, 200, 255, 0.8)';
|
|
3588
|
+
ctx.strokeRect(x + 2, top, laneWidth - 3, h);
|
|
3504
3589
|
});
|
|
3505
3590
|
}
|
|
3591
|
+
}
|
|
3506
3592
|
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3593
|
+
// 3. 通常ノート
|
|
3594
|
+
for (let j = 0; j < _keyNum; j++) {
|
|
3595
|
+
const color = g_dfColorObj.setColorType2[g_keyObj[`color${_keyCtrlPtn}_0`][j]] || '#ffffff';
|
|
3596
|
+
_scoreObj.arrowData[j].forEach(note => {
|
|
3597
|
+
const y = getY(parseFloat(note));
|
|
3598
|
+
distributeDrawing(canvases, y - 1.5, 3, dpr, (ctx) => {
|
|
3599
|
+
ctx.fillStyle = color;
|
|
3600
|
+
ctx.fillRect(timeMargin + j * laneWidth + 1, y - 1.5, laneWidth - 1, 3);
|
|
3601
|
+
});
|
|
3602
|
+
});
|
|
3603
|
+
}
|
|
3515
3604
|
|
|
3516
|
-
|
|
3517
|
-
// 通常版を生成
|
|
3518
|
-
generateMinimap(_scoreId, _scoreObj, keyNum, playingFrame, firstArrowFrame, false);
|
|
3519
|
-
// リバース版を生成
|
|
3520
|
-
generateMinimap(_scoreId, _scoreObj, keyNum, playingFrame, firstArrowFrame, true);
|
|
3605
|
+
return canvases.map(item => item.canvas);
|
|
3521
3606
|
};
|
|
3522
3607
|
|
|
3523
3608
|
/**
|
|
@@ -7752,21 +7837,57 @@ const makeHighScore = _scoreId => {
|
|
|
7752
7837
|
}
|
|
7753
7838
|
};
|
|
7754
7839
|
|
|
7755
|
-
|
|
7840
|
+
/**
|
|
7841
|
+
* 譜面ミニマップを描画し、適切なスクロール位置へ調整する。
|
|
7842
|
+
* デフォルトはスクロール反転で、現在位置を保つように上下反転する。
|
|
7843
|
+
* @param {string} _scoreId - 描画対象の譜面ID
|
|
7844
|
+
* @param {object} [_options={}] - オプションパラメータ
|
|
7845
|
+
* @param {boolean} [_options._initFlg=false] - 譜面切替モード
|
|
7846
|
+
* - 前の譜面での演奏進行度(時間軸の比率)を算出し、新しい譜面でも同じ進行位置を表示する。
|
|
7847
|
+
* 位置記憶がない場合はフェードイン設定に基づく初期位置を表示。
|
|
7848
|
+
* @param {boolean} [_options._fadeinFlg=false] - フェードインモード。フェードイン設定値を優先して移動する。
|
|
7849
|
+
*/
|
|
7850
|
+
const drawMinimap = (_scoreId, { _initFlg = false, _fadeinFlg = false } = {}) => {
|
|
7756
7851
|
const detailMiniMap = document.getElementById(`detailMiniMap`);
|
|
7757
7852
|
if (detailMiniMap === null) return; // scoreDetailUse=false 等で未生成の場合は何もしない
|
|
7758
7853
|
|
|
7759
|
-
const
|
|
7760
|
-
|
|
7854
|
+
const isRev = g_stateObj.miniMapRevFlg;
|
|
7855
|
+
const subEl = document.getElementById(`detailMiniMapSub`);
|
|
7856
|
+
const currentScrollTop = subEl ? subEl.scrollTop : 0;
|
|
7857
|
+
|
|
7858
|
+
// 前の譜面でスクロール可能な状態だったかを判定
|
|
7859
|
+
const hasPreviousScrollRange = subEl && subEl.scrollHeight > subEl.clientHeight;
|
|
7860
|
+
|
|
7861
|
+
// 前の譜面での「スクロール位置の比率」を計算
|
|
7862
|
+
// 完全に一番上のときは 0、一番下のときは 1 となる比率
|
|
7863
|
+
let progressRatio = 0;
|
|
7864
|
+
if (hasPreviousScrollRange) {
|
|
7865
|
+
const rawRatio = currentScrollTop / (subEl.scrollHeight - subEl.clientHeight);
|
|
7866
|
+
// リバース時は「上が終点」なので、進行度としては反転させる
|
|
7867
|
+
progressRatio = isRev ? (1.0 - rawRatio) : rawRatio;
|
|
7868
|
+
}
|
|
7761
7869
|
|
|
7762
7870
|
// 再描画のため一度クリア
|
|
7763
7871
|
deleteChildspriteAll(`detailMiniMap`);
|
|
7764
7872
|
|
|
7765
|
-
//
|
|
7766
|
-
|
|
7873
|
+
// --- ミニマップ生成/取得 (Lazy Generation) ---
|
|
7874
|
+
let savedCanvases = isRev
|
|
7767
7875
|
? g_detailObj.scoreMinimapReverse[_scoreId]
|
|
7768
7876
|
: g_detailObj.scoreMinimap[_scoreId];
|
|
7769
7877
|
|
|
7878
|
+
if (!savedCanvases) {
|
|
7879
|
+
// 未作成の場合のみミニマップを生成(Lazy Generation)
|
|
7880
|
+
const params = g_detailObj.miniMapParams[_scoreId];
|
|
7881
|
+
savedCanvases = generateMinimapData(params, isRev);
|
|
7882
|
+
|
|
7883
|
+
// 生成したものをキャッシュに保存
|
|
7884
|
+
if (isRev) {
|
|
7885
|
+
g_detailObj.scoreMinimapReverse[_scoreId] = savedCanvases;
|
|
7886
|
+
} else {
|
|
7887
|
+
g_detailObj.scoreMinimap[_scoreId] = savedCanvases;
|
|
7888
|
+
}
|
|
7889
|
+
}
|
|
7890
|
+
|
|
7770
7891
|
// --- ヘッダー部分 ---
|
|
7771
7892
|
const detailMiniMapHeader = createEmptySprite(detailMiniMap, `detailMiniMapHeader`, g_windowObj.detailMiniMapHeader);
|
|
7772
7893
|
$id(`detailMiniMapHeader`).top = (g_stateObj.miniMapRevFlg ? 230 : 0) + `px`;
|
|
@@ -7776,20 +7897,18 @@ const drawMinimap = (_scoreId, _initFlg = false) => {
|
|
|
7776
7897
|
const detailMiniMapSub = createEmptySprite(detailMiniMap, `detailMiniMapSub`, g_windowObj.detailMiniMapSub);
|
|
7777
7898
|
$id(`detailMiniMapSub`).top = (g_stateObj.miniMapRevFlg ? 0 : 15) + `px`;
|
|
7778
7899
|
|
|
7779
|
-
detailMiniMapSub.style
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7900
|
+
Object.assign(detailMiniMapSub.style, {
|
|
7901
|
+
overflowX: 'hidden',
|
|
7902
|
+
overflowY: 'auto',
|
|
7903
|
+
pointerEvents: 'auto',
|
|
7904
|
+
display: 'block',
|
|
7905
|
+
textAlign: 'left',
|
|
7906
|
+
});
|
|
7784
7907
|
|
|
7785
7908
|
if (savedCanvases && Array.isArray(savedCanvases)) {
|
|
7786
7909
|
// 退避したCanvasそのものをDOMに追加(再描画不要で高速)
|
|
7787
|
-
detailMiniMapSub.style.overflow = C_DIS_AUTO;
|
|
7788
|
-
detailMiniMapSub.style.pointerEvents = C_DIS_AUTO;
|
|
7789
7910
|
savedCanvases.forEach(canvas => {
|
|
7790
|
-
canvas.style
|
|
7791
|
-
canvas.style.display = 'block';
|
|
7792
|
-
canvas.style.height = 'auto';
|
|
7911
|
+
Object.assign(canvas.style, { position: 'static', display: 'block', height: 'auto' });
|
|
7793
7912
|
detailMiniMapSub.appendChild(canvas);
|
|
7794
7913
|
});
|
|
7795
7914
|
}
|
|
@@ -7802,16 +7921,36 @@ const drawMinimap = (_scoreId, _initFlg = false) => {
|
|
|
7802
7921
|
getStartFrame(lastFrame, g_stateObj.fadein, _scoreId) - firstArrowFrame
|
|
7803
7922
|
));
|
|
7804
7923
|
const fadeinScrollTop = scrollHeight * fadeinFrameOffset / playingFrame;
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7924
|
+
const visualFadeinPos = isRev ? scrollHeight - fadeinScrollTop : fadeinScrollTop;
|
|
7925
|
+
|
|
7926
|
+
// --- スクロール位置の決定ロジック
|
|
7927
|
+
let targetScrollTop = 0;
|
|
7928
|
+
|
|
7929
|
+
if (_fadeinFlg) {
|
|
7930
|
+
// 【最優先】フェードイン操作時:設定値を強制適用
|
|
7931
|
+
targetScrollTop = visualFadeinPos;
|
|
7932
|
+
} else if (_initFlg) {
|
|
7933
|
+
// 【譜面切替時】
|
|
7934
|
+
if (hasPreviousScrollRange) {
|
|
7935
|
+
// 以前の進行度を継承
|
|
7936
|
+
const visualRatio = isRev ? (1.0 - progressRatio) : progressRatio;
|
|
7937
|
+
targetScrollTop = scrollHeight * visualRatio;
|
|
7938
|
+
} else {
|
|
7939
|
+
// 初回はフェードイン位置
|
|
7940
|
+
targetScrollTop = visualFadeinPos;
|
|
7941
|
+
}
|
|
7942
|
+
} else {
|
|
7943
|
+
// 【リバース切替時】物理反転
|
|
7944
|
+
targetScrollTop = scrollHeight - currentScrollTop;
|
|
7945
|
+
}
|
|
7946
|
+
detailMiniMapSub.scrollTop = targetScrollTop;
|
|
7808
7947
|
|
|
7809
7948
|
if (document.getElementById(`lnkMiniMapRev`) === null) {
|
|
7810
7949
|
scoreDetail.appendChild(
|
|
7811
7950
|
makeDifLblCssButton(`lnkMiniMapRev`, g_lblNameObj.s_rev + `${g_stateObj.miniMapRevFlg ? `↑` : `↓`}`, 8, () => {
|
|
7812
7951
|
g_stateObj.miniMapRevFlg = !g_stateObj.miniMapRevFlg;
|
|
7813
7952
|
lnkMiniMapRev.textContent = g_lblNameObj.s_rev + `${g_stateObj.miniMapRevFlg ? `↑` : `↓`}`;
|
|
7814
|
-
drawMinimap(g_stateObj.scoreId
|
|
7953
|
+
drawMinimap(g_stateObj.scoreId);
|
|
7815
7954
|
createScText(lnkMiniMapRev, `MiniMapRev`, { targetLabel: `lnkMiniMapRev`, x: -12 });
|
|
7816
7955
|
}, g_lblPosObj.lnkMiniMapRev)
|
|
7817
7956
|
);
|
|
@@ -8007,7 +8146,7 @@ const setDifficulty = (_initFlg) => {
|
|
|
8007
8146
|
drawDensityGraph(g_stateObj.scoreId);
|
|
8008
8147
|
makeDifInfo(g_stateObj.scoreId);
|
|
8009
8148
|
makeHighScore(g_stateObj.scoreId);
|
|
8010
|
-
drawMinimap(g_stateObj.scoreId);
|
|
8149
|
+
drawMinimap(g_stateObj.scoreId, { _initFlg: true });
|
|
8011
8150
|
}
|
|
8012
8151
|
|
|
8013
8152
|
// 楽曲データの表示
|
|
@@ -8305,7 +8444,7 @@ const createOptionWindow = _sprite => {
|
|
|
8305
8444
|
fadeinSlider.value = g_stateObj.fadein;
|
|
8306
8445
|
lnkFadein.textContent = `${g_stateObj.fadein}${g_lblNameObj.percent}`;
|
|
8307
8446
|
updateSettingSummary();
|
|
8308
|
-
drawMinimap(g_stateObj.scoreId);
|
|
8447
|
+
drawMinimap(g_stateObj.scoreId, { _fadeinFlg: true });
|
|
8309
8448
|
};
|
|
8310
8449
|
|
|
8311
8450
|
multiAppend(spriteList.fadein,
|
|
@@ -8324,7 +8463,7 @@ const createOptionWindow = _sprite => {
|
|
|
8324
8463
|
fadeinSlider.addEventListener(`input`, () => {
|
|
8325
8464
|
g_stateObj.fadein = inputSlider(fadeinSlider, lnkFadein, `fadein`);
|
|
8326
8465
|
updateSettingSummary();
|
|
8327
|
-
drawMinimap(g_stateObj.scoreId);
|
|
8466
|
+
drawMinimap(g_stateObj.scoreId, { _fadeinFlg: true });
|
|
8328
8467
|
}, false);
|
|
8329
8468
|
|
|
8330
8469
|
// ---------------------------------------------------
|