danoniplus 32.7.0 → 33.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/js/danoni_main.js CHANGED
@@ -4,12 +4,12 @@
4
4
  *
5
5
  * Source by tickle
6
6
  * Created : 2018/10/08
7
- * Revised : 2023/07/17
7
+ * Revised : 2023/08/03
8
8
  *
9
9
  * https://github.com/cwtickle/danoniplus
10
10
  */
11
- const g_version = `Ver 32.7.0`;
12
- const g_revisedDate = `2023/07/17`;
11
+ const g_version = `Ver 33.1.0`;
12
+ const g_revisedDate = `2023/08/03`;
13
13
 
14
14
  // カスタム用バージョン (danoni_custom.js 等で指定可)
15
15
  let g_localVersion = ``;
@@ -47,8 +47,11 @@ const current = _ => {
47
47
  return targetScript.src;
48
48
  };
49
49
  const g_rootPath = current().match(/(^.*\/)/)[0];
50
+ const g_workPath = new URL(location.href).href.match(/(^.*\/)/)[0];
50
51
  const g_remoteFlg = g_rootPath.match(`^https://cwtickle.github.io/danoniplus/`) !== null;
51
52
  const g_randTime = Date.now();
53
+ const g_isFile = location.href.match(/^file/);
54
+ const g_isLocal = location.href.match(/^file/) || location.href.indexOf(`localhost`) !== -1;
52
55
 
