danoniplus 49.5.0 → 49.6.1

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 CHANGED
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * Source by tickle
6
6
  * Created : 2018/10/08
7
- * Revised : 2026/08/12
7
+ * Revised : 2026/08/19
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 49.5.0`;
12
- const g_revisedDate = `2026/08/12`;
11
+ const g_version = `Ver 49.6.1`;
12
+ const g_revisedDate = `2026/08/19`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -135,6 +135,19 @@ let g_fps = 60;
135
135
  // プレイ画面再生時の内部スケジューリング用のマージン時間(100ms)
136
136
  let g_scheduleLead = 0.1;
137
137
 
138
+ // フレーム進行を音源クロック(AudioContext.currentTime)基準で補正するか
139
+ // - false の場合は従来通り performance.now() 基準で動作
140
+ let g_audioClockSync = true;
141
+
142
+ // 出力遅延(AudioContext.outputLatency)をタイミング補正に含めるか
143
+ // - true にすると出力デバイス(有線/Bluetooth等)によらず同じAdjustmentが使えるが、
144
+ // 既存のAdjustment設定値と互換性がなくなるため既定は false
145
+ let g_audioLatencyCompensation = false;
146
+
147
+ // 次フレームまでの待機時間の上限(ms)
148
+ // - 音源クロックが一時的に停止した際に待ち続けないようにするための保険
149
+ let g_maxFrameWait = 50;
150
+
138
151
  // 譜面データの&区切りを有効にするか
139
152
  let g_enableAmpersandSplit = true;
140
153
 
@@ -384,7 +397,7 @@ const g_convFunc = {
384
397
  * @param {string} _checkStr
385
398
  * @param {string} _default
386
399
  * @param {string} [_type='string']
387
- * @returns
400
+ * @returns {number|boolean|string}
388
401
  */
389
402
  const setVal = (_checkStr, _default, _type = C_TYP_STRING) =>
390
403
  hasValN(_checkStr) ? g_convFunc[_type](_checkStr, _default) : _default;
@@ -1570,7 +1583,7 @@ const getFontSize2 = (_str, _maxWidth, { font = getBasicFont(), maxSiz = 14, min
1570
1583
  };
1571
1584
 
1572
1585
  /**
1573
- * 配列中から最も長い文字列を抽出
1586
+ * 配列中から最も長い文字列を抽出(本体では未使用)
1574
1587
  * @param {string[]} _array
1575
1588
  * @returns {string}
1576
1589
  */
@@ -1688,8 +1701,8 @@ const dragDiv = (_divName, { minX = 0, minY = 0, maxX = g_sWidth, maxY = g_sHeig
1688
1701
  const nextY = div.offsetTop + evt.movementY;
1689
1702
  const clampMaxX = (maxX === minX) ? maxX : (maxX - div.offsetWidth);
1690
1703
  const clampMaxY = (maxY === minY) ? maxY : (maxY - div.offsetHeight);
1691
- div.style.left = Math.min(Math.max(nextX, minX), clampMaxX) + 'px';
1692
- div.style.top = Math.min(Math.max(nextY, minY), clampMaxY) + 'px';
1704
+ div.style.left = wUnit(Math.min(Math.max(nextX, minX), clampMaxX));
1705
+ div.style.top = wUnit(Math.min(Math.max(nextY, minY), clampMaxY));
1693
1706
  div.style.position = 'absolute';
1694
1707
  div.draggable = false;
1695
1708
  div.setPointerCapture(evt.pointerId);
@@ -1826,7 +1839,7 @@ const createMultipleSprite = (_baseName, _num, { x = 0, priority = g_transPriori
1826
1839
  for (let j = 0; j <= _num; j++) {
1827
1840
  createEmptySprite(sprite, `${_baseName}${j}`);
1828
1841
  }
1829
- addTransform(_baseName, `root`, `translateX(${x}px)`, priority);
1842
+ addTransform(_baseName, `root`, `translateX(${wUnit(x)})`, priority);
1830
1843
  return sprite;
1831
1844
  };
1832
1845
 
@@ -2296,13 +2309,19 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
2296
2309
  maxDepth = tmpDepth;
2297
2310
  }
2298
2311
 
2312
+ const colorObjFlg = tmpSpriteData[2]?.startsWith(`[c]`) || false;
2313
+ const transformFlg = tmpSpriteData[2]?.startsWith(`[t]`) || false;
2299
2314
  const tmpObj = {
2300
2315
  path: escapeHtml(tmpSpriteData[2] ?? ``, g_escapeStr.escapeCode), // 画像パス or テキスト
2301
2316
  class: escapeHtml(tmpSpriteData[3] ?? ``), // CSSクラス
2302
- left: setVal(tmpSpriteData[4], `0`).includes(`{`) ?
2303
- `${setVal(tmpSpriteData[4], 0)}` : `{${setVal(tmpSpriteData[4], 0)}}`, // X座標
2304
- top: setVal(tmpSpriteData[5], `0`).includes(`{`) ?
2305
- `${setVal(tmpSpriteData[5], 0)}` : `{${setVal(tmpSpriteData[5], 0)}}`, // Y座標
2317
+ left: transformFlg
2318
+ ? setVal(tmpSpriteData[4], 1000, C_TYP_NUMBER) // [t]のみtransformのPriority
2319
+ : setVal(tmpSpriteData[4], `0`).includes(`{`)
2320
+ ? `${setVal(tmpSpriteData[4], 0)}`
2321
+ : `{${setVal(tmpSpriteData[4], 0)}}`, // X座標
2322
+ top: setVal(tmpSpriteData[5], `0`).includes(`{`)
2323
+ ? `${setVal(tmpSpriteData[5], 0)}`
2324
+ : `{${setVal(tmpSpriteData[5], 0)}}`, // Y座標
2306
2325
  width: `${setIntVal(tmpSpriteData[6])}`, // spanタグの場合は font-size
2307
2326
  height: `${escapeHtml(tmpSpriteData[7] ?? ``)}`, // spanタグの場合は color(文字列可)
2308
2327
  opacity: setVal(tmpSpriteData[8], 1, C_TYP_FLOAT),
@@ -2319,7 +2338,6 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
2319
2338
  checkDuplicatedObjects(spriteData[tmpFrame]);
2320
2339
 
2321
2340
  const emptyPatterns = [`[loop]`, `[jump]`];
2322
- const colorObjFlg = tmpSpriteData[2]?.startsWith(`[c]`) || false;
2323
2341
  const spriteFrameData = spriteData[tmpFrame][dataCnts] = {
2324
2342
  depth: tmpDepth,
2325
2343
  };
@@ -2327,9 +2345,15 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
2327
2345
  if (colorObjFlg) {
2328
2346
  // [c]始まりの場合、カラーオブジェクト用の作成準備を行う
2329
2347
  const data = tmpObj.path.slice(`[c]`.length).split(`/`);
2348
+ let objPart = data[0];
2349
+ if (!isNaN(parseInt(data[0]))) {
2350
+ const keyCtrlPtn = `${g_keyObj.currentKey}_${g_keyObj.currentPtn}`;
2351
+ objPart = g_keyObj[`stepRtn${keyCtrlPtn}`][data[0]];
2352
+ spriteFrameData.transform = parseInt(data[0]);
2353
+ }
2330
2354
  spriteFrameData.colorObjInfo = {
2331
2355
  x: tmpObj.left, y: tmpObj.top, w: tmpObj.width, h: tmpObj.height,
2332
- rotate: setVal(data[0], `0`), opacity: tmpObj.opacity,
2356
+ rotate: setVal(objPart, `0`), opacity: tmpObj.opacity,
2333
2357
  background: makeColorGradation(setVal(data[1], `#ffffff`), { _defaultColorgrd: false }),
2334
2358
  animationName: tmpObj.animationName,
2335
2359
  animationDuration: `${tmpObj.animationDuration}s`,
@@ -2339,6 +2363,12 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
2339
2363
  if (tmpObj.animationFillMode !== undefined) {
2340
2364
  spriteFrameData.colorObjInfo.animationFillMode = tmpObj.animationFillMode;
2341
2365
  }
2366
+ } else if (transformFlg) {
2367
+ // [t]始まりの場合、レイヤーに対してtransformを掛ける準備を行う
2368
+ const transformData = tmpObj.path.slice(`[t]`.length);
2369
+ spriteFrameData.transform = transformData || ``;
2370
+ spriteFrameData.transformId = tmpObj.class;
2371
+ spriteFrameData.transPriority = tmpObj.left;
2342
2372
 
2343
2373
  } else if (tmpObj.path === ``) {
2344
2374
  spriteFrameData.command = ``;
@@ -2422,6 +2452,10 @@ const drawBaseSpriteData = (_spriteData, _name, _condition = true) => {
2422
2452
  }
2423
2453
  } else {
2424
2454
  if (_condition) {
2455
+ if (_spriteData.colorObjInfo === undefined && _spriteData.transform === undefined) {
2456
+ baseSprite.innerHTML = convertStrToVal(_spriteData.htmlText);
2457
+ return;
2458
+ }
2425
2459
  if (_spriteData.colorObjInfo !== undefined) {
2426
2460
  const colorObjClass = _spriteData.colorObjClass?.split(`/`) ?? [];
2427
2461
  const id = `${_name}${_spriteData.depth}${_spriteData.colorObjId}`;
@@ -2429,8 +2463,32 @@ const drawBaseSpriteData = (_spriteData, _name, _condition = true) => {
2429
2463
  baseSprite.appendChild(
2430
2464
  createColorObject2(id, _spriteData.colorObjInfo, ...colorObjClass)
2431
2465
  );
2432
- } else {
2433
- baseSprite.innerHTML = convertStrToVal(_spriteData.htmlText);
2466
+ }
2467
+ if (_spriteData.transform !== undefined) {
2468
+ const targetId = `${_name}Sprite${_spriteData.depth}`;
2469
+
2470
+ if (!isNaN(parseInt(_spriteData.transform))) {
2471
+ // PlayWindow由来のtransformを継承(別transformId)
2472
+ const transformId = `${_name}${_spriteData.depth}PlayWindow`;
2473
+ const transformData = getTransform(`mainSprite`, `playWindow`);
2474
+
2475
+ if (hasVal(transformData)) {
2476
+ if (transformData !== getTransform(targetId, transformId)) {
2477
+ addTransform(targetId, transformId, transformData, g_transPriority.playWindow);
2478
+ }
2479
+ } else {
2480
+ delTransform(targetId, transformId);
2481
+ }
2482
+ } else {
2483
+ // 明示指定のtransformを適用
2484
+ const transformId = _spriteData.transformId || `${_name}${_spriteData.depth}`;
2485
+
2486
+ if (hasVal(_spriteData.transform)) {
2487
+ addTransform(targetId, transformId, _spriteData.transform, _spriteData.transPriority);
2488
+ } else {
2489
+ delTransform(targetId, transformId);
2490
+ }
2491
+ }
2434
2492
  }
2435
2493
  }
2436
2494
  }
@@ -2517,6 +2575,7 @@ class AudioPlayer {
2517
2575
  this._gain = this._context.createGain();
2518
2576
  this._gain.connect(this._context.destination);
2519
2577
  this._startTime = 0;
2578
+ this._scheduledTime = 0;
2520
2579
  this._fadeinPosition = 0;
2521
2580
  this._eventListeners = {};
2522
2581
  this.playbackRate = 1;
@@ -2541,17 +2600,25 @@ class AudioPlayer {
2541
2600
  * - scheduleLead は安定した再生タイミングを確保するための内部マージン
2542
2601
  */
2543
2602
  play(_adjustmentTime = 0) {
2603
+ // AudioContextの時計は1回だけ読み、以降はその値を使い回す
2604
+ // - currentTimeはレンダークォンタム単位でしか進まないため、複数回読むと
2605
+ // 予約時刻と論理開始時刻が最大1クォンタム分ずれる
2606
+ const ctxNow = this._context.currentTime;
2607
+
2544
2608
  this._source = this._context.createBufferSource();
2545
2609
  this._source.buffer = this._buffer;
2546
2610
  this._source.playbackRate.value = this.playbackRate;
2547
2611
  this._source.connect(this._gain);
2548
2612
 
2549
2613
  // 実際の予約時刻(内部スケジューリング用のマージンを含む)
2550
- const startAt = this._context.currentTime + g_scheduleLead + _adjustmentTime;
2614
+ const startAt = ctxNow + g_scheduleLead + _adjustmentTime;
2551
2615
  this._source.start(startAt, this._fadeinPosition);
2552
2616
 
2553
2617
  // ゲーム側の論理的開始時刻(g_scheduleLead を含めない)
2554
- this._startTime = this._context.currentTime + _adjustmentTime;
2618
+ this._startTime = ctxNow + _adjustmentTime;
2619
+
2620
+ // 実際に音が鳴り始めるAudioContext上の時刻(フレーム同期の基準)
2621
+ this._scheduledTime = startAt;
2555
2622
  }
2556
2623
 
2557
2624
  pause() {
@@ -2576,6 +2643,26 @@ class AudioPlayer {
2576
2643
  return this._context.currentTime - this._startTime + this._fadeinPosition;
2577
2644
  }
2578
2645
 
2646
+ /** AudioContextの現在時刻(秒) */
2647
+ get contextTime() {
2648
+ return this._context.currentTime;
2649
+ }
2650
+
2651
+ /** 実際に音が鳴り始めるAudioContext上の時刻(秒) */
2652
+ get scheduledTime() {
2653
+ return this._scheduledTime;
2654
+ }
2655
+
2656
+ /** 音が出力デバイスから実際に出るまでの遅延(秒) */
2657
+ get outputLatency() {
2658
+ return this._context.outputLatency || this._context.baseLatency || 0;
2659
+ }
2660
+
2661
+ /** AudioContextの状態(running / suspended / closed) */
2662
+ get contextState() {
2663
+ return this._context.state;
2664
+ }
2665
+
2579
2666
  set currentTime(_currentTime) {
2580
2667
  this._fadeinPosition = _currentTime;
2581
2668
  }
@@ -2663,6 +2750,37 @@ const getSharedAudioContext = () => {
2663
2750
  return g_sharedAudioContext;
2664
2751
  };
2665
2752
 
2753
+ /**
2754
+ * AudioContextのウォームアップ
2755
+ * - 生成直後・resume直後のAudioContextは出力デバイスの起動待ちのため、
2756
+ * しばらく currentTime が進まない(環境により数十〜数百ms)
2757
+ * - この状態で音源をスケジュールすると、起動に要した時間がそのまま
2758
+ * 音源と譜面のずれになるため、無音を1回鳴らして時計が動き出すまで待つ
2759
+ * @returns {Promise<void>}
2760
+ */
2761
+ const warmUpAudioContext = async () => {
2762
+ const ctx = getSharedAudioContext();
2763
+ if (ctx.state !== `running`) {
2764
+ await ctx.resume().catch(() => { });
2765
+ }
2766
+ if (ctx.state !== `running`) {
2767
+ return; // ジェスチャー未取得等でresumeできない場合は何もしない
2768
+ }
2769
+
2770
+ // 無音を1サンプルだけ鳴らして出力デバイスを起動させる
2771
+ const source = ctx.createBufferSource();
2772
+ source.buffer = ctx.createBuffer(1, 1, ctx.sampleRate);
2773
+ source.connect(ctx.destination);
2774
+ source.start();
2775
+
2776
+ // currentTimeが実際に進み始めるまで待機(最大500ms)
2777
+ const baseTime = ctx.currentTime;
2778
+ const limitTime = performance.now() + 500;
2779
+ while (ctx.currentTime === baseTime && performance.now() < limitTime) {
2780
+ await new Promise(resolve => setTimeout(resolve, 10));
2781
+ }
2782
+ };
2783
+
2666
2784
  /**
2667
2785
  * クリップボードコピー関数
2668
2786
  * 入力値をクリップボードへコピーし、メッセージを表示
@@ -3507,8 +3625,8 @@ const createSplitCanvases = (_width, _totalHeight) => {
3507
3625
  cvs.height = logicalH * g_dpr;
3508
3626
 
3509
3627
  // ブラウザ上の表示サイズをセット
3510
- cvs.style.width = `${_width}px`;
3511
- cvs.style.height = `${logicalH}px`;
3628
+ cvs.style.width = wUnit(_width);
3629
+ cvs.style.height = wUnit(logicalH);
3512
3630
  cvs.style.display = 'block';
3513
3631
 
3514
3632
  const ctx = cvs.getContext('2d');
@@ -3568,8 +3686,8 @@ const createMinimapHeader = (_config, _keyCtrlPtn, _keyNum) => {
3568
3686
  // 解像度と表示サイズの設定
3569
3687
  canvas.width = logicalWidth * g_dpr;
3570
3688
  canvas.height = headerHeight * g_dpr;
3571
- canvas.style.width = `${logicalWidth}px`;
3572
- canvas.style.height = `${headerHeight}px`;
3689
+ canvas.style.width = wUnit(logicalWidth);
3690
+ canvas.style.height = wUnit(headerHeight);
3573
3691
  canvas.style.display = 'block';
3574
3692
 
3575
3693
  ctx.scale(g_dpr, g_dpr);
@@ -4419,8 +4537,8 @@ const headerConvert = _dosObj => {
4419
4537
  obj.playingY = setIntVal(_dosObj.playingY, g_presetObj.playingY ?? 0);
4420
4538
 
4421
4539
  // ステップゾーン位置
4422
- g_posObj.stepY = (isNaN(parseFloat(_dosObj.stepY)) ? C_STEP_Y : parseFloat(_dosObj.stepY));
4423
- g_posObj.stepYR = (isNaN(parseFloat(_dosObj.stepYR)) ? C_STEP_YR : parseFloat(_dosObj.stepYR));
4540
+ g_posObj.stepY = setVal(_dosObj.stepY, C_STEP_Y, C_TYP_FLOAT);
4541
+ g_posObj.stepYR = setVal(_dosObj.stepYR, C_STEP_YR, C_TYP_FLOAT);
4424
4542
  g_posObj.stepDiffY = g_posObj.stepY - C_STEP_Y;
4425
4543
  g_posObj.distY = obj.playingHeight - C_STEP_Y + g_posObj.stepYR;
4426
4544
  g_posObj.reverseStepY = g_posObj.distY - g_posObj.stepY - g_posObj.stepDiffY - C_ARW_WIDTH;
@@ -4431,18 +4549,18 @@ const headerConvert = _dosObj => {
4431
4549
  obj.baseSpeed = 1 + ((g_posObj.distY - (g_posObj.stepY - C_STEP_Y) * 2) / (500 - C_STEP_Y) - 1) * 0.85;
4432
4550
 
4433
4551
  // 矢印・フリーズアロー判定位置補正
4434
- g_diffObj.arrowJdgX = (isNaN(parseFloat(_dosObj.arrowJdgX)) ? 0 : parseFloat(_dosObj.arrowJdgX));
4435
- g_diffObj.arrowJdgY = (isNaN(parseFloat(_dosObj.arrowJdgY)) ? 0 : parseFloat(_dosObj.arrowJdgY));
4436
- g_diffObj.frzJdgX = (isNaN(parseFloat(_dosObj.frzJdgX)) ? 0 : parseFloat(_dosObj.frzJdgX));
4437
- g_diffObj.frzJdgY = (isNaN(parseFloat(_dosObj.frzJdgY)) ? 0 : parseFloat(_dosObj.frzJdgY));
4552
+ g_diffObj.arrowJdgX = setVal(_dosObj.arrowJdgX, 0, C_TYP_FLOAT);
4553
+ g_diffObj.arrowJdgY = setVal(_dosObj.arrowJdgY, 0, C_TYP_FLOAT);
4554
+ g_diffObj.frzJdgX = setVal(_dosObj.frzJdgX, 0, C_TYP_FLOAT);
4555
+ g_diffObj.frzJdgY = setVal(_dosObj.frzJdgY, 0, C_TYP_FLOAT);
4438
4556
  g_diffInitObj.arrowJdgX = g_diffObj.arrowJdgX;
4439
4557
  g_diffInitObj.arrowJdgY = g_diffObj.arrowJdgY;
4440
4558
  g_diffInitObj.frzJdgX = g_diffObj.frzJdgX;
4441
4559
  g_diffInitObj.frzJdgY = g_diffObj.frzJdgY;
4442
4560
 
4443
4561
  // ショートカット表示位置補正
4444
- g_diffObj.shortcutX = (isNaN(parseFloat(_dosObj.shortcutX)) ? 0 : parseFloat(_dosObj.shortcutX));
4445
- g_diffObj.shortcutY = (isNaN(parseFloat(_dosObj.shortcutY)) ? 0 : parseFloat(_dosObj.shortcutY));
4562
+ g_diffObj.shortcutX = setVal(_dosObj.shortcutX, 0, C_TYP_FLOAT);
4563
+ g_diffObj.shortcutY = setVal(_dosObj.shortcutY, 0, C_TYP_FLOAT);
4446
4564
  g_diffInitObj.shortcutX = g_diffObj.shortcutX;
4447
4565
  g_diffInitObj.shortcutY = g_diffObj.shortcutY;
4448
4566
 
@@ -6011,7 +6129,7 @@ const titleInit = (_initFlg = false) => {
6011
6129
  }, g_lblPosObj.btnComment, g_cssObj.button_Default),
6012
6130
  );
6013
6131
  if (g_headerObj.musicSelectUse && getQueryParamVal(`scoreId`) === null) {
6014
- lblComment.style.height = `${g_sHeight - 100}px`;
6132
+ lblComment.style.height = wUnit(g_sHeight - 100);
6015
6133
  }
6016
6134
  setUserSelect(lblComment.style, `text`);
6017
6135
  }
@@ -6555,7 +6673,7 @@ const changeMSelect = (_num, _initFlg = false) => {
6555
6673
  g_lblPosObj.lblDifNameInfoM.w, { maxSiz: g_lblPosObj.lblDifNameInfoM.siz }));
6556
6674
  lblDiffiInfoM.style.fontSize = lblDifNameInfoM.style.fontSize;
6557
6675
  lblNotesInfoM.style.fontSize = lblDifNameInfoM.style.fontSize;
6558
- lblCommentInfoM.style.top = `${getStrHeight(lblDifNameInfoM.innerHTML, parseFloat(lblDifNameInfoM.style.fontSize))}px`;
6676
+ lblCommentInfoM.style.top = wUnit(getStrHeight(lblDifNameInfoM.innerHTML, parseFloat(lblDifNameInfoM.style.fontSize)));
6559
6677
  lblCommentInfoM.innerHTML = convertStrToVal(g_headerObj[`commentVal${g_settings.musicIdxNum}`]);
6560
6678
  lblCommentM.scrollTop = 0;
6561
6679
 
@@ -8012,12 +8130,12 @@ const drawMinimap = (_scoreId, { _initFlg = false, _fadeinFlg = false } = {}) =>
8012
8130
 
8013
8131
  // --- ヘッダー部分 ---
8014
8132
  const detailMiniMapHeader = createEmptySprite(detailMiniMap, `detailMiniMapHeader`, g_windowObj.detailMiniMapHeader);
8015
- $id(`detailMiniMapHeader`).top = (g_stateObj.miniMapRevFlg ? 230 + g_sHeight - 500 : 0) + `px`;
8133
+ $id(`detailMiniMapHeader`).top = wUnit(g_stateObj.miniMapRevFlg ? 230 + g_sHeight - 500 : 0);
8016
8134
  detailMiniMapHeader.appendChild(g_detailObj.scoreMinimapHeader[kPtn]);
8017
8135
 
8018
8136
  // --- メイン(譜面)部分 ---
8019
8137
  const detailMiniMapSub = createEmptySprite(detailMiniMap, `detailMiniMapSub`, g_windowObj.detailMiniMapSub);
8020
- $id(`detailMiniMapSub`).top = (g_stateObj.miniMapRevFlg ? 0 : 15) + `px`;
8138
+ $id(`detailMiniMapSub`).top = wUnit(g_stateObj.miniMapRevFlg ? 0 : 15);
8021
8139
 
8022
8140
  Object.assign(detailMiniMapSub.style, {
8023
8141
  overflowX: 'hidden',
@@ -9854,17 +9972,16 @@ const showToast = _msg => {
9854
9972
  if (existing) existing.remove();
9855
9973
 
9856
9974
  const toast = createDivCss2Label(`previewToast`, _msg, {
9857
- x: g_btnX() + g_btnWidth() / 2, y: 50, w: g_btnWidth() / 2, h: 10,
9975
+ x: g_btnX() + g_btnWidth() / 2, y: 50, w: g_btnWidth() / 2, h: 10, siz: 12,
9858
9976
  transform: `translateX(-50%)`,
9859
9977
  background: `rgba(0,40,80,0.92)`,
9860
9978
  color: `#aaddff`,
9861
9979
  border: `1px solid #3366aa`,
9862
9980
  borderRadius: `6px`,
9863
9981
  padding: `6px 16px`,
9864
- fontSize: `12px`,
9865
9982
  fontFamily: `monospace`,
9866
9983
  whiteSpace: `nowrap`,
9867
- pointerEvents: `none`,
9984
+ pointerEvents: C_DIS_NONE,
9868
9985
  transition: `opacity 0.4s`,
9869
9986
  opacity: `1`,
9870
9987
  });
@@ -10382,14 +10499,20 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
10382
10499
 
10383
10500
  /**
10384
10501
  * 指定関数群だけを抽出したtransform文字列を作成
10502
+ * @param {string} _cssString
10503
+ * @param {string[]} _funcNames
10504
+ * @returns {string}
10385
10505
  */
10386
- const extractTransformFuncs = (cssString, funcNames) => {
10387
- const pattern = new RegExp(`\\b(${funcNames.join(`|`)})\\([^)]*\\)`, `g`);
10388
- return (cssString.match(pattern) || []).join(` `);
10506
+ const extractTransformFuncs = (_cssString, _funcNames) => {
10507
+ const pattern = new RegExp(`\\b(${_funcNames.join(`|`)})\\([^)]*\\)`, `g`);
10508
+ return (_cssString.match(pattern) || []).join(` `);
10389
10509
  };
10390
10510
 
10391
10511
  /**
10392
10512
  * 対象レーンのレイヤー番号を取得
10513
+ * @param {number} _j
10514
+ * @param {number} _posj
10515
+ * @returns {number}
10393
10516
  */
10394
10517
  const getLayerIdx = (_j, _posj) => {
10395
10518
  const baseLayer = g_keyObj[`layerGroup${keyCtrlPtn}`]?.[_j] || 0;
@@ -10399,6 +10522,8 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
10399
10522
 
10400
10523
  /**
10401
10524
  * 対象レイヤーの回転変形を取得
10525
+ * @param {string} _keyCtrlPtn
10526
+ * @returns {string[]}
10402
10527
  */
10403
10528
  const getLayerRotateParts = _keyCtrlPtn => {
10404
10529
  const raw = g_keyObj[`layerTrans${_keyCtrlPtn}`]?.[0] ?? [``, ``, ``, ``];
@@ -10414,7 +10539,7 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
10414
10539
 
10415
10540
  g_keyCopyLists.simpleDef.forEach(header => updateKeyInfo(header, keyCtrlPtn));
10416
10541
  addTransform(`keyconSprite`, `root`, `scale(${g_keyObj.scale})`, g_transPriority.scale);
10417
- keyconSprite.style.height = `${parseFloat(keyconSprite.style.height) / ((1 + g_keyObj.scale) / 2)}px`;
10542
+ keyconSprite.style.height = wUnit(parseFloat(keyconSprite.style.height) / ((1 + g_keyObj.scale) / 2));
10418
10543
  const kWidth = parseInt(keyconSprite.style.width);
10419
10544
  changeSetColor();
10420
10545
 
@@ -11228,10 +11353,10 @@ const keyConfigInit = (_kcType = g_kcType, _initFlg = false) => {
11228
11353
  g_headerObj[`key${selectedKc}Def`] = setKey;
11229
11354
  document.getElementById(`sc${selectedKc}`).textContent = getScMsg[selectedKc]();
11230
11355
  document.getElementById(`sc${selectedKc}`).style.fontSize =
11231
- `${getFontSize2(getScMsg[selectedKc](), g_btnWidth(5 / 12) - 40, { maxSiz: g_limitObj.mainSiz })}px`;
11356
+ wUnit(getFontSize2(getScMsg[selectedKc](), g_btnWidth(5 / 12) - 40, { maxSiz: g_limitObj.mainSiz }));
11232
11357
  if (g_isMac) {
11233
11358
  scTitleBack.textContent = getScMsg.TitleBack();
11234
- scTitleBack.style.fontSize = `${getFontSize2(getScMsg.TitleBack(), g_btnWidth(5 / 12) - 40, { maxSiz: g_limitObj.mainSiz })}px`;
11359
+ scTitleBack.style.fontSize = wUnit(getFontSize2(getScMsg.TitleBack(), g_btnWidth(5 / 12) - 40, { maxSiz: g_limitObj.mainSiz }));
11235
11360
  }
11236
11361
  changeConfigColor(document.getElementById(`sc${selectedKc}`),
11237
11362
  g_headerObj[`key${selectedKc}`] === g_headerObj[`key${selectedKc}Def2`] ?
@@ -11321,6 +11446,7 @@ const completeTransKeyPtn = (_keyList) => {
11321
11446
  });
11322
11447
  };
11323
11448
 
11449
+ // KeyLockボタンを押したときの表示切替
11324
11450
  const toggleKcDesc = () => {
11325
11451
  if (document.getElementById(`kcDesc`) !== null) {
11326
11452
  kcDesc.textContent = getKcDescMsg();
@@ -11331,11 +11457,13 @@ const toggleKcDesc = () => {
11331
11457
  }
11332
11458
  };
11333
11459
 
11460
+ // KeyLockボタンの表示文字列を取得
11334
11461
  const getKcDescMsg = () =>
11335
11462
  g_stateObj.keyLockFlg
11336
11463
  ? g_lblNameObj.kcNonDesc
11337
11464
  : g_lblNameObj.kcDesc.split(`{0}`).join(g_kCd[C_KEY_RETRY]).split(`{1}:`).join(g_isMac ? `` : `Delete:`);
11338
11465
 
11466
+ // KeyLockボタンで使用する絵文字の取得
11339
11467
  const getKeyLockName = () =>
11340
11468
  `${g_lblNameObj.b_keyLock}${g_stateObj.keyLockFlg ? g_emojiObj.locked : g_emojiObj.unlocked}`;
11341
11469
 
@@ -11661,6 +11789,8 @@ const keyconfigKeyboardPreview = (() => {
11661
11789
 
11662
11790
  /**
11663
11791
  * Canvasの共通初期化処理
11792
+ * @param {HTMLCanvasElement} canvas
11793
+ * @returns {CanvasRenderingContext2D|null}
11664
11794
  */
11665
11795
  const setupCanvasContext = (canvas) => {
11666
11796
  if (!canvas) return null;
@@ -11675,6 +11805,15 @@ const keyconfigKeyboardPreview = (() => {
11675
11805
  return ctx;
11676
11806
  };
11677
11807
 
11808
+ /**
11809
+ * 円角矩形を描画する
11810
+ * @param {CanvasRenderingContext2D} ctx
11811
+ * @param {number} x
11812
+ * @param {number} y
11813
+ * @param {number} w
11814
+ * @param {number} h
11815
+ * @param {number} r
11816
+ */
11678
11817
  const roundRect = (ctx, x, y, w, h, r) => {
11679
11818
  ctx.beginPath();
11680
11819
  ctx.moveTo(x + r, y);
@@ -12142,6 +12281,9 @@ const loadMusic = async () => {
12142
12281
  }
12143
12282
 
12144
12283
  if (loadSucceeded) {
12284
+ // 初回プレイ時に出力デバイスの起動待ちでずれるのを防ぐため、
12285
+ // メイン画面へ移行する前にAudioContextを起動させておく
12286
+ await warmUpAudioContext();
12145
12287
  mainInit(); // 音源・譜面変換双方の完了後にまとめて呼ぶ
12146
12288
  }
12147
12289
  };
@@ -12617,7 +12759,7 @@ const applyShuffle = (_keyNum, _shuffleGroup, _style) => {
12617
12759
  * @param {number[]} _group
12618
12760
  * @param {number} _i
12619
12761
  * @param {number} _divideNum
12620
- * @returns
12762
+ * @returns {number[][]}
12621
12763
  */
12622
12764
  const swapGroupNums = (_style, _group, _i, _divideNum) => {
12623
12765
  if (_group.length % _divideNum === 0) {
@@ -12791,7 +12933,7 @@ const applySRandom = (_keyNum, _shuffleGroup, _arrowHeader, _frzHeader) => {
12791
12933
  * @param {string} [_dummyNo] ダミー用譜面番号添え字
12792
12934
  * @param {string} [_keyCtrlPtn] 選択キー及びパターン
12793
12935
  * @param {boolean} [_scoreAnalyzeFlg=false] 譜面詳細データのために必要分で読込を中断
12794
- * @returns
12936
+ * @returns {object} 矢印・フリーズアロー・速度/色変化データの格納オブジェクト
12795
12937
  */
12796
12938
  const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
12797
12939
  _keyCtrlPtn = `${g_keyObj.currentKey}_${g_keyObj.currentPtn}`, _scoreAnalyzeFlg = false) => {
@@ -12934,7 +13076,7 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
12934
13076
  * - 個別・全体色変化を一体で管理するため通常の配列で返却
12935
13077
  * @param {string} _header
12936
13078
  * @param {number} _scoreNo
12937
- * @returns
13079
+ * @returns {any[][]}
12938
13080
  */
12939
13081
  const setColor2Data = (_header, _scoreNo) => {
12940
13082
  const dosColorData = getRefData(_header, `${_scoreNo}_data`);
@@ -13067,7 +13209,7 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
13067
13209
  * 例) |backA2_data=back_data| -> back_dataで定義された値を使用
13068
13210
  * @param {string} _header
13069
13211
  * @param {string} _dataName
13070
- * @returns
13212
+ * @returns {string|undefined}
13071
13213
  */
13072
13214
  const getRefData = (_header, _dataName) => {
13073
13215
  const data = _dosObj[`${_header}${_dataName}`];
@@ -13124,7 +13266,7 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
13124
13266
  /**
13125
13267
  * 歌詞データの分解
13126
13268
  * @param {string} _scoreNo
13127
- * @returns
13269
+ * @returns {string[][]}
13128
13270
  */
13129
13271
  const makeWordData = _scoreNo => {
13130
13272
  const wordDataList = [];
@@ -13225,7 +13367,7 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
13225
13367
  * @param {string} _header
13226
13368
  * @param {string} _scoreNo 譜面番号
13227
13369
  * @param {string[]} [object.resultTypes] リザルトモーションの種類 (result, failedB, failedS)
13228
- * @returns
13370
+ * @returns {any[][]}
13229
13371
  */
13230
13372
  const makeBackgroundData = (_header, _scoreNo, { resultTypes = [] } = {}) => {
13231
13373
  const dataList = [];
@@ -13365,6 +13507,7 @@ const calcLifeVals = _allArrows => {
13365
13507
  * ライフ回復量・ダメージ量の算出
13366
13508
  * @param {number} _val
13367
13509
  * @param {number} _allArrows
13510
+ * @returns {number}
13368
13511
  */
13369
13512
  const calcLifeVal = (_val, _allArrows) => _val * g_headerObj.maxLifeVal / _allArrows;
13370
13513
 
@@ -13484,6 +13627,7 @@ const setSpeedOnFrame = (_speedData, _lastFrame) => {
13484
13627
  /**
13485
13628
  * Motionオプション適用時の矢印別の速度設定
13486
13629
  * - 矢印が表示される最大フレーム数を 101フレーム と定義。
13630
+ * @returns {number[]}
13487
13631
  */
13488
13632
  const setMotionOnFrame = () => g_motionFunc.get(g_stateObj.motion)(fillArray(101));
13489
13633
 
@@ -13828,7 +13972,7 @@ const pushArrows = (_dataObj, _speedOnFrame, _firstArrivalFrame) => {
13828
13972
  * 歌詞表示、背景・マスク表示のフェードイン時調整処理
13829
13973
  * @param {string} _type
13830
13974
  * @param {object} _data
13831
- * @returns
13975
+ * @returns {object}
13832
13976
  */
13833
13977
  const calcAnimationData = (_type, _data) => {
13834
13978
 
@@ -14761,15 +14905,15 @@ const mainInit = () => {
14761
14905
  }
14762
14906
 
14763
14907
  addTransform(`mainSprite`, `root`, `scale(${g_workObj.scale})`, g_transPriority.scale);
14764
- addTransform(`mainSprite`, `main`, `translateX(${g_workObj.playingX}px) translateY(${g_posObj.stepY - C_STEP_Y + g_headerObj.playingY}px)`, g_transPriority.base);
14908
+ addTransform(`mainSprite`, `main`, `translateX(${wUnit(g_workObj.playingX)}) translateY(${wUnit(g_posObj.stepY - C_STEP_Y + g_headerObj.playingY)})`, g_transPriority.base);
14765
14909
 
14766
14910
  // 曲情報・判定カウント用スプライトを作成(メインスプライトより上位)
14767
14911
  const infoSprite = createEmptySprite(divRoot, `infoSprite`, mainCommonPos);
14768
- addTransform(`infoSprite`, `main`, `translateX(${g_workObj.playingX}px) translateY(${g_headerObj.playingY}px)`, g_transPriority.base);
14912
+ addTransform(`infoSprite`, `main`, `translateX(${wUnit(g_workObj.playingX)}) translateY(${wUnit(g_headerObj.playingY)})`, g_transPriority.base);
14769
14913
 
14770
14914
  // 判定系スプライトを作成(メインスプライトより上位)
14771
14915
  const judgeSprite = createEmptySprite(divRoot, `judgeSprite`, mainCommonPos);
14772
- addTransform(`judgeSprite`, `main`, `translateX(${g_workObj.playingX}px) translateY(${g_headerObj.playingY}px)`, g_transPriority.base);
14916
+ addTransform(`judgeSprite`, `main`, `translateX(${wUnit(g_workObj.playingX)}) translateY(${wUnit(g_headerObj.playingY)})`, g_transPriority.base);
14773
14917
  const tkObj = getKeyInfo();
14774
14918
  const [keyCtrlPtn, keyNum] = [tkObj.keyCtrlPtn, tkObj.keyNum];
14775
14919
 
@@ -14896,6 +15040,7 @@ const mainInit = () => {
14896
15040
  let thisTime;
14897
15041
  let buffTime;
14898
15042
  let musicStartTime;
15043
+ let musicStartCtxTime;
14899
15044
  let musicStartFlg = false;
14900
15045
 
14901
15046
  g_inputKeyBuffer = {};
@@ -15634,7 +15779,7 @@ const mainInit = () => {
15634
15779
  const setArrowYCondition = `${String(g_attrObj[arrowName].movLockFlg)}_${String(initManualFlg)}`;
15635
15780
  setArrowY.get(setArrowYCondition)(arrowName, firstPosY, stepY);
15636
15781
  if (!initManualFlg) {
15637
- addTransform(arrowName, `rootX`, `translateX(${g_workObj.stepX[_j]}px)`);
15782
+ addTransform(arrowName, `rootX`, `translateX(${wUnit(g_workObj.stepX[_j])})`);
15638
15783
  }
15639
15784
 
15640
15785
  /**
@@ -15824,7 +15969,7 @@ const mainInit = () => {
15824
15969
  const setArrowYCondition = `${String(g_attrObj[frzName].movLockFlg)}_${String(initManualFlg)}`;
15825
15970
  setArrowY.get(setArrowYCondition)(frzName, firstPosY, stepY);
15826
15971
  if (!initManualFlg) {
15827
- addTransform(frzName, `rootX`, `translateX(${g_workObj.stepX[_j]}px)`);
15972
+ addTransform(frzName, `rootX`, `translateX(${wUnit(g_workObj.stepX[_j])})`);
15828
15973
  }
15829
15974
 
15830
15975
  safeExecuteCustomHooks(`g_customJsObj.makeFrzArrow`, g_customJsObj.makeFrzArrow, _attrs, frzName, _name, _arrowCnt);
@@ -16129,14 +16274,32 @@ const mainInit = () => {
16129
16274
  }
16130
16275
 
16131
16276
  // 60fpsから遅延するため、その差分を取って次回のタイミングで遅れをリカバリする
16277
+ // - WebAudioAPI使用時は音源クロック(AudioContext.currentTime)を基準とする
16278
+ // performance.now()とAudioContextの時計は独立して進むため、後者を基準にしないと
16279
+ // 再生開始時のオフセットずれやsuspend/resumeによるずれを吸収できない
16132
16280
  thisTime = performance.now();
16133
16281
  buffTime = 0;
16134
- if (g_audio instanceof AudioPlayer || currentFrame >= musicStartFrame) {
16282
+ let holdFrame = false;
16283
+
16284
+ if (g_audio instanceof AudioPlayer && g_audioClockSync && musicStartCtxTime !== undefined) {
16285
+ if (g_audio.contextState === `running`) {
16286
+ buffTime = (g_audio.contextTime - musicStartCtxTime) * 1000
16287
+ - (currentFrame - musicStartFrame) * 1000 / g_fps;
16288
+ } else {
16289
+ // AudioContext停止中は音源も止まっているため、フレーム進行も止めて復帰を待つ
16290
+ getSharedAudioContext(); // suspended時はresumeを試行
16291
+ holdFrame = true;
16292
+ }
16293
+ } else if (g_audio instanceof AudioPlayer || currentFrame >= musicStartFrame) {
16135
16294
  buffTime = (thisTime - musicStartTime - (currentFrame - musicStartFrame) * 1000 / g_fps);
16136
16295
  }
16137
- g_scoreObj.frameNum++;
16138
- g_scoreObj.baseFrame++;
16139
- g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps - buffTime);
16296
+
16297
+ if (!holdFrame) {
16298
+ g_scoreObj.frameNum++;
16299
+ g_scoreObj.baseFrame++;
16300
+ }
16301
+ g_timeoutEvtId = setTimeout(flowTimeline, holdFrame ? g_maxFrameWait :
16302
+ Math.min(Math.max(1000 / g_fps - buffTime, 0), g_maxFrameWait));
16140
16303
  }
16141
16304
  };
16142
16305
  safeExecuteCustomHooks(`g_skinJsObj.main`, g_skinJsObj.main);
@@ -16149,6 +16312,11 @@ const mainInit = () => {
16149
16312
  const musicStartAdjustment = (g_headerObj.blankFrame - g_stateObj.decimalAdjustment + 1) / g_fps;
16150
16313
  musicStartTime = performance.now() + (musicStartAdjustment + g_scheduleLead) * 1000;
16151
16314
  g_audio.play(musicStartAdjustment);
16315
+
16316
+ // 音源クロック基準の開始時刻(フレーム進行の基準)
16317
+ // - g_audioLatencyCompensationが有効な場合は出力遅延分だけ表示を後ろへずらす
16318
+ musicStartCtxTime = g_audio.scheduledTime
16319
+ + (g_audioLatencyCompensation ? g_audio.outputLatency : 0);
16152
16320
  }
16153
16321
 
16154
16322
  g_timeoutEvtId = setTimeout(flowTimeline, 1000 / g_fps);
@@ -16340,20 +16508,20 @@ const changeAppearanceFilter = (_num = 10) => {
16340
16508
  $id(`arrowSprite${topNum + j}`).clipPath = topShape;
16341
16509
  $id(`arrowSprite${bottomNum + j}`).clipPath = bottomShape;
16342
16510
 
16343
- addTransform(`filterBar${topNum + j}`, `appearance`, `translateY(${parseFloat($id(`arrowSprite${j}`).top) + topDist}px)`, g_transPriority.layer);
16344
- addTransform(`filterBar${bottomNum + j}`, `appearance`, `translateY(${parseFloat($id(`arrowSprite${j + 1}`).top) + bottomDist}px)`, g_transPriority.layer);
16511
+ addTransform(`filterBar${topNum + j}`, `appearance`, `translateY(${wUnit(parseFloat($id(`arrowSprite${j}`).top) + topDist)})`, g_transPriority.layer);
16512
+ addTransform(`filterBar${bottomNum + j}`, `appearance`, `translateY(${wUnit(parseFloat($id(`arrowSprite${j + 1}`).top) + bottomDist)})`, g_transPriority.layer);
16345
16513
 
16346
16514
  if (doubleFilterFlg) {
16347
- addTransform(`filterBar${bottomNum + j}_HS`, `appearance`, `translateY(${parseFloat($id(`arrowSprite${j}`).top) + bottomDist}px)`, g_transPriority.layer);
16348
- addTransform(`filterBar${topNum + j}_HS`, `appearance`, `translateY(${parseFloat($id(`arrowSprite${j + 1}`).top) + topDist}px)`, g_transPriority.layer);
16515
+ addTransform(`filterBar${bottomNum + j}_HS`, `appearance`, `translateY(${wUnit(parseFloat($id(`arrowSprite${j}`).top) + bottomDist)})`, g_transPriority.layer);
16516
+ addTransform(`filterBar${topNum + j}_HS`, `appearance`, `translateY(${wUnit(parseFloat($id(`arrowSprite${j + 1}`).top) + topDist)})`, g_transPriority.layer);
16349
16517
  }
16350
16518
  }
16351
16519
 
16352
16520
  // フィルターバーのパーセント表示(フィルターバーが複数表示されるなど複雑なため、最初の階層グループの位置に追従)
16353
16521
  if (g_appearanceRanges.includes(g_stateObj.appearance)) {
16354
16522
  const currentBarNum = g_hidSudObj.std[g_stateObj.appearance][g_stateObj.reverse];
16355
- addTransform(`filterView`, `appearance`, `translateY(${parseFloat($id(`arrowSprite${currentBarNum % 2}`).top) +
16356
- (currentBarNum % 2 === 0 ? bottomDist : topDist)}px)`, g_transPriority.layer);
16523
+ addTransform(`filterView`, `appearance`, `translateY(${wUnit(parseFloat($id(`arrowSprite${currentBarNum % 2}`).top) +
16524
+ (currentBarNum % 2 === 0 ? bottomDist : topDist))})`, g_transPriority.layer);
16357
16525
  filterView.textContent = `${_num}%`;
16358
16526
  g_hidSudObj.filterPos = _num;
16359
16527
  }
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Source by tickle
7
7
  * Created : 2019/11/19
8
- * Revised : 2026/08/06 (v49.4.0)
8
+ * Revised : 2026/08/16 (v49.6.0)
9
9
  *
10
10
  * https://github.com/cwtickle/danoniplus
11
11
  */
@@ -163,8 +163,8 @@ const C_TYP_CALC = `calc`;
163
163
  let [g_sWidth, g_sHeight] = [
164
164
  setVal($id(`canvas-frame`).width, 500, C_TYP_FLOAT), setVal($id(`canvas-frame`).height, 500, C_TYP_FLOAT)
165
165
  ];
166
- $id(`canvas-frame`).width = `${Math.max(g_sWidth, 500)}px`;
167
- $id(`canvas-frame`).height = `${Math.max(g_sHeight, 500)}px`;
166
+ $id(`canvas-frame`).width = wUnit(Math.max(g_sWidth, 500));
167
+ $id(`canvas-frame`).height = wUnit(Math.max(g_sHeight, 500));
168
168
  $id(`canvas-frame`).margin = C_DIS_AUTO;
169
169
 
170
170
  const g_btnWidth = (_multi = 1) => Math.min(g_sWidth, g_limitObj.btnBaseWidth) * _multi;
@@ -1615,7 +1615,7 @@ const addX = (_id, _typeId, _x = 0, { overwrite = false, priority = 1000 } = {})
1615
1615
  }
1616
1616
  if (g_posXs[_id].get(_typeId) !== _x) {
1617
1617
  g_posXs[_id].set(_typeId, _x);
1618
- addTransform(_id, `posX`, `translateX(${sumData(Array.from(g_posXs[_id].values()))}px)`, priority);
1618
+ addTransform(_id, `posX`, `translateX(${wUnit(sumData(Array.from(g_posXs[_id].values())))})`, priority);
1619
1619
  }
1620
1620
  };
1621
1621
 
@@ -1636,7 +1636,7 @@ const addY = (_id, _typeId, _y = 0, { overwrite = false, priority = 1000 } = {})
1636
1636
  }
1637
1637
  if (g_posYs[_id].get(_typeId) !== _y) {
1638
1638
  g_posYs[_id].set(_typeId, _y);
1639
- addTransform(_id, `posY`, `translateY(${sumData(Array.from(g_posYs[_id].values()))}px)`, priority);
1639
+ addTransform(_id, `posY`, `translateY(${wUnit(sumData(Array.from(g_posYs[_id].values())))})`, priority);
1640
1640
  }
1641
1641
  };
1642
1642
 
@@ -1653,7 +1653,7 @@ const delX = (_id, _typeId) => {
1653
1653
  return;
1654
1654
  }
1655
1655
  const priority = g_transforms[_id]?.get(`posX`)?.priority ?? 1000;
1656
- addTransform(_id, `posX`, `translateX(${sumData(Array.from(g_posXs[_id].values()))}px)`, priority);
1656
+ addTransform(_id, `posX`, `translateX(${wUnit(sumData(Array.from(g_posXs[_id].values())))})`, priority);
1657
1657
  };
1658
1658
 
1659
1659
  /**
@@ -1669,7 +1669,7 @@ const delY = (_id, _typeId) => {
1669
1669
  return;
1670
1670
  }
1671
1671
  const priority = g_transforms[_id]?.get(`posY`)?.priority ?? 1000;
1672
- addTransform(_id, `posY`, `translateY(${sumData(Array.from(g_posYs[_id].values()))}px)`, priority);
1672
+ addTransform(_id, `posY`, `translateY(${wUnit(sumData(Array.from(g_posYs[_id].values())))})`, priority);
1673
1673
  };
1674
1674
 
1675
1675
  /**
@@ -1866,7 +1866,7 @@ const g_stepAreaFunc = new Map([
1866
1866
  ['Halfway', () => {
1867
1867
  g_arrowGroupSprite.forEach(sprite => {
1868
1868
  for (let j = 0; j < g_stateObj.layerNum; j++) {
1869
- addTransform(`${sprite}${j}`, `stepArea`, `translateY(${halfwayOffset(j)}px)`, g_transPriority.stepArea);
1869
+ addTransform(`${sprite}${j}`, `stepArea`, `translateY(${wUnit(halfwayOffset(j))})`, g_transPriority.stepArea);
1870
1870
  }
1871
1871
  });
1872
1872
 
@@ -1877,7 +1877,7 @@ const g_stepAreaFunc = new Map([
1877
1877
  for (let j = 0; j < g_stateObj.layerNumDf; j++) {
1878
1878
  addTransform(
1879
1879
  `filterBar${j}`, `stepArea`,
1880
- `translateY(calc(${g_hidSudObj[g_stateObj.appearance] === 0 ? 1 : -1} * ${halfwayOffset(j)}px))`,
1880
+ `translateY(calc(${g_hidSudObj[g_stateObj.appearance] === 0 ? 1 : -1} * ${wUnit(halfwayOffset(j))}))`,
1881
1881
  g_transPriority.stepArea
1882
1882
  );
1883
1883
  }
@@ -1888,12 +1888,12 @@ const g_stepAreaFunc = new Map([
1888
1888
  for (let j = 0; j < g_stateObj.layerNumDf; j++) {
1889
1889
  addTransform(
1890
1890
  `filterBar${j}_HS`, `stepArea`,
1891
- `translateY(calc(${(-1) * (g_hidSudObj[g_stateObj.appearance] === 0 ? 1 : -1)} * ${halfwayOffset(j)}px))`,
1891
+ `translateY(calc(${(-1) * (g_hidSudObj[g_stateObj.appearance] === 0 ? 1 : -1)} * ${wUnit(halfwayOffset(j))}))`,
1892
1892
  g_transPriority.stepArea
1893
1893
  );
1894
1894
  }
1895
1895
  }
1896
- addTransform(`filterView`, `stepArea`, `translateY(${halfwayOffset(Number(g_settings.reverseNum))}px)`, g_transPriority.stepArea);
1896
+ addTransform(`filterView`, `stepArea`, `translateY(${wUnit(halfwayOffset(Number(g_settings.reverseNum)))})`, g_transPriority.stepArea);
1897
1897
  }
1898
1898
  }],
1899
1899
  ['Mismatched', () => {
@@ -1903,7 +1903,7 @@ const g_stepAreaFunc = new Map([
1903
1903
  if (g_workObj.orgFlatFlg) {
1904
1904
  g_arrowGroupSprite.forEach(sprite => {
1905
1905
  for (let j = g_stateObj.layerNumDf; j < g_stateObj.layerNum; j++) {
1906
- addTransform(`${sprite}${j}`, `stepArea`, `translateY(${halfwayOffset(j)}px)`, g_transPriority.stepArea);
1906
+ addTransform(`${sprite}${j}`, `stepArea`, `translateY(${wUnit(halfwayOffset(j))})`, g_transPriority.stepArea);
1907
1907
  }
1908
1908
  });
1909
1909
 
@@ -1914,14 +1914,14 @@ const g_stepAreaFunc = new Map([
1914
1914
  // Hidden+用のフィルターバー補正
1915
1915
  addTransform(
1916
1916
  `filterBar${j + Number(g_settings.reverseNum)}_HS`, `stepArea`,
1917
- `translateY(calc((${getDirFromRev()}) * ${halfwayOffset(j)}px))`,
1917
+ `translateY(calc((${getDirFromRev()}) * ${wUnit(halfwayOffset(j))}))`,
1918
1918
  g_transPriority.stepArea
1919
1919
  );
1920
1920
  } else {
1921
1921
  // Sudden+用のフィルターバー補正
1922
1922
  addTransform(
1923
1923
  `filterBar${j - Number(g_settings.reverseNum)}`, `stepArea`,
1924
- `translateY(calc((${(-1) * getDirFromRev()}) * ${halfwayOffset(j)}px))`,
1924
+ `translateY(calc((${(-1) * getDirFromRev()}) * ${wUnit(halfwayOffset(j))}))`,
1925
1925
  g_transPriority.stepArea
1926
1926
  );
1927
1927
  }
@@ -1936,7 +1936,7 @@ const g_stepAreaFunc = new Map([
1936
1936
  if (g_workObj.orgFlatFlg) {
1937
1937
  g_arrowGroupSprite.forEach(sprite => {
1938
1938
  for (let j = 0; j < g_stateObj.layerNumDf; j++) {
1939
- addTransform(`${sprite}${j}`, `stepArea`, `translateY(${halfwayOffset(j)}px)`, g_transPriority.stepArea);
1939
+ addTransform(`${sprite}${j}`, `stepArea`, `translateY(${wUnit(halfwayOffset(j))})`, g_transPriority.stepArea);
1940
1940
  }
1941
1941
  });
1942
1942
 
@@ -1947,14 +1947,14 @@ const g_stepAreaFunc = new Map([
1947
1947
  // Hidden+用のフィルターバー補正
1948
1948
  addTransform(
1949
1949
  `filterBar${j + Number(g_settings.reverseNum)}`, `stepArea`,
1950
- `translateY(calc((${getDirFromRev()}) * ${halfwayOffset(j)}px))`,
1950
+ `translateY(calc((${getDirFromRev()}) * ${wUnit(halfwayOffset(j))}))`,
1951
1951
  g_transPriority.stepArea
1952
1952
  );
1953
1953
  } else {
1954
1954
  // Sudden+用のフィルターバー補正
1955
1955
  addTransform(
1956
1956
  `filterBar${j - Number(g_settings.reverseNum)}_HS`, `stepArea`,
1957
- `translateY(calc((${(-1) * getDirFromRev()}) * ${halfwayOffset(j)}px))`,
1957
+ `translateY(calc((${(-1) * getDirFromRev()}) * ${wUnit(halfwayOffset(j))}))`,
1958
1958
  g_transPriority.stepArea
1959
1959
  );
1960
1960
  }
@@ -1974,7 +1974,7 @@ const g_stepAreaFunc = new Map([
1974
1974
  for (let j = 0; j < g_stateObj.layerNumDf; j++) {
1975
1975
  addTransform(
1976
1976
  `filterBar${j}_HS`, `stepArea`,
1977
- `translateY(calc((${g_hidSudObj[g_stateObj.appearance] === 0 ? 1 : -1}) * ${halfwayOffset(j)}px))`,
1977
+ `translateY(calc((${g_hidSudObj[g_stateObj.appearance] === 0 ? 1 : -1}) * ${wUnit(halfwayOffset(j))}))`,
1978
1978
  g_transPriority.stepArea
1979
1979
  );
1980
1980
  }
@@ -1983,7 +1983,7 @@ const g_stepAreaFunc = new Map([
1983
1983
  for (let j = 0; j < g_stateObj.layerNumDf; j += 2) {
1984
1984
  addTransform(
1985
1985
  `filterBar${j + Number(g_settings.reverseNum)}_HS`, `stepArea`,
1986
- `translateY(calc((${getDirFromRev()}) * ${halfwayOffset(j)}px))`,
1986
+ `translateY(calc((${getDirFromRev()}) * ${wUnit(halfwayOffset(j))}))`,
1987
1987
  g_transPriority.stepArea
1988
1988
  );
1989
1989
  }
@@ -1998,7 +1998,7 @@ const g_stepAreaFunc = new Map([
1998
1998
  ['Alt-Crossing', () => {
1999
1999
  for (let j = 0; j < g_stateObj.layerNum; j++) {
2000
2000
  addTransform(`mainSprite${j}`, `stepArea`, `rotate(${getDirFromLayer(j) * -10}deg) ` +
2001
- `translateX(${getDirFromLayer(j) * 20}px)`, g_transPriority.stepArea);
2001
+ `translateX(${wUnit(getDirFromLayer(j) * 20)})`, g_transPriority.stepArea);
2002
2002
  }
2003
2003
  }],
2004
2004
  ]);
@@ -2012,34 +2012,34 @@ const getShakingDist = () => (Math.abs((g_scoreObj.baseFrame / 2) % 100 - 50) -
2012
2012
  const g_shakingFunc = new Map([
2013
2013
  ['OFF', () => true],
2014
2014
  ['Horizontal', (_multi = 1) => {
2015
- addTransform(`mainSprite`, `shakingX_base`, `translateX(${getShakingDist() * _multi}px)`, g_transPriority.shakingX)
2015
+ addTransform(`mainSprite`, `shakingX_base`, `translateX(${wUnit(getShakingDist() * _multi)})`, g_transPriority.shakingX)
2016
2016
  }],
2017
2017
  ['Vertical', (_multi = 1) => {
2018
- addTransform(`mainSprite`, `shakingY_base`, `translateY(${getShakingDist() / 2 * _multi}px)`, g_transPriority.shakingY)
2018
+ addTransform(`mainSprite`, `shakingY_base`, `translateY(${wUnit(getShakingDist() / 2 * _multi)})`, g_transPriority.shakingY)
2019
2019
  }],
2020
2020
  ['X-Horizontal', (_multi = 1) => {
2021
2021
  for (let j = 0; j < g_stateObj.layerNum; j++) {
2022
- addTransform(`mainSprite${j}`, `shakingX_layer`, `translateX(${getDirFromLayer(j) * (4 / 3) * getShakingDist() * _multi}px)`, g_transPriority.shakingX);
2022
+ addTransform(`mainSprite${j}`, `shakingX_layer`, `translateX(${wUnit(getDirFromLayer(j) * (4 / 3) * getShakingDist() * _multi)})`, g_transPriority.shakingX);
2023
2023
  }
2024
2024
  }],
2025
2025
  ['X-Vertical', (_multi = 1) => {
2026
2026
  for (let j = 0; j < g_stateObj.layerNum; j++) {
2027
- addTransform(`mainSprite${j}`, `shakingY_layer`, `translateY(${getDirFromLayer(j) * getShakingDist() * _multi}px)`, g_transPriority.shakingY);
2027
+ addTransform(`mainSprite${j}`, `shakingY_layer`, `translateY(${wUnit(getDirFromLayer(j) * getShakingDist() * _multi)})`, g_transPriority.shakingY);
2028
2028
  }
2029
2029
  }],
2030
2030
  ['Drunk', (_multi = 1) => {
2031
2031
  const dist = getShakingDist();
2032
2032
  if (g_workObj.drunkXFlg) {
2033
2033
  const deltaX = dist * _multi;
2034
- addTransform(`mainSprite`, `shakingX_drunk`, `translateX(${deltaX}px)`, g_transPriority.shakingX);
2035
- addTransform(`infoSprite`, `shakingX_drunk`, `translateX(${deltaX}px)`, g_transPriority.shakingX);
2036
- addTransform(`judgeSprite`, `shakingX_drunk`, `translateX(${deltaX}px)`, g_transPriority.shakingX);
2034
+ addTransform(`mainSprite`, `shakingX_drunk`, `translateX(${wUnit(deltaX)})`, g_transPriority.shakingX);
2035
+ addTransform(`infoSprite`, `shakingX_drunk`, `translateX(${wUnit(deltaX)})`, g_transPriority.shakingX);
2036
+ addTransform(`judgeSprite`, `shakingX_drunk`, `translateX(${wUnit(deltaX)})`, g_transPriority.shakingX);
2037
2037
  }
2038
2038
  if (g_workObj.drunkYFlg) {
2039
2039
  const deltaY = dist / 2 * _multi;
2040
- addTransform(`mainSprite`, `shakingY_drunk`, `translateY(${deltaY}px)`, g_transPriority.shakingY);
2041
- addTransform(`infoSprite`, `shakingY_drunk`, `translateY(${deltaY}px)`, g_transPriority.shakingY);
2042
- addTransform(`judgeSprite`, `shakingY_drunk`, `translateY(${deltaY}px)`, g_transPriority.shakingY);
2040
+ addTransform(`mainSprite`, `shakingY_drunk`, `translateY(${wUnit(deltaY)})`, g_transPriority.shakingY);
2041
+ addTransform(`infoSprite`, `shakingY_drunk`, `translateY(${wUnit(deltaY)})`, g_transPriority.shakingY);
2042
+ addTransform(`judgeSprite`, `shakingY_drunk`, `translateY(${wUnit(deltaY)})`, g_transPriority.shakingY);
2043
2043
  }
2044
2044
  // 補正がゼロになったときに軸の移動方法をランダムで決定
2045
2045
  if (dist === 0) {
@@ -2298,7 +2298,7 @@ let g_canDisabledSettings = [`speed`, `motion`, `scroll`, `reverse`, `shuffle`,
2298
2298
 
2299
2299
  const g_hidSudFunc = new Map([
2300
2300
  ['filterPos', _filterPos => `${_filterPos}${g_lblNameObj.percent}`],
2301
- ['range', () => `${Math.round(g_posObj.arrowHeight - g_posObj.stepY)}px`],
2301
+ ['range', () => wUnit(Math.round(g_posObj.arrowHeight - g_posObj.stepY))],
2302
2302
  ['hidden', _filterPos => `${Math.min(Math.round(g_posObj.arrowHeight * (100 - _filterPos) / 100), g_posObj.arrowHeight - g_posObj.stepY)}`],
2303
2303
  ['sudden', _filterPos => `${Math.max(Math.round(g_posObj.arrowHeight * (100 - _filterPos) / 100) - g_posObj.stepY, 0)}`],
2304
2304
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "danoniplus",
3
- "version": "49.5.0",
3
+ "version": "49.6.1",
4
4
  "description": "Dancing☆Onigiri (CW Edition) - Web-based Rhythm Game",
5
5
  "main": "./js/danoni_main.js",
6
6
  "jsdelivr": "./js/danoni_main.js",