53
56
  window.onload = async () => {
54
57
  g_loadObj.main = true;
@@ -725,7 +728,7 @@ const loadScript2 = (_url, _requiredFlg = true, _charset = `UTF-8`) => {
725
728
  * デフォルトは danoni_skin_default.css を読み込む
726
729
  * @param {url} _href
727
730
  */
728
- const importCssFile2 = _href => {
731
+ const importCssFile2 = (_href, { crossOrigin = `anonymous` } = {}) => {
729
732
  const baseUrl = _href.split(`?`)[0];
730
733
  g_loadObj[baseUrl] = false;
731
734
 
@@ -733,6 +736,9 @@ const importCssFile2 = _href => {
733
736
  const link = document.createElement(`link`);
734
737
  link.rel = `stylesheet`;
735
738
  link.href = _href;
739
+ if (!g_isFile) {
740
+ link.crossOrigin = crossOrigin;
741
+ }
736
742
  link.onload = _ => {
737
743
  g_loadObj[baseUrl] = true;
738
744
  resolve(link);
@@ -767,32 +773,66 @@ const loadMultipleFiles2 = async (_fileData, _loadType) => {
767
773
  }));
768
774
  };
769
775
 
770
- /**
771
- * 入力されたパスを、ディレクトリとそれ以外に分割
772
- * 返却値:[ファイルキーワード, ルートディレクトリ]
773
- * @param {string} _path
774
- * @param {number} _pos
775
- * @param {string} _directory
776
- */
777
- const getFolderAndType = (_path, _pos, _directory = ``) => {
778
- const rootPath = (_directory === `` ? `` : g_rootPath);
779
- return (_pos > 0 ? [_path.substring(_pos + 1), `${rootPath}${_path.substring(0, _pos)}/`] : [_path, `${rootPath}${_directory}`]);
780
- };
781
-
782
776
  /**
783
777
  * 与えられたパスより、キーワードとディレクトリに分割
784
- * カレントディレクトリ指定がある場合を考慮して、処理を分けている
785
778
  * 返却値:[ファイルキーワード, ルートディレクトリ]
786
779
  * @param {string} _fileName
787
- * @param {string} _directory
780
+ * @param {string} _directory
788
781
  */
789
782
  const getFilePath = (_fileName, _directory = ``) => {
790
- if (_fileName.indexOf(C_MRK_CURRENT_DIRECTORY) !== -1) {
791
- const tmpType = _fileName.split(C_MRK_CURRENT_DIRECTORY)[1];
792
- return getFolderAndType(tmpType, tmpType.indexOf(`/`));
783
+ let fullPath;
784
+ if (_fileName.startsWith(C_MRK_CURRENT_DIRECTORY)) {
785
+ fullPath = `${g_workPath}${_fileName.slice(C_MRK_CURRENT_DIRECTORY.length)}`;
793
786
  } else {
794
- return getFolderAndType(_fileName, _fileName.lastIndexOf(`/`), _directory);
787
+ fullPath = `${g_rootPath}${_directory}${_fileName}`;
788
+ }
789
+ const dirPos = fullPath.lastIndexOf(`/`);
790
+ return [fullPath.slice(dirPos + 1), fullPath.slice(0, dirPos + 1)];
791
+ }
792
+
793
+ /**
794
+ * 画像ファイルの存在チェック後、プリロードする処理
795
+ * @param {string} _imgPath
796
+ * @param {string} directory
797
+ * @param {boolean} syncBackPath
798
+ * @returns
799
+ */
800
+ const preloadImgFile = (_imgPath, { directory = ``, syncBackPath = true } = {}) => {
801
+
802
+ let imgPath = _imgPath;
803
+ if (g_headerObj.autoPreload) {
804
+ if (checkImage(_imgPath)) {
805
+ if (syncBackPath) {
806
+ const [file, dir] = getFilePath(_imgPath, directory);
807
+ imgPath = `${dir}${file}`;
808
+ }
809
+ preloadFile(`image`, imgPath);
810
+ }
811
+ }
812
+ return imgPath;
813
+ };
814
+
815
+ /**
816
+ * 画像パス部分の取得
817
+ * @param {string} _str
818
+ * @returns
819
+ */
820
+ const getImageUrlPath = _str => {
821
+ const matches = _str?.match(/url\("([^"]*)"\)/);
822
+ return matches && matches.length >= 2 ? matches[1] : ``;
823
+ };
824
+
825
+ /**
826
+ * カレントディレクトリを含む文字列を置換し、変更後の文字列を作成
827
+ * @param {string} _str
828
+ */
829
+ const reviseCssText = _str => {
830
+ if (getImageUrlPath(_str) !== ``) {
831
+ const imgOriginal = getImageUrlPath(_str);
832
+ const imgPath = preloadImgFile(imgOriginal);
833
+ return replaceStr(_str, [[imgOriginal, imgPath]]);
795
834
  }
835
+ return _str;
796
836
  };
797
837
 
798
838
  /*-----------------------------------------------------------*/
@@ -890,7 +930,17 @@ const makeColorGradation = (_colorStr, { _defaultColorgrd = g_headerObj.defaultC
890
930
  const alphaVal = (_shadowFlg && _objType !== `frz`) ? `80` : (_objType === `titleArrow` ? `40` : ``);
891
931
 
892
932
  let convertColorStr = ``;
893
- const tmpColorStr = _colorStr.split(`@`);
933
+ const tmpBackgroundStr = _colorStr.split(`;`);
934
+
935
+ // 色情報以外の部分を退避
936
+ const addData = tmpBackgroundStr[1] !== undefined ? tmpBackgroundStr.slice(1).join(` `) : ``;
937
+ if ([``, `-`, `none`].includes(tmpBackgroundStr[0]) ||
938
+ tmpBackgroundStr[0].startsWith(`url(`) || tmpBackgroundStr[0].startsWith(`var(`)) {
939
+ return addData;
940
+ }
941
+
942
+ // 色情報からグラデーションを作成
943
+ const tmpColorStr = tmpBackgroundStr[0].split(`@`);
894
944
  const colorArray = tmpColorStr[0].split(`:`);
895
945
  for (let j = 0; j < colorArray.length; j++) {
896
946
  colorArray[j] = colorCdPadding(_colorCdPaddingUse, colorToHex(colorArray[j].replaceAll(`0x`, `#`)));
@@ -917,7 +967,7 @@ const makeColorGradation = (_colorStr, { _defaultColorgrd = g_headerObj.defaultC
917
967
  convertColorStr += `${colorArray.join(', ')}`;
918
968
  }
919
969
 
920
- return `${gradationType}(${convertColorStr})`;
970
+ return `${hasVal(addData) ? `${addData} ` : ''}${gradationType}(${convertColorStr})`;
921
971
  };
922
972
 
923
973
  /*-----------------------------------------------------------*/
@@ -1406,8 +1456,64 @@ const drawDefaultBackImage = _key => {
1406
1456
  } else {
1407
1457
  createEmptySprite(divRoot, `divBack`);
1408
1458
  }
1459
+
1460
+ // CSSスタイルの初期化
1461
+ Object.keys(g_cssBkProperties).forEach(prop =>
1462
+ document.documentElement.style.setProperty(prop, g_cssBkProperties[prop]));
1463
+
1464
+ Object.keys(g_headerObj).filter(val => val.startsWith(`--`) && hasVal(g_headerObj[val])).forEach(prop =>
1465
+ document.documentElement.style.setProperty(prop, getCssCustomProperty(prop, g_headerObj[prop])));
1466
+
1409
1467
  };
1410
1468
 
1469
+ /**
1470
+ * CSSカスタムプロパティの値を作成
1471
+ * @param {string} _prop
1472
+ * @param {string} _propData
1473
+ */
1474
+ const getCssCustomProperty = (_prop, _propData) =>
1475
+ document.documentElement.style.getPropertyValue(_propData) !== `` ?
1476
+ document.documentElement.style.getPropertyValue(_propData) :
1477
+ g_cssBkProperties[_propData] !== undefined ?
1478
+ g_cssBkProperties[_propData] :
1479
+ _prop.endsWith(`-x`) ? _propData : reviseCssText(makeColorGradation(_propData, { _defaultColorgrd: false }));
1480
+
1481
+ /**
1482
+ * CSSカスタムプロパティの値をオブジェクトへ退避
1483
+ */
1484
+ const getCssCustomProperties = _ => {
1485
+ try {
1486
+ const htmlStyle = document.documentElement.computedStyleMap();
1487
+ for (const [propertyName, value] of htmlStyle.entries()) {
1488
+ if (/^--/.test(propertyName)) {
1489
+ g_cssBkProperties[propertyName] = value.toString();
1490
+ }
1491
+ }
1492
+ } catch (error) {
1493
+
1494
+ try {
1495
+ // FirefoxではcomputedStyleMapが使えないため、
1496
+ // CSSの全スタイルシート定義から :root がセレクタのルールを抽出し、カスタムプロパティを抽出
1497
+ const sheets = document.styleSheets;
1498
+ Array.from(sheets).filter(sheet => !g_isFile && sheet.href !== null &&
1499
+ sheet.href.includes(`danoni_skin_`) && sheet.cssRules).forEach(sheet => {
1500
+ for (const rule of sheet.cssRules) {
1501
+ if (rule.selectorText === ':root') {
1502
+ for (let i = 0; i < rule.style.length; i++) {
1503
+ const propertyName = rule.style.item(i);
1504
+ if (/^--/.test(propertyName)) {
1505
+ g_cssBkProperties[propertyName] = rule.style.getPropertyValue(propertyName);
1506
+ }
1507
+ }
1508
+ }
1509
+ }
1510
+ });
1511
+ } catch (error) {
1512
+ // 上記でもNGの場合は何もしない
1513
+ }
1514
+ }
1515
+ }
1516
+
1411
1517
  /**
1412
1518
  * 背景・マスク用画像の描画
1413
1519
  * @param {object} _obj
@@ -1450,7 +1556,7 @@ const makeSpriteText = _obj => {
1450
1556
  * @param {object, array} _obj
1451
1557
  */
1452
1558
  const checkDuplicatedObjects = _obj => {
1453
- let addFrame = 0;
1559
+ let dataCnts = 0;
1454
1560
  if (_obj === undefined) {
1455
1561
  _obj = [];
1456
1562
  _obj[0] = [];
@@ -1458,17 +1564,17 @@ const checkDuplicatedObjects = _obj => {
1458
1564
  for (let m = 1; ; m++) {
1459
1565
  if (_obj[m] === undefined) {
1460
1566
  _obj[m] = [];
1461
- addFrame = m;
1567
+ dataCnts = m;
1462
1568
  break;
1463
1569
  }
1464
1570
  }
1465
1571
  }
1466
- return [_obj, addFrame];
1572
+ return [_obj, dataCnts];
1467
1573
  };
1468
1574
 
1469
1575
  /**
1470
1576
  * 多層スプライトデータの作成処理
1471
- * @param {array} _data
1577
+ * @param {string} _data
1472
1578
  * @param {function} _calcFrame
1473
1579
  */
1474
1580
  const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
@@ -1485,12 +1591,8 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
1485
1591
  }
1486
1592
 
1487
1593
  // 値チェックとエスケープ処理
1488
- let tmpFrame;
1489
- if (setIntVal(tmpSpriteData[0], -1) === 0) {
1490
- tmpFrame = 0;
1491
- } else {
1492
- tmpFrame = roundZero(_calcFrame(setVal(tmpSpriteData[0], 200, C_TYP_CALC)));
1493
- }
1594
+ const tmpFrame = setIntVal(tmpSpriteData[0], -1) === 0 ? 0 :
1595
+ roundZero(_calcFrame(setVal(tmpSpriteData[0], 200, C_TYP_CALC)));
1494
1596
  const tmpDepth = (tmpSpriteData[1] === C_FLG_ALL ? C_FLG_ALL : setVal(tmpSpriteData[1], 0, C_TYP_CALC));
1495
1597
  if (tmpDepth !== C_FLG_ALL && tmpDepth > maxDepth) {
1496
1598
  maxDepth = tmpDepth;
@@ -1512,23 +1614,15 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
1512
1614
  if (setVal(tmpSpriteData[11], g_presetObj.animationFillMode) !== undefined) {
1513
1615
  tmpObj.animationFillMode = setVal(tmpSpriteData[11], g_presetObj.animationFillMode);
1514
1616
  }
1515
- if (g_headerObj.autoPreload) {
1516
- if (checkImage(tmpObj.path)) {
1517
- if (g_headerObj.syncBackPath) {
1518
- const [file, dir] = getFilePath(tmpObj.path, `./`);
1519
- tmpObj.path = `${dir}${file}`;
1520
- }
1521
- preloadFile(`image`, tmpObj.path);
1522
- }
1523
- }
1617
+ tmpObj.path = preloadImgFile(tmpObj.path, { syncBackPath: g_headerObj.syncBackPath });
1524
1618
 
1525
- let addFrame = 0;
1526
- [spriteData[tmpFrame], addFrame] =
1619
+ let dataCnts = 0;
1620
+ [spriteData[tmpFrame], dataCnts] =
1527
1621
  checkDuplicatedObjects(spriteData[tmpFrame]);
1528
1622
 
1529
1623
  const emptyPatterns = [``, `[loop]`, `[jump]`];
1530
1624
  const colorObjFlg = tmpSpriteData[2]?.startsWith(`[c]`) || false;
1531
- const spriteFrameData = spriteData[tmpFrame][addFrame] = {
1625
+ const spriteFrameData = spriteData[tmpFrame][dataCnts] = {
1532
1626
  depth: tmpDepth,
1533
1627
  };
1534
1628
 
@@ -1542,7 +1636,7 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
1542
1636
  animationName: tmpObj.animationName,
1543
1637
  animationDuration: `${tmpObj.animationDuration}s`,
1544
1638
  };
1545
- spriteFrameData.colorObjId = `${tmpFrame}_${addFrame}`;
1639
+ spriteFrameData.colorObjId = `${tmpFrame}_${dataCnts}`;
1546
1640
  spriteFrameData.colorObjClass = setVal(tmpObj.class, undefined);
1547
1641
  if (tmpObj.animationFillMode !== undefined) {
1548
1642
  spriteFrameData.colorObjInfo.animationFillMode = tmpObj.animationFillMode;
@@ -1564,6 +1658,34 @@ const makeSpriteData = (_data, _calcFrame = _frame => _frame) => {
1564
1658
  return [spriteData, maxDepth];
1565
1659
  };
1566
1660
 
1661
+ /**
1662
+ * スタイル変更データの作成処理
1663
+ * @param {string} _data
1664
+ * @param {function} _calcFrame
1665
+ * @returns
1666
+ */
1667
+ const makeStyleData = (_data, _calcFrame = _frame => _frame) => {
1668
+ const spriteData = [];
1669
+ splitLF(_data).filter(data => hasVal(data)).forEach(tmpData => {
1670
+ const tmpSpriteData = tmpData.split(`,`);
1671
+
1672
+ // カスタムプロパティの名称(--始まり)で無い場合はコメントと見做してスキップ
1673
+ if (tmpSpriteData.length <= 1 || !tmpSpriteData[1].startsWith(`--`)) {
1674
+ return;
1675
+ }
1676
+ const tmpFrame = setIntVal(tmpSpriteData[0], -1) === 0 ? 0 :
1677
+ roundZero(_calcFrame(setVal(tmpSpriteData[0], 200, C_TYP_CALC)));
1678
+
1679
+ let dataCnts = 0;
1680
+ [spriteData[tmpFrame], dataCnts] = checkDuplicatedObjects(spriteData[tmpFrame]);
1681
+ spriteData[tmpFrame][dataCnts] = {
1682
+ depth: tmpSpriteData[1],
1683
+ styleData: getCssCustomProperty(tmpSpriteData[1], tmpSpriteData[2]),
1684
+ };
1685
+ });
1686
+ return [spriteData, 1];
1687
+ };
1688
+
1567
1689
  /**
1568
1690
  * 画像ファイルかどうかをチェック
1569
1691
  * @param {string} _str
@@ -1652,6 +1774,22 @@ const drawSpriteData = (_frame, _displayName, _depthName) => {
1652
1774
  const drawMainSpriteData = (_frame, _depthName) =>
1653
1775
  g_scoreObj[`${_depthName}Data`][_frame].forEach(tmpObj => drawBaseSpriteData(tmpObj, _depthName));
1654
1776
 
1777
+ /**
1778
+ * スタイル切替
1779
+ * @param {number} _frame
1780
+ * @param {string} _displayName
1781
+ */
1782
+ const drawStyleData = (_frame, _displayName) => {
1783
+ g_headerObj[`style${toCapitalize(_displayName)}Data`][_frame].forEach(tmpObj =>
1784
+ document.documentElement.style.setProperty(tmpObj.depth, tmpObj.styleData));
1785
+
1786
+ return _frame;
1787
+ };
1788
+
1789
+ const drawMainStyleData = (_frame) =>
1790
+ g_scoreObj.styleData[_frame].forEach(tmpObj =>
1791
+ document.documentElement.style.setProperty(tmpObj.depth, tmpObj.styleData));
1792
+
1655
1793
  /**
1656
1794
  * タイトル・リザルトモーションの描画
1657
1795
  * @param {string} _displayName
@@ -1660,7 +1798,7 @@ const drawTitleResultMotion = _displayName => {
1660
1798
  g_animationData.forEach(sprite => {
1661
1799
  const spriteName = `${sprite}${toCapitalize(_displayName)}`;
1662
1800
  if (g_headerObj[`${spriteName}Data`][g_scoreObj[`${spriteName}FrameNum`]] !== undefined) {
1663
- g_scoreObj[`${spriteName}FrameNum`] = drawSpriteData(g_scoreObj[`${spriteName}FrameNum`], _displayName, sprite);
1801
+ g_scoreObj[`${spriteName}FrameNum`] = g_animationFunc.draw[sprite](g_scoreObj[`${spriteName}FrameNum`], _displayName, sprite);
1664
1802
  }
1665
1803
  });
1666
1804
  };
@@ -1863,7 +2001,7 @@ const initialControl = async () => {
1863
2001
  await loadChartFile(0);
1864
2002
 
1865
2003
  // 共通設定ファイルの指定
1866
- let [settingType, settingRoot] = getFilePath(g_rootObj.settingType ?? ``, C_DIR_JS);
2004
+ let [settingType, settingRoot] = getFilePath(g_rootObj.settingType ?? ``);
1867
2005
  if (settingType !== ``) {
1868
2006
  settingType = `_${settingType}`;
1869
2007
  }
@@ -2544,6 +2682,19 @@ const headerConvert = _dosObj => {
2544
2682
  // ヘッダー群の格納先
2545
2683
  const obj = {};
2546
2684
 
2685
+ // 自動プリロードの設定
2686
+ obj.autoPreload = setBoolVal(_dosObj.autoPreload, true);
2687
+ g_headerObj.autoPreload = obj.autoPreload;
2688
+
2689
+ // デフォルトスタイルのバックアップ
2690
+ getCssCustomProperties();
2691
+
2692
+ // 初期で変更するカスタムプロパティを設定
2693
+ Object.keys(_dosObj).filter(val => val.startsWith(`--`) && hasVal(_dosObj[val])).forEach(prop => {
2694
+ g_cssBkProperties[prop] = getCssCustomProperty(prop, _dosObj[prop]);
2695
+ document.documentElement.style.setProperty(prop, g_cssBkProperties[prop]);
2696
+ });
2697
+
2547
2698
  // フォントの設定
2548
2699
  obj.customFont = _dosObj.customFont ?? ``;
2549
2700
  g_headerObj.customFont = obj.customFont;
@@ -2890,10 +3041,6 @@ const headerConvert = _dosObj => {
2890
3041
  // ハッシュタグ
2891
3042
  obj.hashTag = _dosObj.hashTag ?? ``;
2892
3043
 
2893
- // 自動プリロードの設定
2894
- obj.autoPreload = setBoolVal(_dosObj.autoPreload, true);
2895
- g_headerObj.autoPreload = obj.autoPreload;
2896
-
2897
3044
  // 読込対象の画像を指定(rel:preload)と同じ
2898
3045
  obj.preloadImages = [];
2899
3046
  if (hasVal(_dosObj.preloadImages)) {
@@ -3064,7 +3211,7 @@ const headerConvert = _dosObj => {
3064
3211
  const dataList = [_dosObj[`${sprite}title${g_localeObj.val}_data`], _dosObj[`${sprite}title_data`]];
3065
3212
  const data = dataList.find((v) => v !== undefined);
3066
3213
  if (hasVal(data)) {
3067
- [obj[`${sprite}TitleData`], obj[`${sprite}TitleMaxDepth`]] = makeSpriteData(data);
3214
+ [obj[`${sprite}TitleData`], obj[`${sprite}TitleMaxDepth`]] = g_animationFunc.make[sprite](data);
3068
3215
  }
3069
3216
  });
3070
3217
 
@@ -3837,12 +3984,12 @@ const titleInit = _ => {
3837
3984
 
3838
3985
  // タイトル用フレーム初期化
3839
3986
  g_scoreObj.titleFrameNum = 0;
3840
- g_scoreObj.backTitleFrameNum = 0;
3841
- g_scoreObj.maskTitleFrameNum = 0;
3842
3987
 
3843
- // タイトル用ループカウンター
3844
- g_scoreObj.backTitleLoopCount = 0;
3845
- g_scoreObj.maskTitleLoopCount = 0;
3988
+ // タイトルアニメーション用フレーム初期化、ループカウンター設定
3989
+ g_animationData.forEach(sprite => {
3990
+ g_scoreObj[`${sprite}TitleFrameNum`] = 0;
3991
+ g_scoreObj[`${sprite}TitleLoopCount`] = 0;
3992
+ });
3846
3993
 
3847
3994
  const keyCtrlPtn = `${g_keyObj.currentKey}_${g_keyObj.currentPtn}`;
3848
3995
 
@@ -4086,15 +4233,14 @@ const titleInit = _ => {
4086
4233
  // ユーザカスタムイベント(フレーム毎)
4087
4234
  g_customJsObj.titleEnterFrame.forEach(func => func());
4088
4235
 
4089
- // 背景・マスクモーション
4236
+ // 背景・マスクモーション、スキン変更
4090
4237
  drawTitleResultMotion(g_currentPage);
4091
4238
 
4092
4239
  thisTime = performance.now();
4093
4240
  buffTime = thisTime - titleStartTime - g_scoreObj.titleFrameNum * 1000 / g_fps;
4094
4241
 
4095
4242
  g_scoreObj.titleFrameNum++;
4096
- g_scoreObj.backTitleFrameNum++;
4097
- g_scoreObj.maskTitleFrameNum++;
4243
+ g_animationData.forEach(sprite => g_scoreObj[`${sprite}TitleFrameNum`]++);
4098
4244
  g_timeoutEvtTitleId = setTimeout(flowTitleTimeline, 1000 / g_fps - buffTime);
4099
4245
  };
4100
4246
 
@@ -7421,7 +7567,11 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7421
7567
  /**
7422
7568
  * 歌詞表示、背景・マスクデータの優先順取得
7423
7569
  */
7424
- const getPriorityHeader = _ => {
7570
+ const getPriorityHeader = (_defaultHeaders = []) => {
7571
+ if (_defaultHeaders.length > 0) {
7572
+ return makeDedupliArray(_defaultHeaders);
7573
+ }
7574
+
7425
7575
  const list = [];
7426
7576
  const anotherKeyFlg = hasVal(g_keyObj[`transKey${_keyCtrlPtn}`]);
7427
7577
  let type = ``;
@@ -7508,17 +7658,17 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7508
7658
  wordMaxDepth = tmpWordData[k + 1];
7509
7659
  }
7510
7660
 
7511
- let addFrame = 0;
7512
- [wordData[tmpWordData[k]], addFrame] =
7661
+ let dataCnts = 0;
7662
+ [wordData[tmpWordData[k]], dataCnts] =
7513
7663
  checkDuplicatedObjects(wordData[tmpWordData[k]]);
7514
7664
 
7515
7665
  if (tmpWordData.length > 3 && tmpWordData.length < 6) {
7516
7666
  tmpWordData[3] = setIntVal(tmpWordData[3], C_WOD_FRAME);
7517
- wordData[tmpWordData[0]][addFrame].push(tmpWordData[1],
7667
+ wordData[tmpWordData[0]][dataCnts].push(tmpWordData[1],
7518
7668
  escapeHtmlForEnabledTag(tmpWordData[2]), tmpWordData[3]);
7519
7669
  break;
7520
7670
  } else {
7521
- wordData[tmpWordData[k]][addFrame].push(tmpWordData[k + 1],
7671
+ wordData[tmpWordData[k]][dataCnts].push(tmpWordData[k + 1],
7522
7672
  escapeHtmlForEnabledTag(tmpWordData[k + 2] ?? ``));
7523
7673
  }
7524
7674
  }
@@ -7528,36 +7678,19 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7528
7678
  };
7529
7679
 
7530
7680
  /**
7531
- * 背景・マスクデータの分解
7681
+ * 背景・マスク、スキン変更データの分解
7532
7682
  * @param {string} _header
7533
- * @param {string} _scoreNo
7534
- */
7535
- const makeBackgroundData = (_header, _scoreNo) => {
7536
- const dataList = [];
7537
- const addDataList = (_type = ``) => dataList.push(...getPriorityList(_header, _type, _scoreNo));
7538
- getPriorityHeader().forEach(val => addDataList(val));
7539
-
7540
- const data = dataList.find((v) => v !== undefined);
7541
- return (data !== undefined ? makeSpriteData(data, calcFrame) : [[], -1]);
7542
- };
7543
-
7544
- /**
7545
- * リザルトモーションデータ(結果画面用背景・マスクデータ)の分解
7546
- * @param {string} _header 背景、マスク (back, mask)
7547
- * @param {string} _resultType リザルトモーションの種類 (result, failedB, failedS)
7548
7683
  * @param {string} _scoreNo 譜面番号
7549
- * @param {string} _defaultType _resultTypeが無いときの代替名
7684
+ * @param {array} resultTypes リザルトモーションの種類 (result, failedB, failedS)
7550
7685
  */
7551
- const makeBackgroundResultData = (_header, _resultType, _scoreNo, _defaultType = ``) => {
7686
+ const makeBackgroundData = (_header, _scoreNo, { resultTypes = [] } = {}) => {
7552
7687
  const dataList = [];
7688
+ const calcFrameFunc = resultTypes.length > 0 ? calcFrame : undefined;
7553
7689
  const addDataList = (_type = ``) => dataList.push(...getPriorityList(_header, _type, _scoreNo));
7554
- addDataList(_resultType);
7555
- if (_defaultType !== ``) {
7556
- addDataList(_defaultType);
7557
- }
7690
+ getPriorityHeader(resultTypes).forEach(val => addDataList(val));
7558
7691
 
7559
7692
  const data = dataList.find((v) => v !== undefined);
7560
- return (data !== undefined ? makeSpriteData(data) : [[], -1]);
7693
+ return (data !== undefined ? g_animationFunc.make[_header](data, calcFrameFunc) : [[], -1]);
7561
7694
  };
7562
7695
 
7563
7696
  // 速度変化データの分解 (2つで1セット)
@@ -7607,34 +7740,30 @@ const scoreConvert = (_dosObj, _scoreId, _preblankFrame, _dummyNo = ``,
7607
7740
  [obj.wordData, obj.wordMaxDepth] = makeWordData(scoreIdHeader);
7608
7741
  }
7609
7742
 
7610
- // 背景・マスクデータの分解 (下記すべてで1セット、改行区切り)
7611
- // [フレーム数, 階層, 背景パス, class(CSSで別定義), X, Y, width, height, opacity, animationName, animationDuration]
7612
- obj.maskData = [];
7613
- obj.maskMaxDepth = -1;
7614
- obj.backData = [];
7615
- obj.backMaxDepth = -1;
7616
- if (g_stateObj.d_background === C_FLG_OFF) {
7617
- } else {
7618
- g_animationData.forEach(sprite =>
7619
- [obj[`${sprite}Data`], obj[`${sprite}MaxDepth`]] = makeBackgroundData(sprite, scoreIdHeader));
7620
- }
7743
+ // 背景・マスク・スキン変更データの分解 (下記すべてで1セット、改行区切り)
7744
+ // - 背景・マスク: [フレーム数, 階層, 背景パス, class(CSSで別定義), X, Y, width, height, opacity, animationName, animationDuration, animationFillMode]
7745
+ // - スキン変更 : [フレーム数, CSSカスタムプロパティ名, 設定内容]
7746
+ g_animationData.forEach(sprite => {
7747
+ obj[`${sprite}Data`] = [];
7748
+ obj[`${sprite}MaxDepth`] = -1;
7621
7749
 
7622
- // 結果画面用・背景/マスクデータの分解 (下記すべてで1セット、改行区切り)
7623
- // [フレーム数,階層,背景パス,class(CSSで別定義),X,Y,width,height,opacity,animationName,animationDuration]
7624
- if (g_stateObj.d_background === C_FLG_OFF && g_headerObj.resultMotionSet) {
7625
- const backgroundResults = [`backResult`, `maskResult`, `backFailed`, `maskFailed`];
7626
- backgroundResults.forEach(backName => {
7627
- g_headerObj[`${backName}Data`] = [];
7628
- g_headerObj[`${backName}MaxDepth`] = -1;
7629
- });
7630
- } else {
7631
- g_animationData.forEach(sprite => {
7750
+ if (g_stateObj.d_background === C_FLG_OFF) {
7751
+ } else {
7752
+ [obj[`${sprite}Data`], obj[`${sprite}MaxDepth`]] = makeBackgroundData(sprite, scoreIdHeader);
7753
+ }
7754
+
7755
+ if (g_stateObj.d_background === C_FLG_OFF && g_headerObj.resultMotionSet) {
7756
+ [`Result`, `Failed`].forEach(backName => {
7757
+ g_headerObj[`${backName}Data`] = [];
7758
+ g_headerObj[`${backName}MaxDepth`] = -1;
7759
+ });
7760
+ } else {
7632
7761
  [g_headerObj[`${sprite}ResultData`], g_headerObj[`${sprite}ResultMaxDepth`]] =
7633
- makeBackgroundResultData(sprite, `result`, scoreIdHeader);
7762
+ makeBackgroundData(sprite, scoreIdHeader, { resultTypes: [`result`] });
7634
7763
  [g_headerObj[`${sprite}FailedData`], g_headerObj[`${sprite}FailedMaxDepth`]] =
7635
- makeBackgroundResultData(sprite, `failed${g_stateObj.lifeMode.slice(0, 1)}`, scoreIdHeader, `result`);
7636
- });
7637
- }
7764
+ makeBackgroundData(sprite, scoreIdHeader, { resultTypes: [`failed${g_stateObj.lifeMode.slice(0, 1)}`, `result`] });
7765
+ }
7766
+ });
7638
7767
 
7639
7768
  // キー変化定義
7640
7769
  obj.keychFrames = [0];
@@ -8040,6 +8169,7 @@ const pushArrows = (_dataObj, _speedOnFrame, _motionOnFrame, _firstArrivalFrame)
8040
8169
  word: (_exceptList, _j) => listMatching(_data[startNum][_j][1], _exceptList.word),
8041
8170
  back: (_exceptList, _j) => listMatching(_data[startNum][_j].animationName, _exceptList.back),
8042
8171
  mask: (_exceptList, _j) => listMatching(_data[startNum][_j].animationName, _exceptList.mask),
8172
+ style: (_exceptList, _j) => listMatching(_data[startNum][_j].depth, _exceptList.style),
8043
8173
  };
8044
8174
 
8045
8175
  const getLength = _list =>
@@ -8603,11 +8733,11 @@ const mainInit = _ => {
8603
8733
  // カラー・モーションを適用するオブジェクトの種類
8604
8734
  const objList = (g_stateObj.dummyId === `` ? [``] : [`dummy`, ``]);
8605
8735
 
8606
- // 背景・マスクモーション(0フレーム指定)
8736
+ // 背景・マスクモーション、スキン変更(0フレーム指定)
8607
8737
  if (g_scoreObj.frameNum === 0) {
8608
8738
  g_animationData.forEach(sprite => {
8609
8739
  if (g_scoreObj[`${sprite}Data`][0] !== undefined) {
8610
- drawMainSpriteData(0, sprite);
8740
+ g_animationFunc.drawMain[sprite](0, sprite);
8611
8741
  g_scoreObj[`${sprite}Data`][0] = undefined;
8612
8742
  }
8613
8743
  });
@@ -8907,7 +9037,7 @@ const mainInit = _ => {
8907
9037
  x: jdgX[j] + 170, y: jdgY[j],
8908
9038
  w: g_limitObj.jdgCharaWidth, h: g_limitObj.jdgCharaHeight, siz: g_limitObj.jdgCharaSiz,
8909
9039
  opacity: g_stateObj.opacity / 100, display: g_workObj.judgmentDisp,
8910
- }, g_cssObj[`common_${jdgCombos[j]}`]),
9040
+ }, g_cssObj[`common_combo${jdg}`]),
8911
9041
 
8912
9042
  // Fast/Slow表示
8913
9043
  createDivCss2Label(`diff${jdg}`, ``, {
@@ -9585,10 +9715,10 @@ const mainInit = _ => {
9585
9715
  }
9586
9716
  }
9587
9717
 
9588
- // 背景・マスクモーション
9718
+ // 背景・マスクモーション、スキン変更
9589
9719
  g_animationData.forEach(sprite => {
9590
9720
  if (g_scoreObj[`${sprite}Data`][currentFrame] !== undefined) {
9591
- drawMainSpriteData(currentFrame, sprite);
9721
+ g_animationFunc.drawMain[sprite](currentFrame, sprite);
9592
9722
  }
9593
9723
  });
9594
9724
 
@@ -10138,14 +10268,14 @@ const displayDiff = (_difFrame, _fjdg = ``, _justFrames = g_headerObj.justFrames
10138
10268
  g_workObj.diffList.push(_difFrame);
10139
10269
  const difCnt = Math.abs(_difFrame);
10140
10270
  if (_difFrame > g_judgObj.arrowJ[g_judgPosObj.shobon]) {
10141
- diffJDisp = `<span class="common_kita">Excessive</span>`;
10271
+ diffJDisp = `<span class="common_excessive">Excessive</span>`;
10142
10272
  g_resultObj.excessive++;
10143
10273
  lifeDamage(true);
10144
10274
  } else if (_difFrame > _justFrames) {
10145
- diffJDisp = `<span class="common_matari">Fast ${difCnt} Frames</span>`;
10275
+ diffJDisp = `<span class="common_diffFast">Fast ${difCnt} Frames</span>`;
10146
10276
  g_resultObj.fast++;
10147
10277
  } else if (_difFrame < _justFrames * (-1)) {
10148
- diffJDisp = `<span class="common_shobon">Slow ${difCnt} Frames</span>`;
10278
+ diffJDisp = `<span class="common_diffSlow">Slow ${difCnt} Frames</span>`;
10149
10279
  g_resultObj.slow++;
10150
10280
  }
10151
10281
  document.getElementById(`diff${_fjdg}J`).innerHTML = diffJDisp;
@@ -10371,12 +10501,12 @@ const resultInit = _ => {
10371
10501
 
10372
10502
  // 結果画面用フレーム初期化
10373
10503
  g_scoreObj.resultFrameNum = 0;
10374
- g_scoreObj.backResultFrameNum = 0;
10375
- g_scoreObj.maskResultFrameNum = 0;
10376
10504
 
10377
- // 結果画面用ループカウンター
10378
- g_scoreObj.backResultLoopCount = 0;
10379
- g_scoreObj.maskResultLoopCount = 0;
10505
+ // リザルトアニメーション用フレーム初期化、ループカウンター設定
10506
+ g_animationData.forEach(sprite => {
10507
+ g_scoreObj[`${sprite}ResultFrameNum`] = 0;
10508
+ g_scoreObj[`${sprite}ResultLoopCount`] = 0;
10509
+ });
10380
10510
 
10381
10511
  const divRoot = document.querySelector(`#divRoot`);
10382
10512
 
@@ -10394,14 +10524,14 @@ const resultInit = _ => {
10394
10524
  g_animationData.forEach(sprite => {
10395
10525
  const failedData = g_rootObj[`${sprite}failedS${scoreIdHeader}_data`] ?? g_rootObj[`${sprite}failedS_data`];
10396
10526
  if (failedData !== undefined) {
10397
- [g_headerObj[`${sprite}ResultData`], g_headerObj[`${sprite}ResultMaxDepth`]] = makeSpriteData(failedData);
10527
+ [g_headerObj[`${sprite}ResultData`], g_headerObj[`${sprite}ResultMaxDepth`]] = g_animationFunc.make[sprite](failedData);
10398
10528
  }
10399
10529
  });
10400
10530
  } else if (g_gameOverFlg) {
10401
- g_headerObj.backResultData = g_headerObj.backFailedData.concat();
10402
- g_headerObj.maskResultData = g_headerObj.maskFailedData.concat();
10403
- g_headerObj.backResultMaxDepth = g_headerObj.backFailedMaxDepth;
10404
- g_headerObj.maskResultMaxDepth = g_headerObj.maskFailedMaxDepth;
10531
+ g_animationData.forEach(sprite => {
10532
+ g_headerObj[`${sprite}ResultData`] = g_headerObj[`${sprite}FailedData`].concat();
10533
+ g_headerObj[`${sprite}ResultMaxDepth`] = g_headerObj[`${sprite}FailedMaxDepth`];
10534
+ });
10405
10535
  }
10406
10536
  }
10407
10537
 
@@ -10482,12 +10612,14 @@ const resultInit = _ => {
10482
10612
  const withOptions = (_flg, _defaultSet, _displayText = _flg) =>
10483
10613
  (_flg !== _defaultSet ? getStgDetailName(_displayText) : ``);
10484
10614
 
10485
- let difData = [
10615
+ const difDatas = [
10486
10616
  `${getKeyName(g_headerObj.keyLabels[g_stateObj.scoreId])}${transKeyData} ${getStgDetailName('key')} / ${g_headerObj.difLabels[g_stateObj.scoreId]}`,
10487
10617
  `${withOptions(g_autoPlaysBase.includes(g_stateObj.autoPlay), true, `-${getStgDetailName(g_stateObj.autoPlay)}${getStgDetailName('less')}`)}`,
10488
10618
  `${withOptions(g_headerObj.makerView, false, `(${g_headerObj.creatorNames[g_stateObj.scoreId]})`)}`,
10489
10619
  `${withOptions(g_stateObj.shuffle, C_FLG_OFF, `[${getStgDetailName(g_stateObj.shuffle)}]`)}`
10490
- ].filter(value => value !== ``).join(` `);
10620
+ ];
10621
+ let difData = difDatas.filter(value => value !== ``).join(` `);
10622
+ const difDataForImage = difDatas.filter((value, j) => value !== `` && j !== 2).join(` `);
10491
10623
 
10492
10624
  let playStyleData = [
10493
10625
  `${g_stateObj.speed}${g_lblNameObj.multi}`,
@@ -10540,16 +10672,16 @@ const resultInit = _ => {
10540
10672
 
10541
10673
  // キャラクタ、スコア描画のID共通部、色CSS名、スコア変数名
10542
10674
  const jdgScoreObj = {
10543
- ii: { pos: 0, id: `Ii`, color: `ii`, label: g_lblNameObj.j_ii, },
10544
- shakin: { pos: 1, id: `Shakin`, color: `shakin`, label: g_lblNameObj.j_shakin, },
10545
- matari: { pos: 2, id: `Matari`, color: `matari`, label: g_lblNameObj.j_matari, },
10546
- shobon: { pos: 3, id: `Shobon`, color: `shobon`, label: g_lblNameObj.j_shobon, },
10547
- uwan: { pos: 4, id: `Uwan`, color: `uwan`, label: g_lblNameObj.j_uwan, },
10548
- kita: { pos: 5, id: `Kita`, color: `kita`, label: g_lblNameObj.j_kita, },
10549
- iknai: { pos: 6, id: `Iknai`, color: `iknai`, label: g_lblNameObj.j_iknai, },
10550
- maxCombo: { pos: 7, id: `MCombo`, color: `combo`, label: g_lblNameObj.j_maxCombo, },
10551
- fmaxCombo: { pos: 8, id: `FCombo`, color: `combo`, label: g_lblNameObj.j_fmaxCombo, },
10552
- score: { pos: 10, id: `Score`, color: `score`, label: g_lblNameObj.j_score, },
10675
+ ii: { pos: 0, id: `Ii`, color: `ii`, label: g_lblNameObj.j_ii, dfColor: `#66ffff`, },
10676
+ shakin: { pos: 1, id: `Shakin`, color: `shakin`, label: g_lblNameObj.j_shakin, dfColor: `#99ff99`, },
10677
+ matari: { pos: 2, id: `Matari`, color: `matari`, label: g_lblNameObj.j_matari, dfColor: `#ff9966`, },
10678
+ shobon: { pos: 3, id: `Shobon`, color: `shobon`, label: g_lblNameObj.j_shobon, dfColor: `#ccccff`, },
10679
+ uwan: { pos: 4, id: `Uwan`, color: `uwan`, label: g_lblNameObj.j_uwan, dfColor: `#ff9999`, },
10680
+ kita: { pos: 5, id: `Kita`, color: `kita`, label: g_lblNameObj.j_kita, dfColor: `#ffff99`, },
10681
+ iknai: { pos: 6, id: `Iknai`, color: `iknai`, label: g_lblNameObj.j_iknai, dfColor: `#99ff66`, },
10682
+ maxCombo: { pos: 7, id: `MCombo`, color: `combo`, label: g_lblNameObj.j_maxCombo, dfColor: `#ffffff`, },
10683
+ fmaxCombo: { pos: 8, id: `FCombo`, color: `combo`, label: g_lblNameObj.j_fmaxCombo, dfColor: `#ffffff`, },
10684
+ score: { pos: 10, id: `Score`, color: `score`, label: g_lblNameObj.j_score, dfColor: `#ffffff`, },
10553
10685
  };
10554
10686
 
10555
10687
  // キャラクタ、スコア描画
@@ -10560,20 +10692,20 @@ const resultInit = _ => {
10560
10692
  ));
10561
10693
  if (g_stateObj.autoAll === C_FLG_OFF) {
10562
10694
  multiAppend(resultWindow,
10563
- makeCssResultSymbol(`lblFast`, 350, g_cssObj.common_matari, 0, g_lblNameObj.j_fast),
10564
- makeCssResultSymbol(`lblSlow`, 350, g_cssObj.common_shobon, 2, g_lblNameObj.j_slow),
10695
+ makeCssResultSymbol(`lblFast`, 350, g_cssObj.common_diffFast, 0, g_lblNameObj.j_fast),
10696
+ makeCssResultSymbol(`lblSlow`, 350, g_cssObj.common_diffSlow, 2, g_lblNameObj.j_slow),
10565
10697
  makeCssResultSymbol(`lblFastS`, 260, g_cssObj.score, 1, g_resultObj.fast, C_ALIGN_RIGHT),
10566
10698
  makeCssResultSymbol(`lblSlowS`, 260, g_cssObj.score, 3, g_resultObj.slow, C_ALIGN_RIGHT),
10567
10699
  );
10568
10700
  if (estimatedAdj !== ``) {
10569
10701
  multiAppend(resultWindow,
10570
- makeCssResultSymbol(`lblAdj`, 350, g_cssObj.common_shakin, 4, g_lblNameObj.j_adj),
10702
+ makeCssResultSymbol(`lblAdj`, 350, g_cssObj.common_estAdj, 4, g_lblNameObj.j_adj),
10571
10703
  makeCssResultSymbol(`lblAdjS`, 260, g_cssObj.score, 5, `${getDiffFrame(estimatedAdj)}`, C_ALIGN_RIGHT),
10572
10704
  );
10573
10705
  }
10574
10706
  if (g_stateObj.excessive === C_FLG_ON) {
10575
10707
  multiAppend(resultWindow,
10576
- makeCssResultSymbol(`lblExcessive`, 350, g_cssObj.common_kita, 6, g_lblNameObj.j_excessive),
10708
+ makeCssResultSymbol(`lblExcessive`, 350, g_cssObj.common_excessive, 6, g_lblNameObj.j_excessive),
10577
10709
  makeCssResultSymbol(`lblExcessiveS`, 260, g_cssObj.score, 7, g_resultObj.excessive, C_ALIGN_RIGHT),
10578
10710
  );
10579
10711
  }
@@ -10702,6 +10834,7 @@ const resultInit = _ => {
10702
10834
  }
10703
10835
  const twiturl = new URL(g_localStorageUrl);
10704
10836
  twiturl.searchParams.append(`scoreId`, g_stateObj.scoreId);
10837
+ const baseTwitUrl = g_isLocal ? `` : `${twiturl.toString()}`.replace(/[\t\n]/g, ``);
10705
10838
 
10706
10839
  const tweetExcessive = (g_stateObj.excessive === C_FLG_ON) ? `(+${g_resultObj.excessive})` : ``;
10707
10840
 
@@ -10723,7 +10856,7 @@ const resultInit = _ => {
10723
10856
  [`[arrowJdg]`, `${g_resultObj.ii}-${g_resultObj.shakin}-${g_resultObj.matari}-${g_resultObj.shobon}-${g_resultObj.uwan}${tweetExcessive}`],
10724
10857
  [`[frzJdg]`, tweetFrzJdg],
10725
10858
  [`[maxCombo]`, tweetMaxCombo],
10726
- [`[url]`, g_isLocal ? `` : `${twiturl.toString()}`.replace(/[\t\n]/g, ``)]
10859
+ [`[url]`, baseTwitUrl]
10727
10860
  ]);
10728
10861
  if (g_presetObj.resultVals !== undefined) {
10729
10862
  Object.keys(g_presetObj.resultVals).forEach(key =>
@@ -10731,6 +10864,111 @@ const resultInit = _ => {
10731
10864
  }
10732
10865
  const resultText = `${unEscapeHtml(tweetResultTmp)}`;
10733
10866
  const tweetResult = `https://twitter.com/intent/tweet?text=${encodeURIComponent(resultText)}`;
10867
+ const currentDateTime = new Date().toLocaleString();
10868
+
10869
+ /**
10870
+ * リザルト画像をCanvasで作成しクリップボードへコピー
10871
+ * @param {string} _msg
10872
+ */
10873
+ const copyResultImageData = _msg => {
10874
+ const tmpDiv = createEmptySprite(divRoot, `tmpDiv`, { x: 0, y: 0, w: g_sWidth, h: g_sHeight });
10875
+ tmpDiv.style.background = `#000000cc`;
10876
+ const canvas = document.createElement(`canvas`);
10877
+ const artistName = g_headerObj.artistNames[g_headerObj.musicNos[g_stateObj.scoreId]] || g_headerObj.artistName;
10878
+
10879
+ canvas.id = `resultImage`;
10880
+ canvas.width = 400;
10881
+ canvas.height = 410;
10882
+ canvas.style.left = `${(g_sWidth - canvas.width) / 2}px`;
10883
+ canvas.style.top = `20px`;
10884
+ canvas.style.position = `absolute`;
10885
+
10886
+ const context = canvas.getContext(`2d`);
10887
+ const drawText = (_text, { x = 30, dy = 0, hy, siz = 15, color = `#cccccc`, align = C_ALIGN_LEFT, font } = {}) => {
10888
+ context.font = `${siz}px ${getBasicFont(font)}`;
10889
+ context.fillStyle = color;
10890
+ context.textAlign = align;
10891
+ context.fillText(_text, x, 35 + hy * 18 + dy);
10892
+ };
10893
+ const grd = context.createLinearGradient(0, 0, 0, canvas.height);
10894
+ grd.addColorStop(0, `#000000`);
10895
+ grd.addColorStop(1, `#222222`);
10896
+ context.fillStyle = grd;
10897
+ context.fillRect(0, 0, g_sWidth, g_sHeight);
10898
+
10899
+ drawText(`R`, { dy: -5, hy: 0, siz: 40, color: `#9999ff` });
10900
+ drawText(`ESULT`, { x: 57, dy: -5, hy: 0, siz: 25 });
10901
+ drawText(`${g_lblNameObj.dancing}${g_lblNameObj.star}${g_lblNameObj.onigiri}`,
10902
+ { x: 280, dy: -15, hy: 0, siz: 20, color: `#999999`, align: C_ALIGN_CENTER });
10903
+ drawText(mTitleForView[0], { hy: 1 });
10904
+ drawText(mTitleForView[1], { hy: 2 });
10905
+ drawText(`📝 ${g_headerObj.tuning} / 🎵 ${artistName}`, { hy: mTitleForView[1] !== `` ? 3 : 2, siz: 12 });
10906
+ drawText(difDataForImage, { hy: 4 });
10907
+ drawText(playStyleData, { hy: 5 });
10908
+
10909
+ Object.keys(jdgScoreObj).forEach(score => {
10910
+ drawText(g_lblNameObj[`j_${score}`], { hy: 7 + jdgScoreObj[score].pos, color: jdgScoreObj[score].dfColor });
10911
+ drawText(g_resultObj[score], { x: 200, hy: 7 + jdgScoreObj[score].pos, align: C_ALIGN_RIGHT });
10912
+ });
10913
+
10914
+ if (highscoreCondition) {
10915
+ drawText(`(${highscoreDfObj.score >= 0 ? '+' : '-'} ${Math.abs(highscoreDfObj.score)})`,
10916
+ { x: 206, hy: 18, color: highscoreDfObj.score > 0 ? `#ffff99` : `#cccccc`, align: C_ALIGN_RIGHT });
10917
+ }
10918
+
10919
+ if (g_stateObj.autoAll === C_FLG_OFF) {
10920
+ drawText(g_lblNameObj.j_fast, { x: 240, hy: 7, color: `#ff9966` });
10921
+ drawText(g_resultObj.fast, { x: 360, hy: 7, align: C_ALIGN_RIGHT });
10922
+ drawText(g_lblNameObj.j_slow, { x: 240, hy: 8, color: `#ccccff` });
10923
+ drawText(g_resultObj.slow, { x: 360, hy: 8, align: C_ALIGN_RIGHT });
10924
+ if (estimatedAdj !== ``) {
10925
+ drawText(g_lblNameObj.j_adj, { x: 240, hy: 9, color: `#99ff99` });
10926
+ drawText(getDiffFrame(estimatedAdj), { x: 360, hy: 9, align: C_ALIGN_RIGHT });
10927
+ }
10928
+ if (g_stateObj.excessive === C_FLG_ON) {
10929
+ drawText(g_lblNameObj.j_excessive, { x: 240, hy: 10, color: `#ffff99` });
10930
+ drawText(g_resultObj.excessive, { x: 360, hy: 10, align: C_ALIGN_RIGHT });
10931
+ }
10932
+ }
10933
+ drawText(rankMark, { x: 240, hy: 18, siz: 50, color: rankColor, font: `"Bookman Old Style"` });
10934
+ drawText(baseTwitUrl, { hy: 19, siz: 8 });
10935
+ drawText(currentDateTime, { hy: 20 });
10936
+
10937
+ tmpDiv.appendChild(canvas);
10938
+
10939
+ try {
10940
+ if (ClipboardItem === undefined) {
10941
+ throw new Error(`error`);
10942
+ }
10943
+ // Canvas の内容を PNG 画像として取得
10944
+ canvas.toBlob(async blob => {
10945
+ await navigator.clipboard.write([
10946
+ new ClipboardItem({
10947
+ 'image/png': blob
10948
+ })
10949
+ ]);
10950
+ });
10951
+ tmpDiv.removeChild(canvas);
10952
+ divRoot.removeChild(tmpDiv);
10953
+ makeInfoWindow(_msg, `leftToRightFade`);
10954
+
10955
+ } catch (err) {
10956
+ // 画像をクリップボードへコピーできないときは代替で画像保存可能な画面を表示
10957
+ if (document.getElementById(`tmpClose`) === null) {
10958
+ divRoot.oncontextmenu = _ => true;
10959
+ makeLinkButton(tmpDiv, `Tmp`);
10960
+ tmpDiv.appendChild(createCss2Button(`tmpClose`, g_lblNameObj.b_close, _ => true,
10961
+ Object.assign(g_lblPosObj.btnRsCopyClose, {
10962
+ resetFunc: _ => {
10963
+ tmpDiv.removeChild(canvas);
10964
+ divRoot.removeChild(tmpDiv);
10965
+ divRoot.oncontextmenu = _ => false;
10966
+ },
10967
+ }), g_cssObj.button_Back));
10968
+ tmpDiv.appendChild(createDescDiv(`resultImageDesc`, g_lblNameObj.resultImageDesc));
10969
+ }
10970
+ }
10971
+ };
10734
10972
 
10735
10973
  /** 音源、ループ処理の停止 */
10736
10974
  const resetCommonBtn = (_id, _name, _posObj, _func, _cssClass) =>
@@ -10742,6 +10980,25 @@ const resultInit = _ => {
10742
10980
  clearTimeout(g_timeoutEvtResultId);
10743
10981
  }, Object.assign(_posObj, { resetFunc: _func }), _cssClass);
10744
10982
 
10983
+ /**
10984
+ * 外部リンクボタンを作成
10985
+ * @param {object} _div
10986
+ * @param {string} _param
10987
+ */
10988
+ const makeLinkButton = (_div = divRoot, _param = ``) => {
10989
+ multiAppend(_div,
10990
+ // リザルトデータをTwitterへ転送
10991
+ createCss2Button(`btnTweet${_param}`, g_lblNameObj.b_tweet, _ => true, Object.assign(g_lblPosObj.btnRsTweet, {
10992
+ resetFunc: _ => openLink(tweetResult),
10993
+ }), g_cssObj.button_Tweet),
10994
+
10995
+ // Gitterへのリンク
10996
+ createCss2Button(`btnGitter${_param}`, g_lblNameObj.b_gitter, _ => true, Object.assign(g_lblPosObj.btnRsGitter, {
10997
+ resetFunc: _ => openLink(`https://app.gitter.im/#/room/#danonicw_freeboard:gitter.im`),
10998
+ }), g_cssObj.button_Default),
10999
+ );
11000
+ }
11001
+
10745
11002
  // ボタン描画
10746
11003
  multiAppend(divRoot,
10747
11004
 
@@ -10751,19 +11008,16 @@ const resultInit = _ => {
10751
11008
  // リザルトデータをクリップボードへコピー
10752
11009
  createCss2Button(`btnCopy`, g_lblNameObj.b_copy, _ => copyTextToClipboard(resultText, g_msgInfoObj.I_0001),
10753
11010
  g_lblPosObj.btnRsCopy, g_cssObj.button_Setting),
10754
-
10755
- // リザルトデータをTwitterへ転送
10756
- createCss2Button(`btnTweet`, g_lblNameObj.b_tweet, _ => true, Object.assign(g_lblPosObj.btnRsTweet, {
10757
- resetFunc: _ => openLink(tweetResult),
10758
- }), g_cssObj.button_Tweet),
10759
-
10760
- // Gitterへのリンク
10761
- createCss2Button(`btnGitter`, g_lblNameObj.b_gitter, _ => true, Object.assign(g_lblPosObj.btnRsGitter, {
10762
- resetFunc: _ => openLink(`https://app.gitter.im/#/room/#danonicw_freeboard:gitter.im`),
10763
- }), g_cssObj.button_Default),
10764
-
11011
+ );
11012
+ makeLinkButton();
11013
+ multiAppend(divRoot,
10765
11014
  // リトライ
10766
11015
  resetCommonBtn(`btnRetry`, g_lblNameObj.b_retry, g_lblPosObj.btnRsRetry, loadMusic, g_cssObj.button_Reset),
11016
+
11017
+ createCss2Button(`btnCopyImage`, `📷`, _ => true,
11018
+ Object.assign(g_lblPosObj.btnRsCopyImage, {
11019
+ resetFunc: _ => copyResultImageData(g_msgInfoObj.I_0001),
11020
+ }), g_cssObj.button_Default_NoColor),
10767
11021
  );
10768
11022
 
10769
11023
  // マスクスプライトを作成
@@ -10776,7 +11030,7 @@ const resultInit = _ => {
10776
11030
  g_animationData.forEach(sprite => {
10777
11031
  if (g_scoreObj[`${sprite}ResultFrameNum`] === 0) {
10778
11032
  if (g_headerObj[`${sprite}ResultData`][0] !== undefined) {
10779
- g_scoreObj[`${sprite}ResultFrameNum`] = drawSpriteData(0, `result`, sprite);
11033
+ g_scoreObj[`${sprite}ResultFrameNum`] = g_animationFunc.draw[sprite](0, `result`, sprite);
10780
11034
  g_headerObj[`${sprite}ResultData`][0] = undefined;
10781
11035
  }
10782
11036
  }
@@ -10790,7 +11044,7 @@ const resultInit = _ => {
10790
11044
  // ユーザカスタムイベント(フレーム毎)
10791
11045
  g_customJsObj.resultEnterFrame.forEach(func => func());
10792
11046
 
10793
- // 背景・マスクモーション
11047
+ // 背景・マスクモーション、スキン変更
10794
11048
  drawTitleResultMotion(g_currentPage);
10795
11049
 
10796
11050
  // リザルト画面移行後のフェードアウト処理
@@ -10813,14 +11067,13 @@ const resultInit = _ => {
10813
11067
  buffTime = thisTime - resultStartTime - g_scoreObj.resultFrameNum * 1000 / g_fps;
10814
11068
 
10815
11069
  g_scoreObj.resultFrameNum++;
10816
- g_scoreObj.backResultFrameNum++;
10817
- g_scoreObj.maskResultFrameNum++;
11070
+ g_animationData.forEach(sprite => g_scoreObj[`${sprite}ResultFrameNum`]++);
10818
11071
  g_timeoutEvtResultId = setTimeout(flowResultTimeline, 1000 / g_fps - buffTime);
10819
11072
  };
10820
11073
  flowResultTimeline();
10821
11074
 
10822
11075
  // キー操作イベント(デフォルト)
10823
- setShortcutEvent(g_currentPage);
11076
+ setShortcutEvent(g_currentPage, _ => true, { dfEvtFlg: true });
10824
11077
  document.oncontextmenu = _ => true;
10825
11078
 
10826
11079
  g_skinJsObj.result.forEach(func => func());