├── .eslintrc.json
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── Add Chart.md
│ └── Bug Report.md
├── PULL_REQUEST_TEMPLATE
│ ├── Add Chart.md
│ └── Code Contributes.md
└── workflows
│ ├── build-and-deploy.yml
│ └── codeql-analysis.yml
├── .gitignore
├── .prettierrc
├── .vscode
└── settings.json
├── AssetSources
├── audio
│ ├── Drag.aup3
│ ├── Exit.aup3
│ ├── Flick.aup3
│ ├── LevelOver.aup3
│ ├── Pause.aup3
│ ├── Start.aup3
│ ├── Tap.aup3
│ ├── calibrate.aup3
│ └── selectSongItem.aup3
└── images
│ ├── hitKeys.sketch
│ └── uiElement.sketch
├── LICENSE
├── Readme.MD
├── assets
├── audio
│ ├── Exit.mp3
│ ├── Pause.mp3
│ ├── Start.mp3
│ └── selectSongItem.mp3
├── css
│ ├── css.css
│ └── fonts
│ │ ├── Exo-Regular.woff
│ │ ├── Exo-Regular.woff2
│ │ ├── Saira.woff2
│ │ ├── SourceHanSans&SairaHybrid.woff
│ │ ├── SourceHanSans&SairaHybrid.woff2
│ │ └── fonts.css
├── images
│ ├── A15A.svg
│ ├── AppIcon.png
│ ├── AppIcon.svg
│ ├── Avatar.svg
│ ├── B15B.svg
│ ├── Back.svg
│ ├── C15C.svg
│ ├── ElementSqare.Half.Size.png
│ ├── ElementSqare.Half.Size.webp
│ ├── ElementSqare.png
│ ├── ElementSqare.webp
│ ├── F15F.svg
│ ├── Restart.svg
│ ├── Resume.svg
│ ├── S15S.svg
│ ├── Settings.svg
│ ├── Sort.svg
│ ├── Tick.svg
│ ├── Title.svg
│ ├── TrashBin.svg
│ ├── V15FC.svg
│ ├── V15V.svg
│ ├── phi15phi.svg
│ ├── showgirl.png
│ └── showgirl_Half.png
└── tips.json
├── config
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js
├── jsconfig.json
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── public
├── 404.css
├── 404.html
├── AppIcon.png
├── CNAME
├── favicon.ico
└── manifest.webmanifest
└── src
├── LevelOver
├── LevelOver0.ogg
├── LevelOver1.ogg
├── LevelOver2.ogg
├── LevelOver3.ogg
├── index.html
├── index.js
└── style.css
├── aboutUs
├── AboutUs.mp3
├── Trigger.js
├── index.js
└── style.css
├── cacheControl
├── index.html
├── index.js
└── style.css
├── chapterSelect
├── ChapterSelect0.mp3
├── index.html
├── index.js
└── style.css
├── constants.js
├── getChart
├── index.html
├── index.js
└── style.css
├── index.html
├── index.redirect.js
├── loadingChartScreen
├── index.html
├── index.js
└── style.css
├── loadingScreen
├── index.html
├── index.js
└── style.css
├── settings
├── calibrate
│ ├── calibrate.mp3
│ ├── index.html
│ ├── index.js
│ └── style.css
├── components
│ ├── button.js
│ ├── index.js
│ ├── slide.js
│ └── toggle.js
├── index.html
├── index.js
├── setting.js
├── statistic
│ ├── index.html
│ ├── index.js
│ └── style.css
└── style.css
├── songSelect
├── SongList.js
├── index.html
├── index.js
└── style.css
├── style.redirect.css
├── sw.js
├── tapToStart
├── TapToStart.mp3
├── index.html
├── index.js
└── style.css
├── template.html
├── utils
└── DB.js
└── whilePlaying
├── assets
├── 0.png
├── Back.svg
├── Drag.ogg
├── Drag.png
├── DragHL.png
├── Flick.ogg
├── Flick.png
├── FlickHL.png
├── Hold.png
├── HoldEnd.png
├── HoldHL.png
├── HoldHead.png
├── HoldHeadHL.png
├── JudgeLine.png
├── Pause.png
├── ProgressBar.png
├── Restart.svg
├── Resume.svg
├── SongNameBar.png
├── Tap.ogg
├── Tap.png
├── Tap2.png
├── TapHL.png
├── clickRaw.png
├── createImageBitmap.js
├── mute.ogg
├── oggmented-bundle.js
├── oldui
│ ├── Drag.png
│ ├── Drag2HL.png
│ ├── Flick.png
│ ├── Flick2HL.png
│ ├── HoldBody.png
│ ├── HoldEnd.png
│ ├── Tap.png
│ ├── Tap2.png
│ ├── Tap2HL.png
│ └── clickRaw.png
├── stackblur.min.js
└── stackblur.min.js.map
├── index.html
├── pec2json.js
├── resource.js
├── script.phi.community.core.js
├── style.css
└── tutorial.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "node": true
6 | },
7 | "extends": "eslint:recommended",
8 | "parserOptions": {
9 | "ecmaVersion": "latest",
10 | "sourceType": "module"
11 | },
12 | "rules": {}
13 | }
14 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: ['Yuameshi']
4 | patreon: yuameshi
5 | custom: ['https://afdian.net/@yuameshi','https://www.han-han.xyz/about/']
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Add Chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 申请收录谱面
3 | about: 如果您要申请收录您的谱面,请使用此ISSUE
4 | ---
5 |
6 | # 谱面下载地址
7 |
8 | 请写出下载地址
9 |
10 | # 谱面演示:
11 |
12 | 给出合适的视频或者第三方视频网站链接
13 |
14 | # 谱面信息
15 |
16 | - 名称
17 | - 代号(不能重复,已有代号前往[此仓库](https://github.com/Yuameshi/PhiCommunity-Charts-Repo)查看)
18 | - 作曲家
19 | - 音乐文件名称
20 | - EZ/HD/IN/AT定数(只有一个定数则写单定数)
21 | - 判定线贴图(可选)
22 | - EZ/HD/IN/AT文件名(只有一个定数则写单文件)
23 | - 曲绘文件名
24 | - 曲绘画师
25 | - 谱师
26 | - 裁切音频(开始时间)
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/Bug Report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 错误报告
3 | about: 如果您要报告错误信息,请使用此Issue模板
4 | ---
5 | # 运行环境
6 |
7 | - 设备:(如iPhone 11,Redmi K30 Ultra等等)
8 |
9 | - 设备平台: Android/iOS(HarmonyOS当Android处理)
10 |
11 | - 浏览器:请写出完整浏览器,如果您的浏览器有详细版本页面(如`chrome://version`),请在下方添加图片,如无,请写出浏览器的`User-Agent`信息,若您使用Webview类浏览器(如Via,请在手机设置中找到Webview应用的版本并填入)。
12 |
13 | - 错误现象: 请描述出完整的复现过程(可以录屏,无录屏则多截几个屏,Android则最好打开点击小白点)
14 |
15 | - 错误Log:在移动端打开`chrome://inspect`(此网页仅限部分Chromium浏览器才可以打开)等网页查看日志,在Android手机上可以参见[这篇文章](https://docs.microsoft.com/zh-cn/microsoft-edge/devtools-guide-chromium/remote-debugging/
16 | )借助计算机查看更加详细的日志,对于iOS的Safari,可以通过[这篇文章](https://www.browserstack.com/guide/how-to-debug-on-iphone)获取详细日志
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/Add Chart.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 申请收录谱面
3 | about: 如果您要申请收录您的谱面,请使用此Pull Requests模板
4 | ---
5 | # 谱面演示:
6 |
7 | 您本地演示此项目能够正常运行的录屏,可以托管到第三方视频平台(GitHub似乎限制10MiB)
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/Code Contributes.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 代码改进
3 | about: 如果您改进了代码或修复了Bug,请使用此Pull Requests模板
4 | ---
5 | ## 变更
6 |
7 | 从开发角度做出了何种变更,从用户角度修复了什么
8 |
9 | ## 截屏或录屏
10 |
11 | 纯视觉方面推荐GIF录屏,带有音频的则放录屏
--------------------------------------------------------------------------------
/.github/workflows/build-and-deploy.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | if: github.event.repository.owner.id == github.event.sender.id
12 |
13 | steps:
14 | - name: Checkout Source
15 | uses: actions/checkout@v2
16 | with:
17 | ref: main
18 |
19 | - name: Setup Node.js
20 | uses: actions/setup-node@v1
21 | with:
22 | node-version: '17'
23 |
24 | - name: Setup Cache
25 | uses: actions/cache@v2
26 | env:
27 | cache-name: cache-node-modules
28 | with:
29 | # npm cache files are stored in `~/.npm` on Linux/macOS
30 | path: ~/.npm
31 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
32 | restore-keys: |
33 | ${{ runner.os }}-build-${{ env.cache-name }}-
34 | ${{ runner.os }}-build-
35 | ${{ runner.os }}-
36 |
37 | - name: Setup Webpack & Install Dependences
38 | run: |
39 | git config --global user.email "64469437+Yuameshi@users.noreply.github.com"
40 | git config --global user.name "Yuameshi"
41 | npm install webpack-cli webpack -g
42 | npm install
43 |
44 | - name: Build
45 | run: npm run build
46 |
47 | - name: Deploy
48 | uses: peaceiris/actions-gh-pages@v3
49 | with:
50 | github_token: ${{ secrets.GH_TOKEN }}
51 | publish_dir: ./dist
52 |
53 | - name: Trigger APP CI Build
54 | run: |
55 | curl 'https://api.github.com/repos/Yuameshi/PhiCommunityAPP/dispatches' --request POST \
56 | -H 'Accept: application/vnd.github.v3+json' \
57 | -H 'Content-Type: application/json' \
58 | -H 'Authorization: Bearer ${{ secrets.GH_TOKEN }}' \
59 | --data-raw '{"event_type": "CI Build triggered by Repo:PhiCommunity"}'
60 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: 'codeQL'
13 |
14 | on:
15 | push:
16 | branches:
17 | - main
18 | - dev
19 | pull_request:
20 | # The branches below must be a subset of the branches above
21 | branches:
22 | - main
23 | - dev
24 | schedule:
25 | - cron: '40 12 * * 2'
26 |
27 | jobs:
28 | analyze:
29 | name: Analyze
30 | runs-on: ubuntu-latest
31 | permissions:
32 | actions: read
33 | contents: read
34 | security-events: write
35 |
36 | strategy:
37 | fail-fast: false
38 | matrix:
39 | language: ['javascript']
40 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
41 | # Learn more:
42 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
43 |
44 | steps:
45 | - name: Checkout repository
46 | uses: actions/checkout@v2
47 |
48 | # Initializes the CodeQL tools for scanning.
49 | - name: Initialize CodeQL
50 | uses: github/codeql-action/init@v1
51 | with:
52 | languages: ${{ matrix.language }}
53 | # If you wish to specify custom queries, you can do so here or in a config file.
54 | # By default, queries listed here will override any specified in a config file.
55 | # Prefix the list here with "+" to use these queries and those in the config file.
56 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
57 |
58 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
59 | # If this step fails, then you should remove it and run the build manually (see below)
60 | - name: Autobuild
61 | uses: github/codeql-action/autobuild@v1
62 |
63 | # ℹ️ Command-line programs to run using the OS shell.
64 | # 📚 https://git.io/JvXDl
65 |
66 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
67 | # and modify them (or add more) to build your code if your project
68 | # uses a compiled language
69 |
70 | #- run: |
71 | # make bootstrap
72 | # make release
73 |
74 | - name: Perform CodeQL Analysis
75 | uses: github/codeql-action/analyze@v1
76 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .idea
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "crlf",
3 | "embeddedLanguageFormatting": "auto",
4 | "htmlWhitespaceSensitivity": "css",
5 | "semi": true,
6 | "singleQuote": true,
7 | "tabWidth": 4,
8 | "useTabs": true,
9 | "printWidth": 256,
10 | "useEditorConfig": false,
11 | "jsxBracketSameLine": true
12 | }
13 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.tabSize": 4,
3 | "editor.insertSpaces": false,
4 | "editor.detectIndentation": true,
5 | "liveServer.settings.multiRootWorkspaceName": "PhiCommunity"
6 | }
7 |
--------------------------------------------------------------------------------
/AssetSources/audio/Drag.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/Drag.aup3
--------------------------------------------------------------------------------
/AssetSources/audio/Exit.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/Exit.aup3
--------------------------------------------------------------------------------
/AssetSources/audio/Flick.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/Flick.aup3
--------------------------------------------------------------------------------
/AssetSources/audio/LevelOver.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/LevelOver.aup3
--------------------------------------------------------------------------------
/AssetSources/audio/Pause.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/Pause.aup3
--------------------------------------------------------------------------------
/AssetSources/audio/Start.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/Start.aup3
--------------------------------------------------------------------------------
/AssetSources/audio/Tap.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/Tap.aup3
--------------------------------------------------------------------------------
/AssetSources/audio/calibrate.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/calibrate.aup3
--------------------------------------------------------------------------------
/AssetSources/audio/selectSongItem.aup3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/audio/selectSongItem.aup3
--------------------------------------------------------------------------------
/AssetSources/images/hitKeys.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/images/hitKeys.sketch
--------------------------------------------------------------------------------
/AssetSources/images/uiElement.sketch:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/AssetSources/images/uiElement.sketch
--------------------------------------------------------------------------------
/Readme.MD:
--------------------------------------------------------------------------------
1 | # 这是什么?
2 | 这是一款名为`PhiCommunity`的节奏游戏,它仿照`Phigros`制作。
3 |
4 | # APP已发布(测试版本)
5 |
6 | 请前往[Actions - PhiCommunityAPP](https://github.com/Yuameshi/PhiCommunityAPP/actions)的最新构建下载Artifact,此构建为`Debug`构建,仅用于测试。
7 |
8 | 您也可以前往[Releases - PhiCommunityAPP](https://github.com/Yuameshi/PhiCommunityAPP/releases)下载发行版本(如果有)。
9 |
10 | # 许可证
11 |
12 | 源代码(不包括多媒体资源,除非另有说明)在[AGPL-3.0](https://www.gnu.org/licenses/agpl-3.0.html)许可下分发
13 |
14 |
15 | 简要概述AGPL-3.0协议内容
16 |
17 | GNU Affero 通用公共许可证 v3.0
18 |
19 | 这种最强大的 Copyleft 许可的许可取决于提供许可作品和修改的完整源代码,其中包括在同一许可下使用许可作品的大型作品。 必须保留版权和许可声明。 贡献者明确授予专利权。 当修改版本用于通过网络提供服务时,必须提供修改版本的完整源代码。
20 |
21 | 您获得的权限:
22 | - 商业用途
23 | - 修改
24 | - 分发
25 | - 专利使用
26 | - 私人使用
27 |
28 | 您将被此许可证限制:
29 | - 责任
30 | - 保障
31 |
32 | 再创作所需的条件:
33 | - 包含许可和版权声明
34 | - 标明修改的内容
35 | - 同样保持开源
36 | - 作为网络服务使用视为分发
37 | - 使用相同的许可证(AGPL-3.0)
38 |
39 |
40 |
41 |
42 |
43 | 对于多媒体资源,我们保留著作权。
44 |
45 | >对于`多媒体资源`的定义
46 | >
47 | >包括但不限于拓展名包含 `ogg`、`mp3`、`aac`、`wav`、`jp(e)g`、`png`、`svg`、`sketch`、`zip`、`au3`、`aup3-shm`、`aup3-wal`、`flp` 字段的文件。
48 | >
49 | >包括但不限于文件头标识包含 `ogg`、`mp3`、`aac`、`wav`、`jp(e)g`、`png`、`svg`、`sketch`、`zip`、`au3`、`aup3-shm`、`aup3-wal`、`flp` 文件头标识特征的文件。
50 |
51 | # 如何贡献
52 | 如您所见,游戏还没有完成,具体体现在“谱面少”和“性能差”。
53 | ## 为`谱面`做贡献
54 |
55 | 如需提交谱面文件,请前往[此仓库](https://github.com/Yuameshi/PhiCommunity-Charts-Repo)。
56 |
57 | ## 贡献代码
58 |
59 | 如果您想提高该项目的代码质量,请执行上述类似操作:`Fork->Clone->EditCode->Commit->Upload->Open Pull Requests`。不要忘记写下您修改的内容和改进的内容!
60 |
61 | 注意,贡献代码请不要修改`缩进`,贡献前请进行格式化(使用Prettier,项目内已有Prettier设置)和查错(使用ESLint,项目内已经有ESLint配置文件)操作。
62 |
--------------------------------------------------------------------------------
/assets/audio/Exit.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/audio/Exit.mp3
--------------------------------------------------------------------------------
/assets/audio/Pause.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/audio/Pause.mp3
--------------------------------------------------------------------------------
/assets/audio/Start.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/audio/Start.mp3
--------------------------------------------------------------------------------
/assets/audio/selectSongItem.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/audio/selectSongItem.mp3
--------------------------------------------------------------------------------
/assets/css/css.css:
--------------------------------------------------------------------------------
1 | @import url(./fonts/fonts.css);
2 | div.backBtn {
3 | position: fixed;
4 | left: -15px;
5 | top: 5px;
6 | width: 100px;
7 | height: 50px;
8 | z-index: 1;
9 | cursor: pointer;
10 | }
11 | div.backBtn::before {
12 | content: '';
13 | position: absolute;
14 | left: -15px;
15 | top: 0;
16 | height: 100%;
17 | width: 100%;
18 | background: #000;
19 | z-index: 1;
20 | filter: drop-shadow(#fff 5px 0);
21 | }
22 | div.backBtn::after {
23 | content: '';
24 | position: absolute;
25 | left: 0;
26 | top: 0;
27 | height: 100%;
28 | width: 100%;
29 | background: url(../images/Back.svg) no-repeat center center;
30 | transform: scale(0.3);
31 | z-index: 5;
32 | }
33 | div.avatarBar {
34 | position: fixed;
35 | right: -6px;
36 | top: 0;
37 | margin: 10px 0;
38 | background-color: #000;
39 | transform: skew(-15deg);
40 | /* width: fit-content; */
41 | height: 50px;
42 | background: #000 no-repeat;
43 | display: flex;
44 | justify-content: space-between;
45 | align-items: center;
46 | transition: all 0.3s ease-in-out;
47 | z-index: 1;
48 | }
49 | div.avatarBar.expand {
50 | padding-left: 20px;
51 | }
52 | div.avatarBar.expand::before {
53 | content: attr(data-name);
54 | color: #fff;
55 | width: fit-content;
56 | font-size: 1.3rem;
57 | transform: skew(15deg);
58 | margin-right: 0.8em;
59 | }
60 | div.avatarBar::before {
61 | content: attr(data-name);
62 | width: 0;
63 | }
64 | div.avatarBar::after {
65 | content: attr(data-rks);
66 | width: 50px;
67 | text-align: center;
68 | font-size: 14px;
69 | transform: translateX(-10px);
70 | align-self: flex-end;
71 | color: #000;
72 | background: #fff;
73 | z-index: 10;
74 | }
75 | div.avatarBar > div.avatar {
76 | --avatar: url(assets/images/Avatar.svg);
77 | height: 100%;
78 | width: 100px;
79 | background: var(--avatar) no-repeat center center;
80 | background-size: cover;
81 | transform: skew(15deg);
82 | margin: 0;
83 | padding: 0;
84 | display: flex;
85 | justify-content: space-between;
86 | align-items: center;
87 | }
88 | div.avatarBar > div.avatar::before,
89 | div.avatarBar > div.avatar::after {
90 | content: '';
91 | height: 100%;
92 | width: 20px;
93 | display: block;
94 | background: #000;
95 | transform: skew(-15deg) translateX(-10px);
96 | margin: 0;
97 | padding: 0;
98 | }
99 | div.avatarBar > div.avatar::after {
100 | transform: skew(-15deg) translateX(10px);
101 | }
102 |
--------------------------------------------------------------------------------
/assets/css/fonts/Exo-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/css/fonts/Exo-Regular.woff
--------------------------------------------------------------------------------
/assets/css/fonts/Exo-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/css/fonts/Exo-Regular.woff2
--------------------------------------------------------------------------------
/assets/css/fonts/Saira.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/css/fonts/Saira.woff2
--------------------------------------------------------------------------------
/assets/css/fonts/SourceHanSans&SairaHybrid.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/css/fonts/SourceHanSans&SairaHybrid.woff
--------------------------------------------------------------------------------
/assets/css/fonts/SourceHanSans&SairaHybrid.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/css/fonts/SourceHanSans&SairaHybrid.woff2
--------------------------------------------------------------------------------
/assets/css/fonts/fonts.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: Phi;
3 | src: url(./Exo-Regular.woff2)format('woff2');
4 | src: url(./SourceHanSans&SairaHybrid.woff2)format('woff2');
5 | src: url(./Exo-Regular.woff)format('woff');
6 | src: url(./SourceHanSans&SairaHybrid.woff)format('woff');
7 | /* src: url(./Saira.woff2)format('woff2'); */
8 | }
9 | @font-face {
10 | font-family: Mina;
11 | src: url(./Exo-Regular.woff2)format('woff2');
12 | src: url(./SourceHanSans&SairaHybrid.woff2)format('woff2');
13 | src: url(./Exo-Regular.woff)format('woff');
14 | src: url(./SourceHanSans&SairaHybrid.woff)format('woff');
15 | /* src: url(./Saira.woff2)format('woff2'); */
16 | }
17 |
18 | * {
19 | font-family: Phi;
20 | }
21 |
22 | * {
23 | user-select: none;
24 | }
25 |
26 | input{
27 | user-select: unset;
28 | }
29 |
--------------------------------------------------------------------------------
/assets/images/A15A.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/assets/images/AppIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/images/AppIcon.png
--------------------------------------------------------------------------------
/assets/images/AppIcon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/assets/images/Avatar.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/assets/images/B15B.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/assets/images/Back.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/assets/images/C15C.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/assets/images/ElementSqare.Half.Size.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/images/ElementSqare.Half.Size.png
--------------------------------------------------------------------------------
/assets/images/ElementSqare.Half.Size.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/images/ElementSqare.Half.Size.webp
--------------------------------------------------------------------------------
/assets/images/ElementSqare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/images/ElementSqare.png
--------------------------------------------------------------------------------
/assets/images/ElementSqare.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/images/ElementSqare.webp
--------------------------------------------------------------------------------
/assets/images/F15F.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/assets/images/Restart.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/assets/images/Resume.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/assets/images/S15S.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/assets/images/Settings.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/assets/images/Sort.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/assets/images/Tick.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/assets/images/Title.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
--------------------------------------------------------------------------------
/assets/images/TrashBin.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/assets/images/V15FC.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/assets/images/V15V.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/assets/images/phi15phi.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/assets/images/showgirl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/images/showgirl.png
--------------------------------------------------------------------------------
/assets/images/showgirl_Half.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/assets/images/showgirl_Half.png
--------------------------------------------------------------------------------
/assets/tips.json:
--------------------------------------------------------------------------------
1 | [
2 | "哼~哼~哼!啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!",
3 | "手瞎几把晃两下,是个人都行",
4 | "这是什么游戏,节奏大师吗?",
5 | "这是节奏大师吗?",
6 | "人生在世,十有八九是寄了",
7 | "Well, you can get it on phicommunity.com.cn",
8 | "打屁股肉打出肌腱炎来怎么办?",
9 | "2022年,性年快乐!",
10 | "O̸̡̻̻͓̣-̢̨̼̗̩̕o̻̯͕͞͡o͓̯͙̞̝̱̞̕͡͝ó̷̯̥̲̞̺͖͍o͡͞ͅo̫͇̞͘͞o̷̩̬̩̬̲ͅͅo̻̥͔̠̖̩̕ǫ̷̺͉̖͔̖̟ơ̢̤̤o̫̺͙̭̖͙̰̹͙ ̻̹͈̼̣̺A̯̦͔̲̞̣̦̹̞̕A̬̘̯̳͝A̸͖̬͈͔̖̳̤͔͡A̱͚̤̞͙̪̘͇É̫̮̳-̡̛̛͍̬̘̞̘̲A̧͏̞̱͍̘͕̲̠͟-̷̩̞̱̣̼̺͘͜A͖̼̞-͓͇̫̩ͅͅI̷͎͖̝̲͖̰͟-҉͖͉̱̞̙̮͇A̸̡̡̞͕͈͓̙͔-̴̺̖͡U͏͏̲̗͈̮-̸̨͉͉̳̦̭̼̙̝̀ ̲̘͙̦͇̘͘͠J͎O͉͉̙̳͇͝-̠͍̩̖͡o̧̺̞̠̝o̥̳͎̰̮̯̕ͅo̸͖͕͚̠ơ̶̫̖͙̤͜ͅo҉̩͔̺̥̣̗̣̯̝o͏̤͔̳̲͚̟̻o̳͎͚̟̦͍̱͝ͅò̮̥͚̻̫͉͟o͕̕͞o̸̗͖̬͠͝o̕͟҉͔̘̪̜̲ó̼͈̫̥̤̪͟ ̱̻͟A͏̝̞͇̻Ą̺̺̠̫̫̱E̴̢̩̻͈͙-̴̢̡̠͎͚͕ͅͅO̭͕͚̳̭͍͍̕͜͢-͕̮̟̗̦͕A̯̲̲̬̱̟̕͞-̦͔̪̥̹͚A̼̦-̢̜̲͓͙̥̭̕͞U̥͉̻͢-̸̵͈̫̣̭̳͓U̝̪̩͕̗͍̮-̙̝̮̫̪̦̝̩̀͠A҉͚̯̣̯-̷̨̠̳̠͍͙̟̫̺ ̢҉̟̬͙̥͖̼É͓̰̗̝-̷̟̙̯̪͡e͏̭͉̘̘́e̞̬̹͍ͅe̩̺̻͉-̡̠̦͜͜ę̵͙̠͈̭͉͙̣̘e̵̼̬͖͟-̲͔e̡̱̖͖͙͈̼̲͎̯͢e̢̮e̥̬͇̤̫̘̙̮͟ ̨̦̤̭͖̕A̟̪̱̰͉̼̤͓͜ͅA̘̱͉͙̼̤͍̻͎͝Á̝͔̺̝̻͍̠A̴̩̫̩̳̙̙̞͓͘E̮͎̺̱̬͞ͅ-̧̜̟̯A͚̝̖̭̰̹̩͙-̱͜Ȩ̸͇̫͎ͅͅ-͔̘̳̥͙͎́̕͞I̥̤̣͢͢-͏͖É͍͈̻̦̟̕-̸̦̥̮A̴̸̖̳͎͡-̵̱͉̘͚̺̝͟ ͔̯̜͈̥͜J̯̜̖̖̙͇̳̱ͅƠ̻̲̝̪̲̖͠-͇̙̪̠̺͖̠̥̫o̱̥̟̯̘͓̥͠͞o͠͠҉͈̲͔̤͕o͎̤̗̼̕͝-͇̣̠̯͜ͅo̟̫̝ͅo̵̵̷̹̭̪-̸͉͖͔̗̠͔͔̕o̵̧̪̻̦͍̘͉͢ò̴̯̹-̟̲̰͓̖͎̻ͅǫ̴̛̻̟͙̙o̡̡҉̠̮̟̤̳̞̮̯ ҉̛̮̰̫̪̩̫̯͓͡E͡͏̰͓̹͎ͅE̶͖̙E̤͕͚̲̜͖Ȩ̴̭̰͜Ǫ̤͡-͖͎̮̬̜A͘͡͏̠̗̦͍̻͉-̵͍̳͞Ą͏̛̲̙̺̠͈͓̪̞A͔̟À̰̟̙̘̦͖̬̩-̯̗͚͜͞Á̷̭͚̦ͅA̴̸̻̬A̬̫̫A̯̪͇̣̮",
11 | "O-oooooooooo AAAAE-A-A-I-A-U- JO-oooooooooooo AAE-O-A-A-U-U-A- E-eee-ee-eee",
12 | "下载下来8倍慢速一个一个数着看,终于明白了音游到底是怎么判定的了——虽然没按不到那么多,但就假装你按了算了;虽然你没按歪了,但也假装你按到了。说白了这么快的速度就是唬人的玩意。",
13 | "我rks12.66,双星很难吗",
14 | "你ptt才12我都200了",
15 | "你rks才15.94我都114514了",
16 | "一个背谱就能玩的游戏种类🤣下至儿童音乐,改的眼花缭乱一点点就把自己捧成神了,说白了音游就是儿童音乐游戏罢了,何必给自己加那么多名堂,会点屏幕的低智儿童都玩的明白的rz游戏就不要在跳了好嘛😥",
17 | "音游确实是这样的,毕竟就是背板按QTE,完全没有游戏性,但就是有一部分傻子爱玩这种不需要动脑子的疯狂点点点游戏",
18 | "ptt200很高吗,你才12啊",
19 | "好像傻子一样 。玩个跳舞机还能耍耍帅, 这个游戏,拍来拍去的那些人还以为自己很酷 !",
20 | "我知道难,你0.5倍速慢慢打不行吗?(噢好像PhiCommunity调不了倍速现在)",
21 | "——忘了,1000w左右吧。——你ptt呢?——也是1000w左右吧",
22 | "样子的,他妈圈怎么变成这个音游",
23 | "打开,打开,一定要打开,再不打开那些音游手元,音游圈哪有美好的前程,哪有美好的未来。",
24 | "LET THE BASS KICK",
25 | "听说,在愚人节通关新手教程将成为最强新人?",
26 | "DABC ABCD ABCA DBAA BABC ABCD BAAA DBAB BCAB ABCA ABCA BBAC BABC ABCA BACA BBAA BCAB ABCA ABCA BBAC BABC ABCA BACA BBAA",
27 | "I just wanna jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓jump↑↓",
28 | "One, two, three,FIRE!",
29 | "Click~the~Circles",
30 | "大本钟下开快递,上面开摆下面寄",
31 | "提交Tips可以到GitHub仓库发PR噢!",
32 | "怎么,我是判定线,你不满意?",
33 | "你知道彩48有多难吗?我打了一百遍",
34 | "你充不充Q币?nnd!",
35 | "0xcc0xcc的锟斤拷,又0xcc又好吃(bushi",
36 | "有一种崩溃叫密码输入有误;有一种惊慌叫做账号异地登陆;有一种感情叫隐身对其可见;有一种误会叫人机离线;有一种失落叫没有访问权限;有一种感情叫站点访问失败;有一种无奈叫bug无法复现",
37 | "修完了一个bug,啪!好多bug又出现了",
38 | "iOS没关闭手势的话,噔 噔 咚",
39 | "Tips:Tips:Tips:Tips:NULL",
40 | "没有防滑垫,滑板上西天",
41 | "使用浏览器游玩PhiCommunity感觉很违和?PWA应用了解一下!",
42 | "你蓝了,你白了,你没了",
43 | "请记得让手休息哦",
44 | "不要用力敲打屏幕,屏幕裂开了就没得玩了",
45 | "竖起大拇指,自信音游人",
46 | "杜绝ky,从你我做起",
47 | "少做粪谱,从你我做起",
48 | "看Tips发现了什么?一段密文:5qyi6L+O5p2l5YiwUGhpQ29tbXVuaXR5",
49 | "这里没有tips",
50 | "异常的时钟哒哒跳,那首歌你φ了没?",
51 | "要是如果游戏变成了另外一种游戏的玩法会发生什么?",
52 | "如果加载慢,建议使用cloudflare节点哦!",
53 | "Never Gonna Give You Up~",
54 | "啊对对对",
55 | "tips找不到tips了",
56 | "饿——————————————————————————————啊——~~~~~~~~~~~~~~~~~~~~",
57 | "啊哈哈哈哈,谱子来咯!",
58 | "大伙都不敢打,有人说,你这谱下了粪",
59 | "王大队长,这不对吧?今天这是谁要陷害我?",
60 | "要是你这谱里没粪,你就自己打一编",
61 | "这打粪谱多是一件美事啊,又踩音踩不准又乱写配置。",
62 | "你们怎么还不打啊?快打啊",
63 | "亲爱的,你平板飞了",
64 | "亲爱的,你手机飞了",
65 | "亲爱的,你手台塌了",
66 | "啊啊啊啊啊啊啊啊啊!我没有点外卖!!!",
67 | "好活,但是有点烂,不过也是挺好的,可惜对我来说比较烂,只是太好了,没体现出烂的感觉,所以相对好来说,有点烂,总体来说还是好,好中不足就是烂了点。",
68 | "三倍☆Icecream!",
69 | "谁开的浴霸,给我快关了!!",
70 | "D☆A☆I☆S☆U☆K☆E",
71 | "丫口一一一一一一一一一一一一一一一",
72 | "YEE~",
73 | "粉键满天飞来飞去,那首歌你φ了没?",
74 | "要命就快点跑!",
75 | "STOMP YOUR FEET ↓FEET↓",
76 | "TIME IS OVER",
77 | "锤锤锤锤锤你的肺!",
78 | "what are they used to do",
79 | "e→mo↗tion→al↘ da↗ma→ge↘?",
80 | "⚡HIU↗~—~—~—BOOM⚡",
81 | "两只老虎爱跳舞,小兔子乖乖拔萝卜",
82 | "你爱我,我爱你,Tips冰城甜蜜蜜",
83 | "玛卡巴卡",
84 | " U R B B R G R O U N ",
85 | "toggle = true;",
86 | "adb shell am start -n me.PhiCommunity.device/.ui.DevicePlaysMusicGamesByItselfUI",
87 | "你一定在看tips吧!",
88 | "你的双手是给你一生服务的,而不是音游哦~注意休息哦",
89 | "Nyanyanyanyanyanyanya!",
90 | "啊~啊~啊~啊~啊~啊~啊~啊↑啊↓!我没有点外卖!!!",
91 | "遇到bug请去PhiCommunity Github项目中提交issue哦!记得提供完整信息以及bug复现方式!",
92 | "github.com 花了太长时间响应",
93 | "不要改成绩哦~造假行为这是不好的!",
94 | "?这视频怎么转载来转载去还转载回来了?",
95 | "您有一个好",
96 | "您有一个小姐",
97 | "您这个人实在是真的非常好",
98 | "😃👍👍👍👍👍👍👍",
99 | "🤔?????",
100 | "某玩家正用鼠标创造奇迹",
101 | "疑为什么突然谱面都没了?去“获取谱面”页面添加吧!",
102 | "cmd start AutoPlay.exe",
103 | "tips跑走了!",
104 | "玩音游要适度,记得到外面看看哦!",
105 | "What's up",
106 | "ϵͳ̡ʾ:LCHZHµׁ¦²»ף(ȧ¹ûģϫ֪µÀ´펳Ϫǩ,¿ɒԔڍøɏˑ˷բ¸ö´펳£ºphicommunity.com.cn)",
107 | "qwq? qwq!",
108 | "PhiCommunity是一款我的问题",
109 | "Let's! Get! Higher!!!",
110 | "上次的上次还是在上一次的上一次",
111 | "你知道吗?tips实际上夹带了一些密文哦:比如U2FsdGVkX1/gxmAqxNFcyqGCssGrKmyLUBKQnAN8/fBZyCmXAyAv4FFeXQrkl1XJ",
112 | "您才打了60w",
113 | "爆蓝!爆蓝!爆蓝!爆蓝!爆蓝!爆蓝!爆蓝!爆蓝!爆蓝!爆蓝!爆爆爆爆爆爆爆爆爆爆爆爆爆爆爆(bushi",
114 | "👴🚅📱",
115 | "禁止在tips中写生!",
116 | "得 ↓ 得 ↑ 得 ↓ 得 ↑ 得 ↑ 得 ↑ 得 ↓ 得 ↑ 得 ↓ 得 ↑ 得 ↓",
117 | "echo 46756e64696e6720666f722070726f6772616d206d61646520706f737369626c652062792d62792d62792d62792d6279",
118 | "echo 436f72706f72612d436f72706f72612d436f72706f72612d436f72706f72612d436f72706f72612d436f72706f7261",
119 | "echo 416e6e75616c2066696e616e63696c20737570706f722d416e6e75616c2066696e616e63696c20737570706f72",
120 | "iOS用户注意了,请开启引导式访问以获得最佳体验",
121 | "adb shell settings put system screen_brightness 1000",
122 | "adb shell am start -a android.intent.action.VIEW -d https://cf.phicommunity.com.cn/whilePlaying/?play=只有你知道&l=in",
123 | "adb shell am start -a android.intent.action.VIEW -d https://cf.phicommunity.com.cn/songSelect/",
124 | "爷┉是┉你┉椰┉叶~",
125 | "歌终有一收,禁止造假成绩!",
126 | "🙍▢▬▬▬*SAKUZYO BEAM*▬▬▬▶",
127 | "machine gun>◾ ◾◾ ◾ ◾◾◾ ◾◾",
128 | "creeper? awwwwwwwwwwwwww man!",
129 | "∀∀∀∀-∀∀∀-∀-OƎƎƎƎ oo-oo-oo-ooo-Oſ -∀-Ǝ-I-Ǝ-∀-Ǝ∀∀∀∀ ǝǝǝ-ǝǝ-ǝǝǝ-Ǝ -∀-∩-∩-∀-∀-O-Ǝ∀∀ oooooooooooo-Oſ -∩-∀-I-∀-∀-Ǝ∀∀∀∀ oooooooooo-O",
130 | "学音游,认识音游!",
131 | "你学会了吗?",
132 | "我滴啊呀啵啵,我滴哎呀啵啵~",
133 | "叮!IndiH🏠me",
134 | "叮!您miss了",
135 | "Makka Pakka Wakka Ikka Mikka Hum~~~~~~~~~~~",
136 | "可爱的羊巴鲁,刚好拿下硬度,要把你给劈了",
137 | "电量不足:仅剩10%(游戏暂停",
138 | "电量不足:仅剩20%(游戏暂停",
139 | "电量不足:仅剩5%(游戏暂停",
140 | "电量不足:仅剩3%(游戏暂停",
141 | "要是卡在这里不动了,可能是网络原因或者你的浏览器不支持哦~请去主页设备需求看一下哦~",
142 | "上一次打歌还是上一次!",
143 | "qwq?你还在吗?",
144 | "着急音乐人,你好极了!",
145 | "玩音游也可以用触摸笔玩哦~只要屏幕不裂开就好!",
146 | "请跟你的朋友准确描述我们的名字,我们真的不叫Phigros网页版,也和Phigros没有任何关系!",
147 | "我怀疑是先是瞎点的后配上的那些音符",
148 | "天晴了,雨停了,我觉得我又行了!",
149 | "您甚至还空敲",
150 | "您没有一点好",
151 | "猫玩音游玩得都比我好.jpg",
152 | "要是用钟打钟会怎么样呢?",
153 | "ı||ııı||ı|ıı||ı| 114514",
154 | "要是有更多歌曲就好了!拨打https://github.com/Yuameshi/PhiCommunity/issues来贡献谱面吧!",
155 | "↑ / ↓ ↑ ↓ ↓ ↑ / ↑ ↑ ↓ ↑ ↑ ↓ ↑ / ↑ ↑ ↓ / ↓ ↓ ↑ / ↓ ↑ ↓ ↑ ↓ ~ ↑ ~",
156 | "滚回功率,坐和放宽",
157 | "Windows 10 不是面向我们所有人,而是面向我们每一个人。",
158 | "滚回到以前的版本",
159 | "正在更新 Windows , 请勿™关闭电脑。",
160 | "震惊!一人在家里玩音游把板子擦的起火,平板原地裂开了!",
161 | "大家好,这里是tips看世界,让我们看看一外国小伙在………………",
162 | "玩音游也可以用触摸笔玩哦~只要屏幕不裂开就好!",
163 | "Microsoft Edge has stopped working",
164 | "Apple Safari has stopped working",
165 | "Opera has stopped working",
166 | "Mozilla Firefox has stopped working",
167 | "devenv.exe has stopped working",
168 | "Microsoft Visual Studio Code has stopped working",
169 | "Node.js has stopped working",
170 | "Uncaught SyntaxError: Unexpected token ?",
171 | "打5把CS:GO!",
172 | "Just do it!",
173 | "We are number one",
174 | "if {} else if {} else if {} else if {} else if {} else if {} else if {} else if {} else if {} else if {} else if {} else if {} ",
175 | "实际上PhiComnunity是可以用电脑玩的",
176 | "如果你使用电脑玩,请转头吧(doge",
177 | "你的电脑遇到了问题,需要重新启动。我们只收集一些错误信息,然后为你重新启动",
178 | "你的设备遇到问题,需要重启。你可以重新启动。",
179 | "Your PC ran into aa problem and needs to restart. We're just collecting some error info, and then we'll restart for you",
180 | "嘴 note note note note note ",
181 | "72786326464",
182 | "72783335464",
183 | "72788433374733678633778263464",
184 | "踩点就离谱",
185 | "你....你怎么推错分支了???仓库....仓库!崩坏了?我*******!",
186 | "小手一抖,推错分支,仓库爆炸,没有备份,原地爆炸",
187 | "小手一抖,写错代码,bug很多,没有备份,原地爆炸",
188 | "电脑卡顿,原地蓝屏,代码裂开,重新再写,推错分支,仓库爆炸,自己爆炸",
189 | "SyntaxError: Invalid regular expression flags",
190 | "SyntaxError: Unexpected end of input",
191 | "SyntaxError: Invalid or unexpected token",
192 | "SyntaxError: Identifier 'x' has already been declared",
193 | "RangeError: Maximum call stack size exceeded",
194 | "Uncaught URIError: URI malformed",
195 | "Uncaught SyntaxError: Unexpected token ILLEGAL",
196 | "WinRARInstall.rar",
197 | "???",
198 | "老师我鞋带疼…不对老师我肚子开了",
199 | "a b c d e f gun!",
200 | "Everybody do the flop",
201 | "好东西就要来了",
202 | "成。成。成。成。成。成。成。成。成。成。",
203 | "PhiCommunity爱发电网站速速拨打:https://afdian.net/@han-han"
204 | ]
205 |
--------------------------------------------------------------------------------
/config/webpack.common.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const CopyWebpackPlugin = require('copy-webpack-plugin');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
6 | const { GitRevisionPlugin } = require('git-revision-webpack-plugin');
7 |
8 | const path = require('path');
9 |
10 | const resolve = (...paths) => path.resolve(__dirname, '..', ...paths);
11 | const resolveSrc = (...paths) => path.resolve(__dirname, '../src', ...paths);
12 |
13 | const pagePlugins = [
14 | new HtmlWebpackPlugin({
15 | template: resolveSrc('index.html'),
16 | filename: 'index.html',
17 | chunks: ['index'],
18 | }),
19 | new HtmlWebpackPlugin({
20 | template: resolveSrc('template.html'),
21 | title: '关于我们 - PhiCommunity',
22 | filename: 'aboutUs/index.html',
23 | chunks: ['aboutUs'],
24 | }),
25 | ...[
26 | 'cacheControl',
27 | 'chapterSelect',
28 | 'LevelOver',
29 | 'loadingChartScreen',
30 | 'loadingScreen',
31 | 'settings',
32 | 'songSelect',
33 | 'tapToStart',
34 | 'whilePlaying',
35 | "getChart",
36 | ].map(
37 | (pagename) =>
38 | new HtmlWebpackPlugin({
39 | template: resolveSrc(pagename, 'index.html'),
40 | filename: `${pagename}/index.html`,
41 | chunks: [pagename],
42 | })
43 | ),
44 |
45 | //pages under 'settings/'
46 | ...['calibrate', 'statistic'].map(
47 | (pagename) =>
48 | new HtmlWebpackPlugin({
49 | template: resolveSrc('settings', pagename, 'index.html'),
50 | filename: `settings/${pagename}/index.html`,
51 | chunks: [pagename],
52 | })
53 | ),
54 | ];
55 |
56 | const gitRevisionPlugin = new GitRevisionPlugin({
57 | versionCommand: 'describe --always --tags',
58 | });
59 |
60 | module.exports = {
61 | entry: {
62 | index: resolveSrc('index.redirect.js'),
63 | aboutUs: resolveSrc('aboutUs/index.js'),
64 | cacheControl: resolveSrc('cacheControl/index.js'),
65 | chapterSelect: resolveSrc('chapterSelect/index.js'),
66 | LevelOver: resolveSrc('LevelOver/index.js'),
67 | loadingChartScreen: resolveSrc('loadingChartScreen/index.js'),
68 | loadingScreen: resolveSrc('loadingScreen/index.js'),
69 | settings: resolveSrc('settings/index.js'),
70 | songSelect: resolveSrc('songSelect/index.js'),
71 | tapToStart: resolveSrc('tapToStart/index.js'),
72 | calibrate: resolveSrc('settings/calibrate/index.js'),
73 | statistic: resolveSrc('settings/statistic/index.js'),
74 | getChart: resolveSrc('getChart/index.js'),
75 | whilePlaying: resolveSrc('whilePlaying/script.phi.community.core.js'),
76 | },
77 | output: {
78 | path: resolve('dist'),
79 | filename: 'js/[name].[chunkhash].js',
80 | assetModuleFilename: 'assets/[name].[contenthash:4][ext]',
81 | },
82 | resolve: {
83 | alias: {
84 | assets: resolve('assets'),
85 | public: resolve('public'),
86 | },
87 | },
88 | performance: {
89 | hints: 'warning',
90 | maxAssetSize: 12 * 1024 * 1024,
91 | maxEntrypointSize: 4 * 1024 * 1024,
92 | assetFilter: (assetFilename) =>
93 | assetFilename.match(/\.(css|js|mp3|wav|ogg|png|jpg|webp|svg)$/i),
94 | },
95 | plugins: [
96 | new CleanWebpackPlugin(),
97 | ...pagePlugins,
98 | gitRevisionPlugin,
99 | new webpack.DefinePlugin({
100 | $VERSION: JSON.stringify(gitRevisionPlugin.version()),
101 | }),
102 | new MiniCssExtractPlugin({
103 | filename: 'css/[name].[contenthash].css',
104 | }),
105 | new CopyWebpackPlugin({
106 | patterns: [resolve('public')],
107 | }),
108 | ],
109 | module: {
110 | rules: [
111 | {
112 | test: /\.(html)$/,
113 | use: {
114 | loader: 'html-loader',
115 | },
116 | },
117 | {
118 | test: /\.css$/i,
119 | use: [MiniCssExtractPlugin.loader, 'css-loader'],
120 | },
121 | {
122 | test: /\.(mp3|wav|ogg|png|jpg)$/,
123 | type: 'asset/resource',
124 | },
125 | {
126 | test: /\.(webp|svg)$/,
127 | use: [
128 | {
129 | loader: 'file-loader',
130 | options: {
131 | name: 'assets/[name].[contenthash:4].[ext]',
132 | esModule: false,
133 | },
134 | },
135 | /* {
136 | loader: 'url-loader',
137 | options: {
138 | name: '[path][name].[ext]',
139 | esModule: false,
140 | },
141 | }, */
142 | ],
143 | type: 'javascript/auto',
144 | },
145 | ],
146 | },
147 | };
148 |
--------------------------------------------------------------------------------
/config/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common');
3 |
4 | module.exports = merge(common, {
5 | mode: 'development',
6 | devtool: 'inline-source-map',
7 | devServer: {
8 | hot: true,
9 | },
10 | });
11 |
--------------------------------------------------------------------------------
/config/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common');
3 | const { InjectManifest } = require('workbox-webpack-plugin');
4 |
5 | const path = require('path');
6 |
7 | module.exports = (env) => {
8 | console.log(env);
9 | const chartsSource = env['charts-source'] || 'default';
10 |
11 | if (chartsSource) console.log('Deploy with charts source: ' + chartsSource);
12 |
13 | const loaders = [];
14 | if (chartsSource !== 'default')
15 | loaders.push({
16 | test: /\.js$/,
17 | loader: 'string-replace-loader',
18 | options: {
19 | search: /https:\/\/charts\.phicommunity\.com\.cn/gi,
20 | replace: chartsSource,
21 | },
22 | });
23 |
24 | return merge(common, {
25 | mode: 'production',
26 | plugins: [
27 | new InjectManifest({
28 | swSrc: path.resolve(__dirname, '../src', 'sw.js'),
29 | swDest: 'service-worker.js',
30 | exclude: [/service-worker\.js/, /sw\.js/],
31 | maximumFileSizeToCacheInBytes: 8 * 1024 * 1024,
32 | }),
33 | ],
34 | module: {
35 | rules: [...loaders],
36 | },
37 | });
38 | };
39 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "allowSyntheticDefaultImports": false,
5 | "baseUrl": "./",
6 | "paths": {
7 | "assets/*": ["assets/*"],
8 | "public/*": ["public/*"]
9 | }
10 | },
11 | "exclude": ["node_modules", "dist"],
12 | "include": ["src", "src/sw.js"]
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phi-community",
3 | "version": "1.0.0",
4 | "description": "A community driven project to make a game like phigros.",
5 | "author": "Yuameshi",
6 | "license": "GPL-3.0",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/Yuameshi/PhiCommunity.git"
10 | },
11 | "scripts": {
12 | "test": "echo \"Error: no test specified\" && exit 1",
13 | "start": "webpack serve --config ./config/webpack.dev.js --open",
14 | "watch": "webpack --config ./config/webpack.prod.js --watch",
15 | "build": "webpack --config ./config/webpack.prod.js",
16 | "build:cf": "npm run build -- --env charts-source=\"https://cf.charts.phicommunity.com.cn\"",
17 | "build:vercel": "npm run build -- --env charts-source=\"https://vercel.charts.phicommunity.com.cn\""
18 | },
19 | "bugs": {
20 | "url": "https://github.com/Yuameshi/PhiCommunity/issues"
21 | },
22 | "homepage": "https://github.com/Yuameshi/PhiCommunity#readme",
23 | "devDependencies": {
24 | "clean-webpack-plugin": "^4.0.0",
25 | "copy-webpack-plugin": "^10.2.4",
26 | "css-loader": "^6.6.0",
27 | "eslint": "^8.13.0",
28 | "file-loader": "^6.2.0",
29 | "git-revision-webpack-plugin": "^5.0.0",
30 | "html-loader": "^3.1.0",
31 | "html-webpack-plugin": "^5.5.0",
32 | "mini-css-extract-plugin": "^2.6.0",
33 | "string-replace-loader": "^3.1.0",
34 | "style-loader": "^3.3.1",
35 | "webpack": "^5.69.1",
36 | "webpack-cli": "^4.9.2",
37 | "webpack-dev-server": "^4.7.4",
38 | "webpack-merge": "^5.8.0",
39 | "workbox-webpack-plugin": "^6.5.1"
40 | },
41 | "dependencies": {
42 | "dom-element-factory": "^1.5.0",
43 | "oggmented": "^1.0.1",
44 | "stackblur-canvas": "^2.5.0",
45 | "workbox-core": "^6.5.1",
46 | "workbox-precaching": "^6.5.1",
47 | "workbox-routing": "^6.5.1",
48 | "workbox-strategies": "^6.5.1"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/public/404.css:
--------------------------------------------------------------------------------
1 | body,
2 | html {
3 | height: 100vh;
4 | width: 100vw;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | box-sizing: border-box;
9 | color: #fff;
10 | }
11 | button {
12 | outline: none;
13 | border: 1px #fff solid;
14 | background: none;
15 | color: #fff;
16 | padding: 1rem;
17 | margin: 5px 10px;
18 | font-size: 1.1rem;
19 | font-weight: bold;
20 | cursor: pointer;
21 | }
22 | body::before {
23 | content: '';
24 | display: block;
25 | height: 100%;
26 | width: 100%;
27 | position: fixed;
28 | left: 0;
29 | top: 0;
30 | z-index: -1;
31 | filter: contrast(0.6) brightness(0.6);
32 | background: #999 url(assets/images/ElementSqare.Half.Size.webp) center
33 | center no-repeat fixed;
34 | background-size: cover;
35 | transition: all 0.3s ease-in-out;
36 | }
37 | div.main {
38 | min-width: 320px;
39 | height: 90%;
40 | width: 80%;
41 | display: flex;
42 | justify-content: center;
43 | align-items: center;
44 | flex-direction: column;
45 | border-top: 2px #fff solid;
46 | border-bottom: 2px #fff solid;
47 | animation: stretch 1.5s ease;
48 | }
49 | @keyframes stretch {
50 | 0% {
51 | opacity: 0;
52 | height: 10px;
53 | overflow: hidden;
54 | }
55 | 50% {
56 | opacity: 1;
57 | height: 10px;
58 | overflow: hidden;
59 | }
60 | }
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | Error 404
11 |
12 |
13 |
14 |
15 |
Error 404
16 |
17 | 找不到指定的页面,点击
此处 回到主页
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/public/AppIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/public/AppIcon.png
--------------------------------------------------------------------------------
/public/CNAME:
--------------------------------------------------------------------------------
1 | phicommunity.com.cn
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/public/favicon.ico
--------------------------------------------------------------------------------
/public/manifest.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "background_color": "#ada5dc",
3 | "description": "A community driven project to make a game like phigros.",
4 | "display": "fullscreen",
5 | "icons": [
6 | {
7 | "src": "../AppIcon.png",
8 | "sizes": "192x192",
9 | "type": "image/png"
10 | }
11 | ],
12 | "name": "PhiCommunity",
13 | "short_name": "Phi",
14 | "start_url": "/tapToStart/index.html",
15 | "orientation": "landscape"
16 | }
17 |
--------------------------------------------------------------------------------
/src/LevelOver/LevelOver0.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/LevelOver/LevelOver0.ogg
--------------------------------------------------------------------------------
/src/LevelOver/LevelOver1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/LevelOver/LevelOver1.ogg
--------------------------------------------------------------------------------
/src/LevelOver/LevelOver2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/LevelOver/LevelOver2.ogg
--------------------------------------------------------------------------------
/src/LevelOver/LevelOver3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/LevelOver/LevelOver3.ogg
--------------------------------------------------------------------------------
/src/LevelOver/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | 结束 - PhiCommunity
11 |
12 |
13 |
14 |
17 |
25 |
26 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
47 |
51 |
52 |
609
53 |
0
54 |
0
55 |
0
56 |
60 |
61 |
62 |
63 |
66 | PhiCommunity By lchzh3473 & Yuameshi, sound by starN5150
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/src/aboutUs/AboutUs.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/aboutUs/AboutUs.mp3
--------------------------------------------------------------------------------
/src/aboutUs/Trigger.js:
--------------------------------------------------------------------------------
1 | import { createElement } from 'dom-element-factory';
2 | import Title_svg from '/assets/images/Title.svg';
3 |
4 | export const Trigger = (onTriggered) => {
5 | const element = createElement(
6 | 'div',
7 | {
8 | class: 'trigger',
9 | },
10 | [
11 | createElement('img', {
12 | src: Title_svg,
13 | alt: 'PhiCommunity',
14 | class: 'title',
15 | }),
16 | createElement(
17 | 'div',
18 | {
19 | class: 'tapToStart',
20 | },
21 | 'touch to start'
22 | ),
23 | ]
24 | );
25 |
26 | const onTrigger = () => {
27 | element.removeEventListener('click', onTrigger);
28 | element.classList.add('fadeout');
29 | setTimeout(() => {
30 | element.remove();
31 | onTriggered();
32 | }, 1000);
33 | };
34 |
35 | element.addEventListener('click', onTrigger);
36 |
37 | return { element };
38 | };
39 |
--------------------------------------------------------------------------------
/src/aboutUs/index.js:
--------------------------------------------------------------------------------
1 | import { createElement, br } from 'dom-element-factory';
2 | import './style.css';
3 | import AboutUs_mp3 from './AboutUs.mp3';
4 | import { Trigger } from './Trigger';
5 |
6 | const thanks = [
7 | `At First / 写在前面
8 | 由雨糸 (Yuameshi)
9 | 如你所见,PhiCommunity是一个仿照Phigros制作基于HTML5的游戏。
10 | 也感谢Pigeon Games创造出Phigros这一如此好玩的游戏。
11 | 顺便这里特别感谢lchzh3473的Phigros模拟器,没有它,这个项目消耗的时间可能要长数倍。
12 | PhiCommunity已经在GitHub开源,人人皆可贡献。
13 | 你可以在其中上传自己的谱面(粪谱就算了),改进代码,我十分欢迎这样做,大家共同进步。
14 |
15 | 祝你们在这里玩得愉快
16 |
17 | 具有较大贡献的社区人员 / Developers
18 | Yuameshi 开发者
19 | lchzh3473 开发者
20 | 熙晨 代码优化
21 | DrYeXiu 背景图片
22 | 万炯鸣 部分背景音乐
23 | 爱音乐de大神🎶 部分背景音乐思路提供
24 | 余音歆风 测试人员
25 | 守约 测试人员`,
26 | '感谢所有为PhiCommunity提供帮助的个人或团体',
27 | ['And', br(), 'You.', br()],
28 | ];
29 |
30 | const main = Main();
31 |
32 | const exitPrompt = ExitPrompt();
33 |
34 | const onTriggered = () => {
35 | const actx = new (window.AudioContext ||
36 | window.webkitAudioContext ||
37 | window.mozAudioContext ||
38 | window.msAudioContext)();
39 | const abortController = new AbortController();
40 | fetch(AboutUs_mp3, abortController.signal)
41 | .then((res) => res.arrayBuffer())
42 | .then((arrayBuffer) => {
43 | actx.decodeAudioData(arrayBuffer, function (buffer) {
44 | var source = actx.createBufferSource();
45 | source.buffer = buffer;
46 | source.loop = true;
47 | source.connect(actx.destination);
48 | source.start(0);
49 | });
50 | });
51 |
52 | main.scrollStart(() => {
53 | setTimeout(() => {
54 | actx == undefined ? abortController.abort() : actx.close();
55 | location.href = '../chapterSelect/index.html';
56 | }, 3000);
57 | });
58 |
59 | let exitCounter = 6;
60 | document.body.addEventListener('click', () => {
61 | exitCounter--;
62 | exitPrompt.prompt(exitCounter);
63 |
64 | if (exitCounter <= 0) {
65 | setTimeout(() => {
66 | actx == undefined ? abortController.abort() : actx.close();
67 | location.href = '../chapterSelect/index.html';
68 | }, 1000);
69 | }
70 | const bactToMinScreenTimeOut = setTimeout(() => {
71 | exitCounter = 6;
72 | /* setTimeout(() => {
73 | document.querySelector('div.clickToExitTag').innerText =
74 | '再点击' + window.clickToExitCounter + '次以跳过';
75 | }, 300); */
76 | exitPrompt.unVisible();
77 | clearTimeout(bactToMinScreenTimeOut);
78 | }, 5000);
79 | });
80 | };
81 |
82 | const trigger = Trigger(onTriggered);
83 |
84 | document.body.append(...[trigger, exitPrompt, main].map((c) => c.element));
85 |
86 | function ExitPrompt() {
87 | const element = createElement('div', {
88 | class: 'clickToExitTag',
89 | });
90 | return { element, prompt, unVisible };
91 |
92 | function prompt(num) {
93 | element.innerText = '再点击' + num + '次以跳过';
94 | element.style.opacity = '0.' + (10 - num);
95 | }
96 |
97 | function unVisible() {
98 | element.style.opacity = 0;
99 | }
100 | }
101 |
102 | function Main() {
103 | const element = createElement(
104 | 'div',
105 | {
106 | id: 'main',
107 | },
108 | [
109 | createElement(
110 | 'pre',
111 | {
112 | class: 'fromGameDirector',
113 | },
114 | thanks[0]
115 | ),
116 | createElement(
117 | 'div',
118 | {
119 | class: 'thanksAllHelpers',
120 | },
121 | thanks[1]
122 | ),
123 | createElement(
124 | 'div',
125 | {
126 | class: 'thankYou',
127 | },
128 | thanks[2]
129 | ),
130 | ]
131 | );
132 |
133 | return { element, scrollStart };
134 |
135 | // 自动滚动,通过持续修改CSS的Margin Top实现
136 | // window.addEventListener('DOMContentLoaded',()=>{
137 | // autoScroll();
138 | // });
139 | function scrollStart(onScrollEnd) {
140 | let topSize = window.innerHeight;
141 | element.style.setProperty('--topSize', topSize + 'px');
142 |
143 | const autoScrollInterval = setInterval(() => {
144 | if (element.offsetTop < window.innerHeight * -2.25 == true) {
145 | console.log('The END!');
146 | clearInterval(autoScrollInterval);
147 | onScrollEnd();
148 | }
149 | element.style.setProperty('--topSize', topSize + 'px');
150 | // document.body.style.marginTop=topSize+'px';
151 | topSize -= 0.5;
152 | }, 12); // 此数字改小同时topSize需要相应改小,改小后滑动更细腻,但是资源占用会增大
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/aboutUs/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 |
3 | html {
4 | background: #000;
5 | color: #fff;
6 | background-size: cover;
7 | box-sizing: border-box;
8 | height: 100%;
9 | overflow: hidden;
10 | }
11 | body {
12 | display: flex;
13 | height: 100%;
14 | margin: 0;
15 | padding: 0;
16 | width: 100%;
17 | justify-content: center;
18 | align-items: center;
19 | flex-direction: column;
20 | z-index: 0;
21 | }
22 |
23 | div.trigger {
24 | position: fixed;
25 | top: 0;
26 | left: 0;
27 | height: 100vh;
28 | width: 100vw;
29 | display: flex;
30 | flex-direction: column;
31 | justify-content: center;
32 | align-items: center;
33 | background: #000;
34 | /* z-index: 1; */
35 | }
36 | div.trigger > img.title {
37 | height: 20%;
38 | width: auto;
39 | max-width: 80%;
40 | object-fit: scale-down;
41 | }
42 | div.trigger.fadeout {
43 | animation: fadeOut 0.8s ease-in-out;
44 | opacity: 0;
45 | }
46 | @keyframes fadeOut {
47 | 0% {
48 | opacity: 1;
49 | }
50 | 100% {
51 | opacity: 0;
52 | }
53 | }
54 | div.trigger > div.tapToStart {
55 | margin: 30px;
56 | letter-spacing: 1.1em;
57 | animation: letterSpacingStretchAnim 6s linear infinite;
58 | }
59 | @keyframes letterSpacingStretchAnim {
60 | 0% {
61 | letter-spacing: 1.1em;
62 | }
63 | 50% {
64 | letter-spacing: 1.2em;
65 | }
66 | 100% {
67 | letter-spacing: 1.1em;
68 | }
69 | }
70 | div#main {
71 | height: 100%;
72 | width: 100%;
73 | display: flex;
74 | align-items: center;
75 | flex-direction: column;
76 | scrollbar-width: none;
77 | position: fixed;
78 | top: 0;
79 | left: 0;
80 | --topSize: 100vh;
81 | margin-top: var(--topSize);
82 | }
83 | ::-webkit-scrollbar {
84 | display: none;
85 | }
86 |
87 | pre {
88 | text-align: center;
89 | line-height: 1.5em;
90 | }
91 | pre.fromGameDirector {
92 | text-align: center;
93 | line-height: 2.2em;
94 | }
95 | div.clickToExitTag {
96 | position: fixed;
97 | top: 0;
98 | left: 0;
99 | margin: 15px;
100 | opacity: 0;
101 | transition: all 0.3s ease-in-out;
102 | }
103 | div.thanksAllHelpers,
104 | div.thankYou {
105 | margin-top: 20%;
106 | margin-bottom: 20%;
107 | }
108 |
--------------------------------------------------------------------------------
/src/cacheControl/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 缓存管理 - PhiCommunity
8 |
9 |
10 |
11 |
12 |
缓存管理
13 |
您可以在此处管理已缓存的谱面数据(包括音乐、曲绘、谱面和元信息)
14 |
删除所有缓存
15 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/cacheControl/index.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 |
3 | window.addEventListener('DOMContentLoaded', async () => {
4 | const cacheItems = [];
5 | const cacheList = document.getElementById('container');
6 | const cache = await caches.open('phi-runtime-v1');
7 | const keys = await cache.keys();
8 |
9 | document
10 | .querySelector('#delAllCache')
11 | .addEventListener('click', delAllCache);
12 |
13 | keys.forEach(function (request) {
14 | const item = CacheItem(cache, request);
15 | cacheList.append(item.element);
16 | cacheItems.push(item);
17 | });
18 |
19 | function delAllCache() {
20 | cacheItems.forEach((item) => {
21 | item.delCache().then(item.element.remove);
22 | });
23 | }
24 | });
25 |
26 | function CacheItem(cache, request) {
27 | const item = document.createElement('div');
28 | item.classList.add('item');
29 | item.setAttribute('title', request.url);
30 | item.setAttribute('data-url', request.url);
31 | item.setAttribute(
32 | 'data-file-name',
33 | new URL(request.url).pathname.split('/').slice(-1)[0] // url的最后一段(文件名)
34 | );
35 |
36 | const delBtn = document.createElement('button');
37 | delBtn.classList.add('deleteBtn');
38 | delBtn.innerText = '删除';
39 | delBtn.addEventListener('click', delCache);
40 | item.appendChild(delBtn);
41 |
42 | return { element: item, delCache };
43 |
44 | async function delCache() {
45 | await cache.delete(request);
46 | delBtn.removeEventListener('click', delCache);
47 | item.remove();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/cacheControl/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 | html {
3 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
4 | no-repeat fixed;
5 | background-size: cover;
6 | box-sizing: border-box;
7 | height: 100%;
8 | width: 100%;
9 | backdrop-filter: blur(15px) brightness(0.5) contrast(0.5);
10 | overflow: hidden;
11 | display: flex;
12 | justify-content: center;
13 | align-items: center;
14 | flex-direction: column;
15 | }
16 | html::before {
17 | content: '';
18 | position: absolute;
19 | height: 100vh;
20 | width: 100vw;
21 | top: 0;
22 | left: 0;
23 | bottom: 0;
24 | right: 0;
25 | background: url(assets/images/ElementSqare.Half.Size.webp) no-repeat center
26 | center fixed;
27 | background-size: cover;
28 | filter: blur(10px) brightness(0.5);
29 | z-index: -1;
30 | }
31 | div.main {
32 | height: 70vh;
33 | width: 85vw;
34 | display: flex;
35 | align-items: center;
36 | flex-direction: column;
37 | overflow-x: scroll;
38 | z-index: 0;
39 | background: #fff;
40 | min-width: 320px;
41 | min-height: 480px;
42 | }
43 | div#container {
44 | width: 80%;
45 | }
46 | div#container > div.item::before {
47 | order: 0;
48 | width: 10%;
49 | content: attr(data-file-name);
50 | margin-right: 0 50px;
51 | text-overflow: ellipsis;
52 | white-space: nowrap;
53 | overflow: hidden;
54 | }
55 | div#container > div.item::after {
56 | order: 1;
57 | width: calc(90% - 48px);
58 | display: flex;
59 | justify-content: flex-start;
60 | align-items: flex-start;
61 | content: attr(data-url);
62 | margin: 0 50px;
63 | }
64 | div#container > div.item > .deleteBtn {
65 | order: 2;
66 | width: 48px;
67 | text-align: center;
68 | outline: none;
69 | cursor: pointer;
70 | text-overflow: ellipsis;
71 | white-space: nowrap;
72 | overflow: hidden;
73 | }
74 | div#container > div.item {
75 | width: 100%;
76 | display: flex;
77 | flex-direction: row;
78 | justify-content: space-between;
79 | align-items: center;
80 | color: #000;
81 | text-decoration: none;
82 | margin: 5px 0;
83 | }
84 |
85 | @media screen and (max-width: 576px) {
86 | div#container > div.item::before {
87 | order: 0;
88 | width: calc(100% - 48px);
89 | content: attr(data-file-name);
90 | margin-right: 0 50px;
91 | text-overflow: ellipsis;
92 | white-space: nowrap;
93 | overflow: hidden;
94 | }
95 | div#container > div.item::after {
96 | display: none;
97 | }
98 | div#container > div.item > .deleteBtn {
99 | width: 48px;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/chapterSelect/ChapterSelect0.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/chapterSelect/ChapterSelect0.mp3
--------------------------------------------------------------------------------
/src/chapterSelect/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 主界面 - PhiCommunity
9 |
10 |
11 |
12 |
13 |
14 |
无/未获取
15 |
开始游玩
16 |
20 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/chapterSelect/index.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import ChapterSelect0_mp3 from './ChapterSelect0.mp3';
3 |
4 | window.addEventListener('DOMContentLoaded', () => {
5 | const abortController = new AbortController();
6 | const actx = new (window.AudioContext ||
7 | window.webkitAudioContext ||
8 | window.mozAudioContext ||
9 | window.msAudioContext)();
10 | fetch(ChapterSelect0_mp3, abortController.signal)
11 | .then((res) => res.arrayBuffer())
12 | .then((arrayBuffer) => {
13 | actx.decodeAudioData(arrayBuffer, function (buffer) {
14 | var source = actx.createBufferSource();
15 | source.buffer = buffer;
16 | source.loop = true;
17 | source.connect(actx.destination);
18 | source.start(0);
19 | });
20 | });
21 | fetch(
22 | 'https://api.github.com/repos/Yuameshi/PhiCommunity/commits?per_page=1'
23 | )
24 | .then((res) => res.json())
25 | .then((data) => {
26 | document.querySelector('div#recentUpdContent').innerText =
27 | data[0].commit.message;
28 | });
29 | document
30 | .querySelector('div#startToPlayBtn')
31 | .addEventListener('click', () => {
32 | actx == undefined
33 | ? abortController.abort()
34 | : actx.close();
35 | location.href = '../songSelect/index.html';
36 | });
37 | document.querySelector('div#getChart').addEventListener('click', () => {
38 | actx == undefined
39 | ? abortController.abort()
40 | : actx.close();
41 | location.href = '../getChart/index.html';
42 | });
43 | document.querySelector('div#cacheControl').addEventListener('click', () => {
44 | actx == undefined
45 | ? abortController.abort()
46 | : actx.close();
47 | location.href = '../cacheControl/index.html';
48 | });
49 | document.querySelector('div#settingBtn').addEventListener('click', () => {
50 | actx == undefined
51 | ? abortController.abort()
52 | : actx.close();
53 | location.href = '../settings/index.html';
54 | });
55 | document
56 | .querySelector('div#uploadChartsBtn')
57 | .addEventListener('click', () => {
58 | actx == undefined
59 | ? abortController.abort()
60 | : actx.close();
61 | location.href =
62 | 'https://github.com/Yuameshi/PhiCommunity-Charts-Repo';
63 | });
64 | const body = document.getElementById('body');
65 | if (window.DeviceOrientationEvent) {
66 | console.log(
67 | 'DeviceOrientationEvent detected, attaching event listener.'
68 | );
69 | window.addEventListener(
70 | 'deviceorientation',
71 | (event) => {
72 | const { gamma, beta } = event;
73 | body.style.setProperty('--gamma', gamma);
74 | body.style.setProperty('--beta', beta);
75 | // console.log(gamma, beta);
76 | },
77 | true
78 | );
79 | }
80 | });
81 |
--------------------------------------------------------------------------------
/src/chapterSelect/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 | html {
3 | background: url(assets/images/ElementSqare.Half.Size.webp) no-repeat
4 | center center fixed;
5 | background-size: cover;
6 | }
7 | html,
8 | body {
9 | /* display: flex;
10 | align-items: center; */
11 | height: 100%;
12 | width: 100%;
13 | box-sizing: border-box;
14 | overflow: hidden;
15 | /* transition: all 0.5s ease-out; */
16 | }
17 | #body {
18 | --gamma: 0;
19 | --translate-x: calc(-2px * var(--gamma) / 180 * 30);
20 | transform: translateX(var(--translate-x));
21 | }
22 | div.blackBoard {
23 | width: 100%;
24 | height: 80%;
25 | background: url(assets/images/showgirl_Half.png) no-repeat bottom left;
26 | background-size: contain;
27 | position: fixed;
28 | transition: all 0.3s ease;
29 | bottom: 0;
30 | left: 10%;
31 | z-index: 0;
32 | }
33 | div.mainActivityArea {
34 | height: 80%;
35 | width: 40%;
36 | position: fixed;
37 | right: 7.5%;
38 | bottom: 10%;
39 | margin: 0;
40 | padding: 0;
41 | display: flex;
42 | transform: perspective(30em) rotateY(-10deg) scale(0.9);
43 | flex-direction: column;
44 | justify-content: center;
45 | align-items: center;
46 | z-index: 1;
47 | }
48 |
49 | div#recentUpdContent{
50 | width: 100%;
51 | display: flex;
52 | justify-content: flex-start;
53 | align-items: center;
54 | font-size: 18px;
55 | padding: 20px 0;
56 | margin-bottom: 20px;
57 | color: #fff;
58 | text-overflow: ellipsis;
59 | overflow: hidden;
60 | white-space: nowrap;
61 | background: rgba(0,0,0,0.6);
62 | }
63 | div#recentUpdContent::before{
64 | content: '\3000近期更新内容:\3000';
65 | }
66 |
67 | div#startToPlayBtn{
68 | width: 100%;
69 | height: 60%;
70 | min-height: 100px;
71 | background-size: contain;
72 | font-size: 3rem;
73 | display: flex;
74 | justify-content: center;
75 | align-items: center;
76 | margin-bottom: 5% !important;
77 | overflow: hidden;
78 | }
79 | div#startToPlayBtn,
80 | div#cacheControl,
81 | div.buttomLayer > div {
82 | cursor: pointer;
83 | color: #fff;
84 | white-space: nowrap;
85 | overflow: hidden;
86 | text-overflow: ellipsis;
87 | background: linear-gradient(to right, #0173ffd7, #588de4d7);
88 | backdrop-filter: blur(10px);
89 | box-shadow: 0 0 10px 0 #0173fff7;
90 | }
91 |
92 | div.buttomLayer {
93 | display: flex;
94 | flex-direction: row;
95 | justify-content: space-between;
96 | align-items: center;
97 | width: 100%;
98 | height: 35%;
99 | padding: 15px;
100 | }
101 | div.buttomLayer > div {
102 | height: 100%;
103 | width: 45%;
104 | font-size: 2rem;
105 | overflow: hidden;
106 | display: flex;
107 | justify-content: center;
108 | align-items: center;
109 | }
110 | @media screen and (max-width: 576px) {
111 | body {
112 | display: flex;
113 | justify-content: center;
114 | align-items: center;
115 | margin: 0;
116 | padding: 0;
117 | }
118 | div.blackBoard {
119 | display: none;
120 | }
121 | div.mainActivityArea {
122 | width: 95%;
123 | margin: 0;
124 | padding: 0;
125 | position: unset;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | export const gameLevels = {
2 | ez: 0,
3 | hd: 1,
4 | in: 2,
5 | at: 3,
6 | };
7 |
--------------------------------------------------------------------------------
/src/getChart/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 获取谱面 - PhiCommunity
10 |
11 |
12 |
13 |
16 |
17 |
18 | 获取谱面
19 |
20 |
21 |
22 |
23 |
24 |
25 |
31 |
添加到我的谱面仓库
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/getChart/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 |
3 | html {
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: center;
7 | align-items: center;
8 | margin: 50px 0;
9 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
10 | no-repeat fixed;
11 | background-size: cover !important;
12 | backdrop-filter: blur(15px) brightness(0.8) contrast(0.8);
13 | }
14 | html::before {
15 | content: '';
16 | position: fixed;
17 | top: 0;
18 | left: 0;
19 | right: 0;
20 | bottom: 0;
21 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
22 | no-repeat fixed;
23 | background-size: cover;
24 | filter: blur(15px) brightness(0.8) contrast(0.8);
25 | z-index: -1;
26 | }
27 |
28 | div.title {
29 | font-size: 48px;
30 | font-weight: 500;
31 | text-align: center;
32 | margin-bottom: 50px;
33 | }
34 |
35 | div.chartListContainer {
36 | width: 90vw;
37 | height: auto;
38 | display: flex;
39 | flex-wrap: wrap;
40 | justify-content: center;
41 | align-items: center;
42 | }
43 |
44 | div.chartListContainer > div.item {
45 | width: 300px;
46 | height: 75px;
47 | margin: 15px;
48 | padding: 20px;
49 | --color: black;
50 | color: var(--color);
51 | display: flex;
52 | flex-direction: column;
53 | justify-content: flex-end;
54 | align-items: flex-start;
55 | border-radius: 5px;
56 | box-shadow: 0px 0px 5px #000;
57 | border: 1px #000 solid;
58 | overflow: hidden;
59 | --background: url(assets/images/ElementSqare.Half.Size.png);
60 | background: var(--background) center center no-repeat;
61 | background-size: cover;
62 | text-shadow: 0px 0px 5px #fff;
63 | cursor: pointer;
64 | }
65 |
66 | div.chartListContainer > div.item::before {
67 | content: attr(data-name);
68 | }
69 | div.chartListContainer > div.item::after {
70 | content: attr(data-artist);
71 | }
72 |
73 | div.expandView {
74 | display: none;
75 | }
76 | div.expandView.show {
77 | display: block;
78 | --expandViewColor: black;
79 | --expandViewBG: url(assets/images/ElementSqare.Half.Size.webp);
80 | color: var(--expandViewColor);
81 | text-shadow: 0px 0px 5px #fff;
82 | position: fixed;
83 | top: 0;
84 | left: 0;
85 | bottom: 0;
86 | right: 0;
87 | z-index: 10;
88 | animation: expandViewSlideIn 0.5s ease-in-out;
89 | }
90 | @keyframes expandViewSlideIn {
91 | 0% {
92 | overflow: 0;
93 | transform: translateX(-100%);
94 | }
95 | 50% {
96 | opacity: 1;
97 | }
98 | 100% {
99 | transform: translateX(0%);
100 | }
101 | }
102 | div.expandView.slideOut {
103 | animation: expandViewSlideOut 0.5s ease-in-out;
104 | }
105 | @keyframes expandViewSlideOut {
106 | 0% {
107 | transform: translateX(0%);
108 | }
109 | 50% {
110 | opacity: 1;
111 | }
112 | 100% {
113 | overflow: 0;
114 | transform: translateX(-100%);
115 | }
116 | }
117 | div.infoFrame {
118 | height: 100vh;
119 | width: 35vw;
120 | padding-left: 35px;
121 | position: absolute;
122 | top: 0;
123 | left: 0;
124 | bottom: 0;
125 | /* background: var(--expandViewBG) center center no-repeat fixed; */
126 | background-size: cover;
127 | display: flex;
128 | flex-direction: column;
129 | justify-content: flex-end;
130 | align-items: flex-start;
131 | }
132 | div.infoFrame::before {
133 | content: '';
134 | height: 100%;
135 | width: 100%;
136 | position: absolute;
137 | top: 0;
138 | left: 0;
139 | right: 0;
140 | bottom: 0;
141 | background: var(--expandViewBG) center center no-repeat fixed;
142 | background-size: cover;
143 | background-position: center;
144 | filter: blur(15px) brightness(0.8) contrast(0.8);
145 | z-index: -1;
146 | }
147 | div.infoFrame > div.songName,
148 | div.infoFrame > div.artist,
149 | div.infoFrame > div.chartDesigner {
150 | width: 100%;
151 | text-overflow: ellipsis;
152 | white-space: nowrap;
153 | overflow: hidden;
154 | }
155 | div.infoFrame > div.songName {
156 | font-size: 48px;
157 | font-weight: 500;
158 | }
159 | div.infoFrame > div.artist,
160 | div.infoFrame > div.chartDesigner {
161 | height: 24px;
162 | }
163 | div.infoFrame > div.artist::before {
164 | content: 'Music composed by:\3000';
165 | }
166 |
167 | div.infoFrame > div.chartDesigner::before {
168 | content: 'Chart design by\3000\3000:\3000';
169 | }
170 | div.levels {
171 | width: 100%;
172 | margin-left: -10px;
173 | margin-top: 25px;
174 | }
175 | div.levels > div.levelItem {
176 | max-width: 300px;
177 | height: 75px;
178 | border-radius: 10px;
179 | margin: 10px 0;
180 | font-size: 24px;
181 | display: flex;
182 | justify-content: space-between;
183 | align-items: center;
184 | padding: 0 20px;
185 | color: #fff;
186 | }
187 | div.levels > div.levelItem.hidden {
188 | display: none;
189 | }
190 | div.levels > div.levelItem::after {
191 | content: attr(data-level);
192 | }
193 | div.levels > div.levelItem.ez {
194 | background-color: #51af44;
195 | }
196 | div.levels > div.levelItem.ez::before {
197 | content: 'Easy';
198 | }
199 | div.levels > div.levelItem.hd {
200 | background-color: #3173b3;
201 | }
202 | div.levels > div.levelItem.hd::before {
203 | content: 'Hard';
204 | }
205 | div.levels > div.levelItem.in {
206 | background-color: #be2d23;
207 | }
208 | div.levels > div.levelItem.in::before {
209 | content: 'Insane';
210 | }
211 | div.levels > div.levelItem.at {
212 | background-color: #3a3637;
213 | }
214 | div.levels > div.levelItem.at::before {
215 | content: 'Another';
216 | }
217 | button.addToChartListBtn {
218 | height: 50px;
219 | width: 200px;
220 | align-self: flex-start;
221 | font-size: 16px;
222 | outline: none;
223 | color: var(--expandViewColor);
224 | border: 2px var(--expandViewColor) solid;
225 | background: transparent;
226 | cursor: pointer;
227 | display: flex;
228 | flex-direction: row;
229 | justify-content: center;
230 | align-items: center;
231 | margin-bottom: 35px;
232 | }
233 |
234 | .loadingEmbedFrame {
235 | position: fixed;
236 | height: 100%;
237 | width: 100%;
238 | left: 0;
239 | top: 0;
240 | object-fit: cover;
241 | z-index: 999999999;
242 | background: #999;
243 | }
244 |
245 | @media screen and (max-width: 576px) {
246 | div.infoFrame {
247 | top: auto;
248 | bottom: 0;
249 | width: 100vw;
250 | height: 90vh;
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | Redirecting...
12 |
13 |
14 |
15 |
16 |
准备重定向
17 | 在开始前,请先调整好设备音量,下方是一段测试音频,点击左侧的三角形以开始播放。
18 |
19 |
20 | 顺带一提,由于本项目变更较为频繁,所以可能在某些时候可能需要您清除浏览器缓存来修复一些错误。
21 |
22 | 开始游玩
23 | 添加到主屏幕
24 | 前往位于GitHub Pages的节点
25 | 前往位于Vercel的节点
26 | 前往位于CloudFlare的节点
27 |
28 | 查看GitHub仓库
29 | 查看变更记录
30 | 设备要求
31 | 提提建议
32 |
33 |
34 |
50 |
84 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/src/index.redirect.js:
--------------------------------------------------------------------------------
1 | import './style.redirect.css';
2 | window.addEventListener('DOMContentLoaded', () => {
3 | fetch('https://api.github.com/repos/Yuameshi/PhiCommunity/commits')
4 | .then((res) => res.json())
5 | .then((data) => {
6 | const changeLogFrame = document.querySelector(
7 | 'div#changelogContainer'
8 | );
9 | data.forEach(({ commit, html_url, sha }) => {
10 | const item = document.createElement('a');
11 | item.classList.add('item');
12 | item.href = html_url;
13 | item.setAttribute('data-sha', sha.slice(0, 7));
14 | item.innerText = commit.message;
15 | changeLogFrame.appendChild(item);
16 | });
17 | });
18 | const addBtn = document.querySelector('#installPWA');
19 | addBtn.style.display = 'none';
20 | window.addEventListener('beforeinstallprompt', (e) => {
21 | e.preventDefault();
22 | addBtn.style.display = 'unset';
23 | addBtn.addEventListener('click', ({ prompt, userChoice }) => {
24 | prompt();
25 | userChoice.then(({ outcome }) => {
26 | if (outcome === 'accepted') {
27 | console.log('准备添加到主屏幕');
28 | } else {
29 | console.log('用户拒绝了添加到主屏幕');
30 | }
31 | });
32 | });
33 | });
34 |
35 | document.querySelector('button#go').addEventListener('click', () => {
36 | location.href = './tapToStart/index.html';
37 | });
38 | document
39 | .querySelector('button#gotoCFPages')
40 | .addEventListener('click', () => {
41 | location.href = 'https://cf.phicommunity.com.cn';
42 | });
43 | document
44 | .querySelector('button#gotoVercel')
45 | .addEventListener('click', () => {
46 | location.href = 'https://vercel.phicommunity.com.cn';
47 | });
48 | document
49 | .querySelector('button#gotoGHPages')
50 | .addEventListener('click', () => {
51 | location.href = 'https://phicommunity.com.cn';
52 | });
53 | if (location.href.match('cf')) {
54 | document.querySelector('button#gotoCFPages').style.display = 'none';
55 | } else if (location.href.match('vercel')) {
56 | document.querySelector('button#gotoVercel').style.display = 'none';
57 | } else {
58 | document.querySelector('button#gotoGHPages').style.display = 'none';
59 | }
60 | document.querySelector('button#ghRepo').addEventListener('click', () => {
61 | window.open('https://github.com/Yuameshi/PhiCommunity');
62 | });
63 | document.querySelector('button#deviceReq').addEventListener('click', () => {
64 | document
65 | .querySelector('div#devRequirementPopupOverlay')
66 | .classList.add('show');
67 | });
68 | document
69 | .querySelector('div#devRequirementPopupOverlay')
70 | .addEventListener('click', (e) => {
71 | if (e.target !== document.querySelector('#devReq')) {
72 | document
73 | .querySelector('div#devRequirementPopupOverlay')
74 | .classList.remove('show');
75 | }
76 | });
77 | document.querySelector('button#changeLog').addEventListener('click', () => {
78 | document
79 | .querySelector('div#changeLogContainerPopupOverlay')
80 | .classList.add('show');
81 | });
82 | document
83 | .querySelector('div#changeLogContainerPopupOverlay')
84 | .addEventListener('click', (e) => {
85 | if (e.target !== document.querySelector('#changelogContainer')) {
86 | document
87 | .querySelector('div#changeLogContainerPopupOverlay')
88 | .classList.remove('show');
89 | }
90 | });
91 | document.querySelector('button#ContactUs').addEventListener('click', () => {
92 | document
93 | .querySelector('div#ContactUsPopupOverlay')
94 | .classList.add('show');
95 | });
96 | document
97 | .querySelector('div#ContactUsPopupOverlay')
98 | .addEventListener('click', (e) => {
99 | if (e.target !== document.querySelector('#ContactUs')) {
100 | document
101 | .querySelector('div#ContactUsPopupOverlay')
102 | .classList.remove('show');
103 | }
104 | });
105 | });
106 |
107 | /* if ('serviceWorker' in navigator) {
108 | navigator.serviceWorker.register('sw.js').then(function () {
109 | console.log('Service Worker Registered');
110 | });
111 | } */
112 | if ('serviceWorker' in navigator) {
113 | window.addEventListener('load', () => {
114 | navigator.serviceWorker
115 | .register('/service-worker.js')
116 | .then((registration) => {
117 | console.log('SW registered: ', registration);
118 | })
119 | .catch((registrationError) => {
120 | console.log('SW registration failed: ', registrationError);
121 | });
122 | });
123 | }
124 |
--------------------------------------------------------------------------------
/src/loadingChartScreen/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | 正在加载 - PhiCommunity
11 |
12 |
13 |
14 |
15 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
Loading...
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/loadingChartScreen/index.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import tips from 'assets/tips.json';
3 |
4 | window.addEventListener('DOMContentLoaded', () => {
5 | const rndNum = parseInt(Math.random() * (tips.length + 1), 10);
6 | const tip = tips[rndNum];
7 | console.log(tip);
8 | document.querySelector('#tipConteiner').innerText = tip;
9 |
10 | const urlParams = new URL(location.href).search;
11 | const parsedURLParams = new URLSearchParams(urlParams);
12 | const chart = parsedURLParams.get('c');
13 | const level = parsedURLParams.get('l').toLowerCase();
14 | fetch(
15 | encodeURI('https://charts.phicommunity.com.cn/' + chart + '/meta.json')
16 | )
17 | .then((response) => response.json())
18 | .then((songInfoObj) => {
19 | document.querySelector('#songNameElem').innerText =
20 | songInfoObj.name;
21 | document.querySelector('#artistElem').innerText =
22 | songInfoObj.artist;
23 | if (songInfoObj.chartDesigner != undefined) {
24 | document.querySelector('#chartDesignerElem').innerText =
25 | songInfoObj.chartDesigner;
26 | } else {
27 | document.querySelector('#chartDesignerElem').innerText =
28 | songInfoObj[level + 'ChartDesigner'];
29 | }
30 | document.querySelector('#illustratorElem').innerText =
31 | songInfoObj.illustrator;
32 | document
33 | .querySelector('#songImgElem')
34 | .setAttribute(
35 | 'src',
36 | encodeURI(
37 | 'https://charts.phicommunity.com.cn/' +
38 | chart +
39 | '/' +
40 | songInfoObj.illustration
41 | )
42 | );
43 | document.body.setAttribute(
44 | 'style',
45 | '--background: url(' +
46 | encodeURI(
47 | 'https://charts.phicommunity.com.cn/' +
48 | chart +
49 | '/' +
50 | songInfoObj.illustration
51 | ) +
52 | ');'
53 | );
54 | document
55 | .querySelector('#levelInfoElem')
56 | .setAttribute(
57 | 'data-level',
58 | Math.floor(songInfoObj[level + 'Ranking'] || 0)
59 | );
60 | document.querySelector('#levelInfoElem').classList.add(level);
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/src/loadingChartScreen/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 | html,
3 | body {
4 | box-sizing: border-box;
5 | height: 100%;
6 | width: 100%;
7 | margin: 0;
8 | padding: 0;
9 | }
10 | body {
11 | background: var(--background) center center no-repeat fixed;
12 | background-size: cover !important;
13 | backdrop-filter: blur(100px);
14 | display: flex;
15 | justify-content: center;
16 | align-items: center;
17 | color: #fff;
18 | }
19 | body::before {
20 | content: '';
21 | position: fixed;
22 | top: 0;
23 | left: 0;
24 | right: 0;
25 | bottom: 0;
26 | background: var(--background) center center no-repeat fixed;
27 | background-size: cover;
28 | filter: blur(15px);
29 | z-index: -1;
30 | }
31 | div.tip::before {
32 | content: 'Tip: ';
33 | }
34 | div.tip {
35 | color: #fff;
36 | position: fixed;
37 | left: 2%;
38 | bottom: 5%;
39 | max-width: 85%;
40 | text-overflow: ellipsis;
41 | overflow: hidden;
42 | white-space: nowrap;
43 | }
44 | div.loadingBar {
45 | position: fixed;
46 | right: 2%;
47 | bottom: 5%;
48 | overflow: hidden;
49 | }
50 | div.loadingBarTxT {
51 | color: #000;
52 | z-index: 999;
53 | animation: loadingBarTXTAnim 1s ease-in-out infinite;
54 | }
55 | div.loadingBarBG {
56 | position: absolute;
57 | left: 0;
58 | top: 0;
59 | height: 100%;
60 | width: 100%;
61 | z-index: -1;
62 | background-color: #fff;
63 | animation: loadingBarBGAnim 1s ease-in-out infinite;
64 | }
65 | @keyframes loadingBarBGAnim {
66 | 0% {
67 | left: -100%;
68 | }
69 | 100% {
70 | left: 200%;
71 | }
72 | }
73 | @keyframes loadingBarTXTAnim {
74 | 0% {
75 | color: #fff;
76 | }
77 | 50% {
78 | color: #000;
79 | }
80 | 100% {
81 | color: #fff;
82 | }
83 | }
84 | div.mainContent {
85 | height: 70%;
86 | width: 100%;
87 | display: flex;
88 | flex-direction: row;
89 | justify-content: center;
90 | align-items: center;
91 | overflow: hidden;
92 | padding: 0 10%;
93 | /* border: 1px #000 solid; */
94 | }
95 | div.textInfo {
96 | transform: skew(-15deg);
97 | }
98 | div.textInfo > * {
99 | transform: skew(15deg);
100 | }
101 | div.songInfoFrame {
102 | display: flex;
103 | height: 75px;
104 | width: 350px;
105 | flex-direction: row;
106 | align-items: center;
107 | justify-content: space-between;
108 | transform: skew(0deg);
109 | background-color: rgba(0, 0, 0, 0.8);
110 | padding: 10px 10px 10px 20px;
111 | margin: 25px;
112 | }
113 | div.songInfoFrame > * {
114 | text-overflow: ellipsis;
115 | white-space: nowrap;
116 | overflow: hidden;
117 | transform: skew(15deg);
118 | }
119 | div.songName {
120 | text-overflow: ellipsis;
121 | white-space: nowrap;
122 | overflow: hidden;
123 | font-size: 32px;
124 | }
125 | div.level::before {
126 | content: attr(data-level);
127 | display: block;
128 | color: #000;
129 | font-size: 42px;
130 | transform: skew(15deg);
131 | }
132 | div.level {
133 | height: calc(100% + 35px);
134 | width: 90px;
135 | min-width: 90px;
136 | background-color: #fff;
137 | transform: skew(0deg);
138 | margin: 0;
139 | padding: 0;
140 | display: flex;
141 | flex-direction: column;
142 | justify-content: center;
143 | align-items: center;
144 | }
145 | div.level::after {
146 | display: block;
147 | color: #000;
148 | transform: skew(15deg);
149 | }
150 | div.level.ez::after {
151 | content: 'EZ';
152 | }
153 | div.level.hd::after {
154 | content: 'HD';
155 | }
156 | div.level.in::after {
157 | content: 'IN';
158 | }
159 | div.level.at::after {
160 | content: 'AT';
161 | }
162 | div.chartDesigner::before {
163 | content: 'Chart';
164 | font-size: 12px;
165 | }
166 | div.illustrator::before {
167 | content: 'Illustration';
168 | font-size: 12px;
169 | }
170 | div.illustrator,
171 | div.chartDesigner {
172 | display: flex;
173 | flex-direction: column;
174 | font-size: 18px;
175 | margin: 10px 0 10px 80px;
176 | animation: slideAndFadeIn 0.8s ease-out;
177 | }
178 | div.illustrator {
179 | animation: slideAndFadeIn 1s ease-out;
180 | }
181 | @keyframes slideAndFadeIn {
182 | 0% {
183 | opacity: 0;
184 | margin: 10px 0 10px 150px;
185 | }
186 | 70% {
187 | opacity: 1;
188 | }
189 | 100% {
190 | opacity: 1;
191 | margin: 10px 0 10px 80px;
192 | }
193 | }
194 | div.songImage {
195 | transform: skew(-15deg);
196 | height: 80%;
197 | width: 50%;
198 | overflow: hidden;
199 | display: flex;
200 | justify-content: center;
201 | align-items: center;
202 | object-fit: cover;
203 | }
204 | img.illustration {
205 | height: 100%;
206 | width: 150%;
207 | margin: -15%;
208 | object-fit: cover;
209 | margin-right: -50px;
210 | transform: skew(15deg);
211 | /* clip-path: polygon(75px 0, 100% 0, 400px 100%, 0 100%); */
212 | }
213 |
214 | @media screen and (max-width: 576px) {
215 | body > div.mainContent > div.songImage {
216 | display: none;
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/loadingScreen/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 正在加载... - PhiCommunity
9 |
10 |
11 |
12 |
13 |
14 |
Loading...
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/loadingScreen/index.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import tips from 'assets/tips.json';
3 |
4 | window.addEventListener('DOMContentLoaded', () => {
5 | const rndNum = parseInt(Math.random() * (tips.length + 1), 10);
6 | const tip = tips[rndNum];
7 | console.log(tip);
8 | document.querySelector('#tipConteiner').innerText = tip;
9 | });
10 |
--------------------------------------------------------------------------------
/src/loadingScreen/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 | html,
3 | body {
4 | box-sizing: border-box;
5 | height: 100%;
6 | width: 100%;
7 | margin: 0;
8 | padding: 0;
9 | }
10 | body {
11 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
12 | no-repeat fixed;
13 | background-size: cover !important;
14 | backdrop-filter: blur(1px);
15 | display: flex;
16 | justify-content: center;
17 | align-items: center;
18 | color: #fff;
19 | }
20 | body::before {
21 | content: '';
22 | position: fixed;
23 | top: 0;
24 | left: 0;
25 | right: 0;
26 | bottom: 0;
27 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
28 | no-repeat fixed;
29 | background-size: cover;
30 | filter: blur(1px);
31 | z-index: -1;
32 | }
33 | div.tip::before {
34 | content: 'Tip: ';
35 | }
36 | div.tip {
37 | color: #000;
38 | position: fixed;
39 | left: 2%;
40 | bottom: 5%;
41 | text-shadow: 0 0 5px #000;
42 | }
43 | div.loadingBar {
44 | position: fixed;
45 | right: 2%;
46 | bottom: 5%;
47 | overflow: hidden;
48 | }
49 | div.loadingBarTxT {
50 | color: #000;
51 | z-index: 999;
52 | animation: loadingBarTXTAnim 1s ease-in-out infinite;
53 | text-shadow: 0 0 10px #000;
54 | }
55 | div.loadingBarBG {
56 | position: absolute;
57 | left: 0;
58 | top: 0;
59 | height: 100%;
60 | width: 100%;
61 | z-index: -1;
62 | background-color: #fff;
63 | animation: loadingBarBGAnim 1s ease-in-out infinite;
64 | }
65 | @keyframes loadingBarBGAnim {
66 | 0% {
67 | left: -100%;
68 | }
69 | 100% {
70 | left: 200%;
71 | }
72 | }
73 | @keyframes loadingBarTXTAnim {
74 | 0% {
75 | color: #fff;
76 | }
77 | 50% {
78 | color: #000;
79 | }
80 | 100% {
81 | color: #fff;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/settings/calibrate/calibrate.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/settings/calibrate/calibrate.mp3
--------------------------------------------------------------------------------
/src/settings/calibrate/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 校准 - PhiCommunity
9 |
10 |
11 |
12 |
13 |
14 | 偏移率设置
15 |
16 |
17 |
18 | 在每个第三拍(重拍)点击按钮
19 | 本结果仅供参考,实际效果可能会有所不同(触摸屏延迟可能需要手动调低)
20 | 不同设备(键盘/鼠标/触摸)的结果可能会有所不同
21 |
22 |
点击
23 |
24 |
-
25 |
-
26 |
-
27 |
-
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/settings/calibrate/index.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import calibrate_mp3 from './calibrate.mp3';
3 | window.addEventListener('DOMContentLoaded', function () {
4 | fetch(calibrate_mp3)
5 | .then((response) =>response.arrayBuffer())
6 | .then((arrayBuffer) => {
7 | window.caliBrateAudioArrayBuffer = arrayBuffer;
8 | })
9 | .catch(() => {
10 | alert('错误:无法加载校准音频');
11 | });
12 | });
13 | document
14 | .querySelector('button#startBtn')
15 | .addEventListener('click', function () {
16 | if (window.caliBrateAudioArrayBuffer == undefined) {
17 | alert('抱歉,校准音频尚在加载中,请稍后');
18 | return;
19 | }
20 | window.calibrateActx = null;
21 | window.calibrateActx = new (window.AudioContext ||
22 | window.webkitAudioContext ||
23 | window.mozAudioContext ||
24 | window.msAudioContext)();
25 | window.calibrateActx.decodeAudioData(
26 | window.caliBrateAudioArrayBuffer,
27 | function (buffer) {
28 | window.calibraceACtxSource =
29 | window.calibrateActx.createBufferSource();
30 | window.calibraceACtxSource.buffer = buffer;
31 | window.calibraceACtxSource.connect(
32 | window.calibrateActx.destination
33 | );
34 | window.actxStartTime = window.calibrateActx.currentTime;
35 | window.calibraceACtxSource.start(0);
36 | window.calibraceACtxSource.addEventListener(
37 | 'ended',
38 | function () {
39 | window.calibrateActx == undefined ? undefined : window.calibrateActx.close();
40 | window.calibrateActx = undefined;
41 | document
42 | .querySelector('button#startBtn')
43 | .removeAttribute('disabled');
44 | document
45 | .querySelector('button#clickBtn')
46 | .removeEventListener('click', calibrate);
47 | window.calibrateActx = null;
48 | }
49 | );
50 | }
51 | );
52 | document
53 | .querySelector('button#startBtn')
54 | .setAttribute('disabled', 'disabled');
55 | document
56 | .querySelector('button#clickBtn')
57 | .addEventListener('click', calibrate);
58 | });
59 | document.body.addEventListener('keydown', () => {
60 | document.querySelector('button#clickBtn').click();
61 | });
62 | function calibrate(e) {
63 | const currentTime= window.calibrateActx.currentTime - window.actxStartTime;
64 | console.log(currentTime);
65 | var stage = 1;
66 | if (currentTime > 0 && currentTime <= 2) {
67 | console.log('Calibration stage 1',e);
68 | stage = 1;
69 | }
70 | if (currentTime > 2 && currentTime <= 4) {
71 | console.log('Calibration stage 2',e);
72 | stage = 2;
73 | }
74 | if (currentTime > 4 && currentTime <= 6) {
75 | console.log('Calibration stage 3',e);
76 | stage = 3;
77 | }
78 | if (currentTime > 6 && currentTime <= 8) {
79 | console.log('Calibration stage 4',e);
80 | stage = 4;
81 | }
82 | const result = document.querySelector('#result' + stage);
83 | result.innerText = Math.round((currentTime - (stage*2-0.5))*1000);
84 | }
85 | document
86 | .querySelector('button#cancelBtn')
87 | .addEventListener('click', function () {
88 | document.querySelector('button#startBtn').removeAttribute('disabled');
89 | document
90 | .querySelector('button#clickBtn')
91 | .removeEventListener('click', calibrate);
92 | try {
93 | window.calibraceACtxSource.stop();
94 | } catch (e) {
95 | undefined;
96 | }
97 | const result1 = parseFloat(
98 | document.querySelector('#result1').innerText.replace('-', '-0')
99 | );
100 | const result2 = parseFloat(
101 | document.querySelector('#result2').innerText.replace('-', '-0')
102 | );
103 | const result3 = parseFloat(
104 | document.querySelector('#result3').innerText.replace('-', '-0')
105 | );
106 | const result4 = parseFloat(
107 | document.querySelector('#result4').innerText.replace('-', '-0')
108 | );
109 | const finalResult = Math.round(
110 | (result1 + result2 + result3 + result4) / 4
111 | );
112 | const result = confirm(
113 | '谱面延时即将被设置为 ' +
114 | finalResult +
115 | ' ,是否确认?\n单击“取消”为继续而不保存'
116 | );
117 | if (result) {
118 | localStorage.setItem('input-offset', finalResult);
119 | location.href = '../index.html';
120 | }
121 | if (!result) {
122 | location.href = '../index.html';
123 | }
124 | });
125 |
--------------------------------------------------------------------------------
/src/settings/calibrate/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 | html,
3 | body {
4 | display: flex;
5 | justify-content: center;
6 | align-items: center;
7 | font-size: 16px;
8 | height: 100%;
9 | width: 100%;
10 | /* overflow: hidden; */
11 | box-sizing: border-box;
12 | margin: 0;
13 | padding: 0;
14 | }
15 | * {
16 | outline: none;
17 | }
18 | html {
19 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
20 | no-repeat fixed;
21 | background-size: cover;
22 | }
23 | html::before {
24 | content: '';
25 | position: absolute;
26 | top: 0;
27 | left: 0;
28 | right: 0;
29 | bottom: 0;
30 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
31 | no-repeat fixed;
32 | background-size: cover;
33 | filter: blur(10px);
34 | z-index: -1;
35 | }
36 | div.container {
37 | height: 300px;
38 | width: 500px;
39 | background: #dddddf;
40 | display: flex;
41 | flex-direction: column;
42 | justify-content: space-between;
43 | align-items: center;
44 | overflow: hidden;
45 | }
46 | div.container div.title {
47 | width: 100%;
48 | padding: 10px;
49 | background: linear-gradient(to right, #0173ff, #588de4);
50 | color: #fff;
51 | text-align: center;
52 | font-weight: bold;
53 | }
54 | div.container div.content {
55 | display: flex;
56 | flex-direction: column;
57 | justify-content: space-between;
58 | align-items: center;
59 | width: 100%;
60 | height: max-content;
61 | }
62 | div.container div.content div.description {
63 | font-size: 12px;
64 | padding: 10px;
65 | text-align: center;
66 | }
67 | div.container div.content button#clickBtn {
68 | width: 50px;
69 | height: 50px;
70 | margin: 10px;
71 | background: #b7b6bb;
72 | color: #000;
73 | border: 1.5px solid #939394;
74 | color: #fff;
75 | text-shadow: 1px 1px 1px #9f9f9f;
76 | font-weight: bold;
77 | cursor: pointer;
78 | }
79 | div.container div.content div.results {
80 | width: 60%;
81 | display: flex;
82 | flex-direction: row;
83 | justify-content: space-between;
84 | color: #000;
85 | text-align: center;
86 | }
87 | div.container div.content div.results div:first-child {
88 | border-top-left-radius: 5px;
89 | border-bottom-left-radius: 5px;
90 | border-left: 1.4px #9f9f9f solid;
91 | }
92 | div.container div.content div.results div:last-child {
93 | border-top-right-radius: 5px;
94 | border-bottom-right-radius: 5px;
95 | border-right: 1.4px #9f9f9f solid;
96 | }
97 | div.container div.content div.results div {
98 | border: 1.2px #9f9f9f solid;
99 | width: 25%;
100 | display: flex;
101 | justify-content: center;
102 | align-items: center;
103 | background: #fff;
104 | }
105 | div.container div.footer {
106 | display: flex;
107 | flex-direction: row;
108 | width: 100%;
109 | justify-content: center;
110 | align-items: center;
111 | }
112 | div.container div.footer button {
113 | width: 50%;
114 | padding: 5px;
115 | cursor: pointer;
116 | }
117 | div.container div.footer button#startBtn {
118 | background: #0173ff;
119 | color: #fff;
120 | font-size: 16px;
121 | border: none;
122 | border-right: 1px solid #fff;
123 | }
124 | div.container div.footer button#cancelBtn {
125 | background: #588de4;
126 | color: #fff;
127 | font-size: 16px;
128 | border: none;
129 | border-left: 1px solid #fff;
130 | }
131 |
--------------------------------------------------------------------------------
/src/settings/components/button.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @param {import('../setting').ButtonSetting} option
3 | */
4 | export function ButtonItem({ title, onClick }) {
5 | const container = document.createElement('div');
6 | container.className = 'item';
7 |
8 | const button = document.createElement('button');
9 | button.innerText = title;
10 | button.className = 'button';
11 |
12 | container.appendChild(button);
13 |
14 | if (title=='修改玩家头像') {
15 | const fileBtn = document.createElement('input');
16 | fileBtn.type = 'file';
17 | fileBtn.accept = '.png,.jpg,.jpeg,.webp';
18 | fileBtn.id = 'fileBtn';
19 | fileBtn.style = 'display:none';
20 | fileBtn.addEventListener(
21 | 'change',
22 | function (e) {
23 | let file = e.path[0].files[0];
24 | if (!/(.png|.jpg|.jpeg|.webp)$/.test(file.name)) {
25 | alert('不支持的文件格式,支持的格式为:png, jpg, jpeg, webp');
26 | return ;
27 | }
28 | if (file.size > 4194304) {
29 | alert(`头像过大,大小限制为:4MB`);
30 | return;
31 | }
32 |
33 | let reader = new FileReader();
34 | reader.readAsDataURL(file);
35 | reader.onloadend = function () {
36 | const avatar = reader.result;
37 | if (avatar==''||avatar==null||avatar==undefined) {
38 | console.error('Failed to set player avatar : Empty input');
39 | return;
40 | }
41 | localStorage.setItem('playerAvatar', avatar);
42 | }
43 | e.stopPropagation();
44 | e.preventDefault();
45 | },
46 | false
47 | )
48 | container.appendChild(fileBtn);
49 | }
50 |
51 | button.onclick = onClick;
52 |
53 | return { element: container };
54 | }
55 |
--------------------------------------------------------------------------------
/src/settings/components/index.js:
--------------------------------------------------------------------------------
1 | export * from './slide.js';
2 | export * from './toggle.js';
3 | export * from './button.js';
4 |
--------------------------------------------------------------------------------
/src/settings/components/slide.js:
--------------------------------------------------------------------------------
1 | const marginRange = 80;
2 |
3 | /**
4 | * @param {import('./setting').SlideSetting} option
5 | */
6 | export function SliderItem({
7 | title,
8 | codename,
9 | range,
10 | defaultValue = range[0],
11 | offset = 1,
12 | }) {
13 | let currentValue = defaultValue;
14 |
15 | const container = document.createElement('div');
16 | container.className = 'item';
17 |
18 | const titleElement = document.createElement('div');
19 | titleElement.className = 'title';
20 | titleElement.dataset['name'] = title;
21 | titleElement.dataset['value'] = currentValue;
22 |
23 | const slider = Slider({ range, defaultValue, offset, onValueChange });
24 |
25 | container.appendChild(titleElement);
26 | container.appendChild(slider.element);
27 |
28 | return { element: container, getValue: () => currentValue };
29 |
30 | function onValueChange(value) {
31 | titleElement.dataset['value'] = value;
32 | window.localStorage.setItem(codename, value);
33 | currentValue = value;
34 | }
35 | }
36 |
37 | /**
38 | * @param {{
39 | * range: [number,number],
40 | * defaultValue?: number = range[0],
41 | * offset: number = 1,
42 | * onValueChange:(number) => void
43 | * }} option
44 | */
45 | function Slider({ range, defaultValue = range[0], offset = 1, onValueChange }) {
46 | let currentValue;
47 | const total = range[1] - range[0];
48 |
49 | const container = document.createElement('div');
50 | container.className = 'slider';
51 |
52 | const slideBlock = document.createElement('div');
53 | slideBlock.className = 'slideBlock';
54 | container.appendChild(slideBlock);
55 |
56 | container.addEventListener('click', (e) => {
57 | if (e.offsetX > container.offsetWidth - 35) {
58 | add(offset);
59 | }
60 | if (e.offsetX < 35) {
61 | add(0 - offset);
62 | }
63 | });
64 |
65 | //拖曳滚动条
66 | let isDragStart = false;
67 |
68 | /**
69 | * 电脑端
70 | * @param {MouseEvent} e
71 | */
72 | const onMouseDrag = (e) => {
73 | set(
74 | Math.round(
75 | range[0] +
76 | ((e.pageX - container.clientLeft - 70) /
77 | (container.clientWidth - 100)) *
78 | total
79 | )
80 | );
81 | };
82 | slideBlock.addEventListener('mousedown', () => {
83 | isDragStart = true;
84 | window.addEventListener('mousemove', onMouseDrag);
85 | });
86 | window.addEventListener('mouseup', () => {
87 | if (!isDragStart) return;
88 | isDragStart = false;
89 | window.removeEventListener('mousemove', onMouseDrag);
90 | });
91 |
92 | /**
93 | * 移动端
94 | * @param {TouchEvent} e
95 | */
96 | const onTouchDrag = (e) => {
97 | set(
98 | Math.round(
99 | range[0] +
100 | ((e.targetTouches[0].pageX - container.clientLeft - 70) /
101 | (container.clientWidth - 100)) *
102 | total
103 | )
104 | );
105 | };
106 |
107 | slideBlock.addEventListener('touchstart', () => {
108 | isDragStart = true;
109 | window.addEventListener('touchmove', onTouchDrag);
110 | });
111 |
112 | window.addEventListener('touchend', () => {
113 | if (!isDragStart) return;
114 | isDragStart = false;
115 | window.removeEventListener('touchmove', onTouchDrag);
116 | });
117 |
118 | set(defaultValue);
119 |
120 | return { element: container, set, add };
121 |
122 | /**
123 | * @param {number} value
124 | */
125 | function set(newValue) {
126 | if (newValue < range[0]) newValue = range[0];
127 | if (newValue > range[1]) newValue = range[1];
128 |
129 | slideBlock.style.marginLeft =
130 | ((newValue - (range[0] + range[1]) / 2) / total) * 2 * marginRange +
131 | '%';
132 |
133 | onValueChange(newValue);
134 |
135 | currentValue = newValue;
136 | return currentValue;
137 | }
138 |
139 | /**
140 | * @param {number} offset
141 | */
142 | function add(offset) {
143 | console.log(offset);
144 | return set(currentValue + offset);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/settings/components/toggle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @param {import('./setting').ToggleSetting} option
3 | */
4 | export function ToggleItem({ title, codename, defaultValue = false }) {
5 | let isChecked = defaultValue;
6 |
7 | const container = document.createElement('div');
8 | container.className = 'item';
9 |
10 | const titleElement = document.createElement('div');
11 | titleElement.className = 'title';
12 | titleElement.dataset['name'] = title;
13 |
14 | const toggleElement = document.createElement('div');
15 | toggleElement.classList.add('toggle');
16 | if (isChecked) toggleElement.classList.add('checked');
17 |
18 | container.appendChild(titleElement);
19 | container.appendChild(toggleElement);
20 |
21 | container.addEventListener('click', () => {
22 | if (isChecked) {
23 | toggleElement.classList.remove('checked');
24 | window.localStorage.setItem(codename, false);
25 | } else {
26 | toggleElement.classList.add('checked');
27 | window.localStorage.setItem(codename, true);
28 | }
29 | isChecked = !isChecked;
30 | });
31 |
32 | return { element: container };
33 | }
34 |
--------------------------------------------------------------------------------
/src/settings/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 | 设置 - PhiCommunity
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
PhiCommunity
25 |
26 | 版本:未获取/获取失败
27 |
28 |
设备 未获取/获取失败
29 |
30 | 注意:此项目与厦门鸽游网络有限公司(Xiamen Pigeon Games Network
31 | Co., Ltd.)没有任何关系
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/settings/index.js:
--------------------------------------------------------------------------------
1 | import { settings } from './setting.js';
2 | import { SliderItem, ToggleItem, ButtonItem } from './components/index.js';
3 | import './style.css';
4 |
5 | // 全局初始化鼠标滚轮/移动端滑动坐标
6 | // var yCoord = 0,
7 | // previousTouchYCoord = 0;
8 | window.addEventListener('DOMContentLoaded', () => {
9 | if (window.localStorage.length == 0) {
10 | document.querySelector('#backBtn').addEventListener('click', () => {
11 | // location.href = '../chapterSelect/index.html';
12 | location.href = '../whilePlaying/index.html?play=tutorial&l=ez';
13 | });
14 | } else {
15 | document.querySelector('#backBtn').addEventListener('click', () => {
16 | location.href = '../chapterSelect/index.html';
17 | });
18 | }
19 |
20 | document.querySelector('#ver').innerText = $VERSION;
21 |
22 | try {
23 | document.querySelector('#device').innerText =
24 | 'Platform: ' +
25 | navigator.userAgentData.platform +
26 | ' ; isMobile:' +
27 | navigator.userAgentData.mobile;
28 | } catch (error) {
29 | document.querySelector('#device').innerText =
30 | 'User-Agent : ' +
31 | navigator.userAgent.slice(navigator.userAgent.lastIndexOf(' '));
32 | }
33 | document.querySelector('#device').title = navigator.userAgent;
34 | // loadSettings();
35 | //创建设置条目
36 | settings.forEach((setting) => {
37 | let item;
38 | switch (setting.type) {
39 | case 'slide':
40 | setting.defaultValue =
41 | Number(window.localStorage.getItem(setting.codename)) ||
42 | setting.defaultValue;
43 | item = SliderItem(setting);
44 | break;
45 | case 'toggle':
46 | if (window.localStorage.getItem(setting.codename) !== null) {
47 | setting.defaultValue =
48 | window.localStorage.getItem(setting.codename) == 'true'
49 | ? true
50 | : false;
51 | }
52 | item = ToggleItem(setting);
53 | break;
54 | case 'button':
55 | item = ButtonItem(setting);
56 | break;
57 | default:
58 | throw new Error('Unknown setting: ' + setting);
59 | }
60 | document.getElementById('settingItems').appendChild(item.element);
61 | });
62 | });
63 |
--------------------------------------------------------------------------------
/src/settings/setting.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @typedef {{
3 | * title: string,
4 | * codename: string
5 | * }} BaseSetting
6 | *
7 | * @typedef { BaseSetting & {
8 | * range: [number,number],
9 | * defaultValue?: number = range[0],
10 | * offset: number = 1,
11 | * }} SlideSetting
12 | *
13 | * @typedef { BaseSetting & {
14 | * defaultValue:boolean = false,
15 | * }} ToggleSetting
16 | *
17 | * @typedef {{
18 | * title: string,
19 | * onClick: (this: GlobalEventHandlers, ev: MouseEvent) => any
20 | * }} ButtonSetting
21 | *
22 | */
23 |
24 | /**
25 | * @type {Array <
26 | * (SlideSetting & {type: 'slide'})|
27 | * (ToggleSetting & {type: 'toggle'}|
28 | * (ButtonSetting & {type: 'button'})>
29 | * }
30 | */
31 | export const settings = [
32 | {
33 | type: 'slide',
34 | title: '谱面延时(MS)',
35 | codename: 'input-offset',
36 | range: [-500, 500],
37 | defaultValue: 0,
38 | offset: 5,
39 | },
40 | {
41 | type: 'button',
42 | title: '根据声音调整偏移率',
43 | onClick() {
44 | location.href = './calibrate/index.html';
45 | },
46 | },
47 | {
48 | type: 'slide',
49 | title: '按键缩放',
50 | codename: 'select-scale-ratio',
51 | range: [1, 5],
52 | defaultValue: 3,
53 | },
54 | {
55 | type: 'slide',
56 | title: '背景亮度',
57 | codename: 'select-global-alpha',
58 | range: [1, 5],
59 | defaultValue: 3,
60 | },
61 | {
62 | type: 'toggle',
63 | title: '开启多押辅助',
64 | codename: 'highLight',
65 | defaultValue: true,
66 | },
67 | {
68 | type: 'toggle',
69 | title: '开启打击音效',
70 | codename: 'hitSong',
71 | defaultValue: true,
72 | },
73 | {
74 | type: 'toggle',
75 | title: '游玩时自动全屏',
76 | codename: 'autoFullscreen',
77 | defaultValue: true,
78 | },
79 | {
80 | type: 'toggle',
81 | title: '开启FC/AP指示器',
82 | codename: 'lineColor',
83 | },
84 | //下面就是模拟器其他的功能了
85 | {
86 | type: 'toggle',
87 | title: '开启低分辨率模式',
88 | codename: 'enableLowRes',
89 | },
90 | {
91 | type: 'slide',
92 | title: '界面宽高比',
93 | codename: 'select-aspect-ratio',
94 | range: [1, 8],
95 | defaultValue: 8,
96 | },
97 | {
98 | type: 'button',
99 | title: '界面宽高比数值说明',
100 | onClick() {
101 | alert(
102 | '1=>5:4 (1.25)\n2=>4:3 (1.333333)\n3=>10:7 (1.428571)\n4=>19:13 (1.461538)\n5=>8:5 (1.6)\n6=>5:3 (1.666667)\n7=>22:13 (1.692308)\n8=>16:9 (1.777778)'
103 | );
104 | },
105 | },
106 | {
107 | type: 'toggle',
108 | title: '开启HyperMode(严格判定)',
109 | codename: 'hyperMode',
110 | },
111 | {
112 | type: 'toggle',
113 | title: '启用旧版本打歌界面UI',
114 | codename: 'useOldUI',
115 | },
116 | {
117 | type: 'toggle',
118 | title: '背景模糊显示',
119 | codename: 'imageBlur',
120 | defaultValue: true,
121 | },
122 | // {
123 | // type: 'toggle',
124 | // title: '显示过渡动画',
125 | // codename: 'showTransition',
126 | // },
127 | {
128 | type: 'slide',
129 | title: '谱面倍速(10为1倍)',
130 | codename: 'chart-speedchange',
131 | range: [7, 15],
132 | defaultValue: 10,
133 | offset: 1,
134 | },
135 | {
136 | type: 'toggle',
137 | title: '启用AutoPlay',
138 | codename: 'autoplay',
139 | },
140 | {
141 | type: 'toggle',
142 | title: '游玩时使用背景动画作为背景',
143 | codename: 'useBGABG',
144 | },
145 | {
146 | type: 'toggle',
147 | title: '开启触摸反馈',
148 | codename: 'feedback',
149 | },
150 | {
151 | type: 'toggle',
152 | title: '显示定位点',
153 | codename: 'showPoint',
154 | },
155 | {
156 | type: 'button',
157 | title: '观看教学',
158 | onClick() {
159 | location.href = '../whilePlaying/index.html?play=tutorial&l=ez';
160 | },
161 | },
162 | {
163 | type: 'button',
164 | title: '修改玩家昵称',
165 | onClick() {
166 | const name = prompt('输入昵称', localStorage.getItem('playerName')||'GUEST');
167 | if (name ==''||name==null||name==undefined) {
168 | console.error('Failed to set player name : Empty input');
169 | return;
170 | }
171 | localStorage.setItem('playerName', name);
172 | },
173 | },
174 | {
175 | type: 'button',
176 | title: '修改玩家头像',
177 | onClick() {
178 | document.getElementById("fileBtn").click();
179 | },
180 | },
181 | {
182 | type: 'button',
183 | title: '关于我们',
184 | onClick() {
185 | location.href = '../aboutUs/index.html';
186 | },
187 | },
188 | {
189 | type: 'button',
190 | title: '清除全部数据',
191 | onClick() {
192 | window.localStorage.clear();
193 | location.href = '../index.html';
194 | },
195 | },
196 | {
197 | type: 'button',
198 | title: '导出本地数据到剪贴板',
199 | onClick() {
200 | navigator.clipboard.writeText(JSON.stringify(localStorage));
201 | this.innerHTML = ' 成功';
202 | const timeout = setTimeout(() => {
203 | this.innerHTML = '导出本地数据到剪贴板';
204 | clearTimeout(timeout);
205 | }, 2000);
206 | },
207 | },
208 | {
209 | type: 'button',
210 | title: '从剪贴板导入数据',
211 | onClick() {
212 | navigator.clipboard.readText().then((clipText) => {
213 | try {
214 | const clipTextObj = JSON.parse(clipText);
215 | const clipTextObjKeys = Object.keys(clipTextObj);
216 | for (const keys in clipTextObjKeys) {
217 | console.log(keys, clipTextObj[keys]);
218 | localStorage.setItem(
219 | clipTextObjKeys[keys],
220 | clipTextObj[clipTextObjKeys[keys]]
221 | );
222 | }
223 | this.innerHTML =
224 | ' 成功';
225 | const timeout = setTimeout(() => {
226 | this.innerHTML = '导出本地数据到剪贴板';
227 | clearTimeout(timeout);
228 | }, 2000);
229 | location.reload();
230 | } catch (error) {
231 | alert('导入失败,请检查剪贴板内容是否正确\n' + error);
232 | }
233 | });
234 | },
235 | },
236 | ];
237 |
--------------------------------------------------------------------------------
/src/settings/statistic/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 统计 - PhiCommunity
8 |
9 |
10 |
11 |
12 |
13 |
14 |
GUEST
15 |
16 |
17 |
8.48
18 |
19 |
N/A
20 |
N/A
21 |
N/A
22 |
23 |
24 | Lorem ipsum dolor sit amet consectetur, adipisicing
25 | elit. Harum ratione maiores maxime id laudantium non
26 | architecto. Praesentium explicabo voluptate, perferendis
27 | accusantium minima unde autem dicta, eius quas expedita
28 | qui assumenda.
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/settings/statistic/index.js:
--------------------------------------------------------------------------------
1 | import './style.css';
--------------------------------------------------------------------------------
/src/settings/statistic/style.css:
--------------------------------------------------------------------------------
1 | html,
2 | body{
3 | height: 100vh;
4 | width: 100vw;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | margin: 0;
9 | padding: 0;
10 | background: url(assets/images/ElementSqare.Half.Size.webp) no-repeat center center;
11 | background-size: cover;
12 | }
13 | div.outerContainter{
14 | height: 70%;
15 | width: 70%;
16 | box-shadow: -15px 20px 0 5px rgba(0, 0, 0, 0.8);
17 | display: flex;
18 | justify-content: center;
19 | align-items: center;
20 | }
21 | div.innerContainer{
22 | color: #FFF;
23 | width: 100%;
24 | height: 80%;
25 | background: rgba(0, 0, 0, 0.8);
26 | }
--------------------------------------------------------------------------------
/src/settings/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 | html,
3 | body {
4 | height: 100%;
5 | width: 100%;
6 | margin: 0;
7 | padding: 0;
8 | overflow: hidden;
9 | }
10 | html {
11 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
12 | no-repeat fixed;
13 | background-size: cover;
14 | backdrop-filter: blur(15px);
15 | }
16 | html::before {
17 | content: '';
18 | position: absolute;
19 | top: 0;
20 | left: 0;
21 | right: 0;
22 | bottom: 0;
23 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
24 | no-repeat fixed;
25 | background-size: cover;
26 | filter: blur(10px);
27 | z-index: -1;
28 | }
29 | div.leftArea {
30 | position: fixed;
31 | top: 0;
32 | left: 0;
33 | padding: 100px 1%;
34 | height: calc(100% - 200px);
35 | width: 45%;
36 | background: rgba(0, 0, 0, 0.5);
37 | /* border: 1px #6cf solid; */
38 | color: #fff;
39 | overflow-y: scroll;
40 | scrollbar-width: none;
41 | }
42 |
43 | div.leftArea::-webkit-scrollbar {
44 | display: none;
45 | }
46 | div.item {
47 | width: 100%;
48 | height: 75px;
49 | /* border: 1px #6cf solid; */
50 | margin: 50px 0;
51 | display: flex;
52 | justify-content: space-between;
53 | align-items: center;
54 | flex-direction: column;
55 | }
56 | div.title::before {
57 | content: attr(data-name);
58 | padding-left: 20px;
59 | }
60 | div.title::after {
61 | content: attr(data-value);
62 | padding-right: 20px;
63 | }
64 | div.title {
65 | width: 100%;
66 | display: flex;
67 | justify-content: space-between;
68 | }
69 | div.slider {
70 | width: 95%;
71 | height: 30px;
72 | color: #000;
73 | display: flex;
74 | justify-content: space-between;
75 | align-items: center;
76 | background-color: #00000056;
77 | }
78 | div.slider::before,
79 | div.slider::after,
80 | div.slideBlock {
81 | display: block;
82 | height: 120%;
83 | width: 35px;
84 | max-width: calc(10% - 15px);
85 | display: flex;
86 | justify-content: center;
87 | align-items: center;
88 | background-color: #fff;
89 | font-size: 1.4em;
90 | cursor: pointer;
91 | }
92 | div.slider::before {
93 | content: '-';
94 | margin-right: 10px;
95 | }
96 | div.slider::after {
97 | content: '+';
98 | margin-left: 10px;
99 | }
100 | div.slideBlock::before {
101 | content: '';
102 | display: block;
103 | }
104 | div.slideBlock {
105 | position: relative;
106 | left: 0;
107 | top: 0;
108 | display: block;
109 | height: 120%;
110 | width: 35px;
111 | background-color: #fff;
112 | }
113 |
114 | div.toggle {
115 | height: 25px;
116 | width: 65px;
117 | display: flex;
118 | justify-content: start;
119 | align-items: center;
120 | background-color: #000;
121 | margin-right: 25px;
122 | align-self: flex-end;
123 | cursor: pointer;
124 | }
125 |
126 | div.toggle.checked {
127 | justify-content: end;
128 | }
129 | div.toggle::before {
130 | content: '';
131 | display: block;
132 | height: 30px;
133 | width: 30px;
134 | background-color: #fff;
135 | }
136 | div.toggle.checked::after {
137 | content: ' ';
138 | background: url(assets/images/Tick.svg) no-repeat center center;
139 | background-size: contain;
140 | font-family: Arial, Helvetica, sans-serif;
141 | order: 0;
142 | display: flex;
143 | justify-content: center;
144 | align-items: center;
145 | width: calc(100% - 30px);
146 | height: 100%;
147 | }
148 | div.toggle.checked::before {
149 | order: 1;
150 | content: '';
151 | display: block;
152 | height: 30px;
153 | width: 30px;
154 | background-color: #fff;
155 | }
156 | button.button {
157 | height: 80%;
158 | width: 200px;
159 | margin-left: 10%;
160 | align-self: flex-start;
161 | font-size: 16px;
162 | outline: none;
163 | color: #fff;
164 | border: 2px #fff solid;
165 | background: transparent;
166 | cursor: pointer;
167 | display: flex;
168 | flex-direction: row;
169 | justify-content: center;
170 | align-items: center;
171 | }
172 | button.button > img {
173 | height: 20px;
174 | width: auto;
175 | margin-right: 20px;
176 | }
177 | div.aboutContainer {
178 | position: fixed;
179 | width: 50%;
180 | right: 0px;
181 | top: 50%;
182 | transform: translateY(-50%);
183 | display: flex;
184 | flex-direction: column;
185 | justify-content: center;
186 | align-items: center;
187 | }
188 | div.aboutContainer > div.device {
189 | display: flex;
190 | flex-direction: column;
191 | justify-content: center;
192 | align-items: center;
193 | }
194 | div.aboutContainer > div.device > span#device {
195 | text-align: center;
196 | }
197 | @media screen and (max-width: 576px) {
198 | div.aboutContainer {
199 | display: none;
200 | }
201 | div.leftArea {
202 | width: 100%;
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/songSelect/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 | 歌曲选择 - PhiCommunity
11 |
12 |
13 |
14 |
22 |
23 |
27 |
28 |
29 |
30 |
默认
31 |
35 | 设置
36 |
37 |
38 |
39 |
40 |
41 |
42 |
48 |
49 |
61 |
62 |
63 |
64 |
Loading BGM...
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/style.redirect.css:
--------------------------------------------------------------------------------
1 | body,
2 | html {
3 | height: 100vh;
4 | width: 100vw;
5 | display: flex;
6 | justify-content: center;
7 | align-items: center;
8 | box-sizing: border-box;
9 | color: #fff;
10 | }
11 | button {
12 | outline: none;
13 | border: 1px #fff solid;
14 | background: none;
15 | color: #fff;
16 | padding: 1rem;
17 | margin: 5px 10px;
18 | font-size: 1.1rem;
19 | font-weight: bold;
20 | cursor: pointer;
21 | }
22 | body::before {
23 | content: '';
24 | display: block;
25 | height: 100%;
26 | width: 100%;
27 | position: fixed;
28 | left: 0;
29 | top: 0;
30 | z-index: -1;
31 | filter: contrast(0.6) brightness(0.6);
32 | background: #999 url(assets/images/ElementSqare.Half.Size.webp) center
33 | center no-repeat fixed;
34 | background-size: cover;
35 | transition: all 0.3s ease-in-out;
36 | }
37 | div.main {
38 | min-width: 320px;
39 | height: 90%;
40 | width: 80%;
41 | display: flex;
42 | justify-content: center;
43 | align-items: center;
44 | flex-direction: column;
45 | border-top: 2px #fff solid;
46 | border-bottom: 2px #fff solid;
47 | animation: stretch 1.5s ease;
48 | }
49 | @keyframes stretch {
50 | 0% {
51 | opacity: 0;
52 | height: 10px;
53 | overflow: hidden;
54 | }
55 | 50% {
56 | opacity: 1;
57 | height: 10px;
58 | overflow: hidden;
59 | }
60 | }
61 | audio {
62 | width: 300px !important;
63 | height: 100px !important;
64 | }
65 | div.btnContainer {
66 | text-align: center;
67 | }
68 | div.popupOverlay {
69 | display: none;
70 | }
71 | div.popupOverlay.show {
72 | background: rgba(255, 255, 255, 0.4);
73 | height: 100%;
74 | width: 100%;
75 | position: fixed;
76 | top: 0;
77 | left: 0;
78 | z-index: 3;
79 | display: flex;
80 | justify-content: center;
81 | align-items: center;
82 | flex-direction: column;
83 | animation: fadeIn 0.5s ease;
84 | }
85 | div.popupOverlay.show::after {
86 | content: '点击区域外任意处关闭';
87 | display: block;
88 | padding: 20px;
89 | }
90 | @keyframes fadeIn {
91 | 0% {
92 | opacity: 0;
93 | }
94 | 100% {
95 | opacity: 1;
96 | }
97 | }
98 | div.popupContent {
99 | background: #fff;
100 | padding: 0 50px;
101 | color: #000;
102 | min-width: 320px;
103 | height: 80%;
104 | width: 80%;
105 | display: flex;
106 | align-items: center;
107 | flex-direction: column;
108 | animation: slideDown 0.5s ease;
109 | overflow-y: scroll;
110 | overflow-x: hidden;
111 | }
112 | div.popupContent > img {
113 | width: 100%;
114 | height: auto;
115 | }
116 | div.popupContent > a.title {
117 | margin: 50px 0;
118 | border-bottom: dotted 1px #999;
119 | transition: all 0.2s ease-in-out;
120 | text-decoration: none;
121 | color: inherit;
122 | }
123 | div.popupContent > .title:hover {
124 | border-bottom-color: transparent;
125 | }
126 | @keyframes slideDown {
127 | from {
128 | transform: translateY(-10%);
129 | opacity: 0.3;
130 | }
131 | to {
132 | transform: translateY(0);
133 | }
134 | }
135 | div#changelogContainer > a.item::before {
136 | content: attr(data-sha);
137 | margin-right: 50px;
138 | }
139 | div#changelogContainer > a.item {
140 | width: 100%;
141 | display: flex;
142 | flex-direction: row;
143 | align-items: center;
144 | cursor: pointer;
145 | color: #000;
146 | text-decoration: none;
147 | }
148 |
149 | @media screen and (max-width: 576px) {
150 | div.btnContainer {
151 | display: flex;
152 | justify-content: center;
153 | align-items: center;
154 | flex-direction: column;
155 | }
156 | div.btnContainer > br {
157 | display: none;
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/sw.js:
--------------------------------------------------------------------------------
1 | import { CacheFirst, NetworkFirst } from 'workbox-strategies';
2 | import { registerRoute } from 'workbox-routing';
3 | import { precacheAndRoute } from 'workbox-precaching';
4 | import { cacheNames, clientsClaim, setCacheNameDetails } from 'workbox-core';
5 |
6 | setCacheNameDetails({
7 | prefix: 'phi',
8 | precache: 'precache',
9 | runtime: 'runtime',
10 | suffix: 'v1',
11 | });
12 |
13 | self.skipWaiting();
14 | clientsClaim();
15 |
16 | console.log(cacheNames);
17 |
18 | registerRoute(/\.(html)$/, new NetworkFirst());
19 | registerRoute(/\.(css|js)$/, new CacheFirst());
20 | registerRoute(
21 | /\.(css|js|mp3|wav|ogg|png|jpg|svg|webp)$/,
22 | new CacheFirst({
23 | cacheName: 'static-cache',
24 | })
25 | );
26 |
27 | registerRoute(/^https?:\/\/api\.(.*)/, new NetworkFirst());
28 |
29 | registerRoute(
30 | /alicdn\.com/,
31 | new CacheFirst({
32 | cacheName: 'alicdn-cache',
33 | })
34 | );
35 |
36 | precacheAndRoute(self.__WB_MANIFEST);
37 |
--------------------------------------------------------------------------------
/src/tapToStart/TapToStart.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/tapToStart/TapToStart.mp3
--------------------------------------------------------------------------------
/src/tapToStart/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | 点按以开始 - PhiCommunity
12 |
13 |
14 |
15 |
16 | 点 击 屏 幕 开 始
17 |
18 |
版本:未获取/获取失败
19 |
设备平台:未获取/获取失败
20 |
21 | 注意:此项目与厦门鸽游网络有限公司(Xiamen Pigeon Games Network
22 | Co., Ltd.)没有任何关系
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/tapToStart/index.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import tapToStart_mp3 from './TapToStart.mp3';
3 |
4 | window.addEventListener('DOMContentLoaded', () => {
5 | document.querySelector('#ver').innerText = $VERSION;
6 |
7 | try {
8 | document.querySelector('#device').innerText =
9 | 'Platform: ' +
10 | navigator.userAgentData.platform +
11 | ' ; isMobile:' +
12 | navigator.userAgentData.mobile;
13 | } catch (error) {
14 | document.querySelector('#device').innerText =
15 | 'User-Agent : ' +
16 | navigator.userAgent.slice(navigator.userAgent.lastIndexOf(' '));
17 | }
18 | document.querySelector('#device').title = navigator.userAgent;
19 | const actx = new (window.AudioContext ||
20 | window.webkitAudioContext ||
21 | window.mozAudioContext ||
22 | window.msAudioContext)();
23 | const abortController = new AbortController();
24 | fetch(tapToStart_mp3, abortController.signal)
25 | .then((res) => res.arrayBuffer())
26 | .then((arrayBuffer) => {
27 | actx.decodeAudioData(arrayBuffer, function (buffer) {
28 | var source = actx.createBufferSource();
29 | source.buffer = buffer;
30 | source.loop = true;
31 | source.connect(actx.destination);
32 | source.start(0);
33 | });
34 | });
35 | document.body.addEventListener('click', () => {
36 | console.log('Clicked! Redirecting to MainPage');
37 | var fadeInElem = document.createElement('div');
38 | fadeInElem.classList.add('fadeIn');
39 | document.body.appendChild(fadeInElem);
40 | setTimeout(() => {
41 | actx == undefined ? abortController.abort() : actx.close();
42 | if (window.localStorage.length == 0) {
43 | location.href = '../settings/index.html';
44 | } else {
45 | location.href = '../chapterSelect/index.html';
46 | }
47 | }, 510);
48 | });
49 | });
50 |
51 | if ('serviceWorker' in navigator) {
52 | window.addEventListener('load', () => {
53 | navigator.serviceWorker
54 | .register('../service-worker.js')
55 | .then((registration) => {
56 | console.log('SW registered: ', registration);
57 | })
58 | .catch((registrationError) => {
59 | console.log('SW registration failed: ', registrationError);
60 | });
61 | });
62 | }
63 |
--------------------------------------------------------------------------------
/src/tapToStart/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 | html {
3 | background: url(assets/images/ElementSqare.Half.Size.webp) center center
4 | no-repeat fixed;
5 | background-size: cover;
6 | box-sizing: border-box;
7 | height: 100%;
8 | backdrop-filter: blur(15px) brightness(0.5) contrast(0.5);
9 | overflow: hidden;
10 | }
11 | html::before {
12 | content: '';
13 | position: absolute;
14 | height: 100vh;
15 | width: 100vw;
16 | top: 0;
17 | left: 0;
18 | bottom: 0;
19 | right: 0;
20 | background: url(assets/images/ElementSqare.Half.Size.webp) no-repeat
21 | center center fixed;
22 | background-size: cover;
23 | filter: blur(10px) brightness(0.5);
24 | z-index: -1;
25 | }
26 | div.fadeIn {
27 | height: 100vh;
28 | width: 100vw;
29 | background-color: #000;
30 | position: fixed;
31 | top: 0;
32 | left: 0;
33 | z-index: 999;
34 | animation: fadeIn 0.6s ease-out;
35 | }
36 | @keyframes fadeIn {
37 | 0% {
38 | opacity: 0;
39 | }
40 | }
41 | body {
42 | display: flex;
43 | height: 100%;
44 | margin: 0;
45 | padding: 0;
46 | width: 100%;
47 | justify-content: center;
48 | align-items: center;
49 | flex-direction: column;
50 | z-index: 0;
51 | }
52 | img.title {
53 | height: 20%;
54 | width: auto;
55 | max-width: 80%;
56 | object-fit: scale-down;
57 | }
58 | div.tapToStart {
59 | margin: 30px;
60 | color: #fff;
61 | animation: flash 5s ease infinite;
62 | }
63 | @keyframes flash {
64 | 40% {
65 | opacity: 0.3;
66 | }
67 | 60% {
68 | opacity: 1;
69 | }
70 | }
71 | div.info {
72 | position: fixed;
73 | bottom: 0;
74 | color: #fff;
75 | display: flex;
76 | flex-direction: column;
77 | justify-content: center;
78 | align-items: center;
79 | }
80 | /*
81 | canvas#bubbles {
82 | pointer-events: none;
83 | position: fixed;
84 | top: 0;
85 | left: 0;
86 | width: 100%;
87 | height: 100%;
88 | z-index: 2;
89 | }
90 | */
91 |
--------------------------------------------------------------------------------
/src/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/utils/DB.js:
--------------------------------------------------------------------------------
1 | /*
2 | 测试代码:
3 | import DB from './DB.js';
4 | DB().createDB('test1', 'keyPath',['key1','key2'])
5 | .then((result) => {
6 | DB().createKey(result.objectStore, {keyPath:'t1-value1', key1:'t1-value2', key2:'t1-value3'});
7 | });
8 | DB().createDB('test2', 'keyPath',['key1','key2'])
9 | .then((result) => {
10 | DB().createKey(result.objectStore, {keyPath:'t2-value1', key1:'t2-value2', key2:'t2-value3'});
11 | });
12 | DB().openDB('test1').then((result) => {
13 | DB().readAllKeys(result.objectStore).then((res) =>
14 | console.log('ALL KEYS:', res.data)
15 | );
16 | DB().createKey(result.objectStore, {
17 | keyPath: 't1-value999',
18 | key1: 't1-value998',
19 | key2: 't1-value997',
20 | });
21 | DB().updateKey(result.objectStore, {
22 | keyPath: 't1-value1',
23 | key1: 't1-value666',
24 | key2: 't1-value667',
25 | });
26 | });
27 | */
28 | function DB() {
29 | /*
30 | @param {string} name
31 | @param {string} kPath
32 | @param {Array} indexes
33 |
34 | DB.createDB('MyTable','MyKeyPath',['Key1','Key2'])
35 | .then(res=>console.log(res));
36 | */
37 | function createDB(name, kPath, indexes) {
38 | return new Promise(function (resolve, reject) {
39 | const request = indexedDB.open(name);
40 |
41 | request.onerror = (e) => {
42 | reject({ result: 'failed', e: e, request: request });
43 | // console.error(e);
44 | };
45 | request.onupgradeneeded = (e) => {
46 | const db = e.target.result;
47 | const objectStore = db.createObjectStore(name, {
48 | keyPath: kPath,
49 | });
50 | objectStore.createIndex(kPath, kPath, {
51 | unique: true,
52 | });
53 | indexes.forEach((index) => {
54 | objectStore.createIndex(index, index, { unique: false });
55 | });
56 | resolve({
57 | result: 'success',
58 | database: db,
59 | objectStore: objectStore,
60 | });
61 | };
62 | });
63 | }
64 | /*
65 | @param {string} objStoreName
66 | */
67 | function openDB(name) {
68 | return new Promise(function (resolve, reject) {
69 | const request = indexedDB.open(name);
70 |
71 | request.onerror = (e) => {
72 | console.error(e);
73 | reject({ result: 'failed', e: e, request: request });
74 | };
75 | request.onsuccess = (e) => {
76 | if (!e.target.result.objectStoreNames.contains(name)) {
77 | e.target.result.close();
78 | indexedDB.deleteDatabase(name);
79 | reject({ result: 'failed', e: e, request: request });
80 | return;
81 | }
82 | const db = e.target.result;
83 | var transaction = db.transaction([name], 'readwrite');
84 | var objectStore = transaction.objectStore(name);
85 | resolve({
86 | result: 'success',
87 | database: db,
88 | objectStore: objectStore,
89 | });
90 | };
91 | });
92 | }
93 | /*
94 | @param {IDBObjectStore} objStore
95 | @param {object} data
96 | */
97 | function createKey(objStore, data) {
98 | return new Promise(function (resolve, reject) {
99 | var request = objStore.add(data);
100 |
101 | request.onsuccess = function () {
102 | resolve({
103 | result: 'success',
104 | objectStore: objStore,
105 | request: request,
106 | });
107 | // console.log('Success to add data to indexedDB.');
108 | };
109 |
110 | request.onerror = function () {
111 | reject({
112 | result: 'failed',
113 | objectStore: objStore,
114 | request: request,
115 | });
116 | // console.error('Failed to write into indexedDB.');
117 | };
118 | });
119 | }
120 | /*
121 | @param {IDBObjectStore} objStore
122 | @param {string} key
123 | */
124 | function readKey(objStore, key) {
125 | return new Promise((resolve, reject) => {
126 | var request = objStore.get(key);
127 | request.onerror = (e) => {
128 | reject({
129 | result: 'failed',
130 | objectStore: objStore,
131 | request: request,
132 | e: e,
133 | });
134 | // console.error('Failed to get the data from indexedDB.');
135 | };
136 |
137 | request.onsuccess = (e) => {
138 | resolve(request.result);
139 | resolve({
140 | result: 'success',
141 | value: request.result,
142 | objectStore: objStore,
143 | request: request,
144 | e: e,
145 | });
146 | };
147 | });
148 | }
149 |
150 | /*
151 | @param {IDBObjectStore} objStore
152 | @param {object} data
153 | */
154 | function updateKey(objStore, data) {
155 | return new Promise((resolve, reject) => {
156 | var request = objStore.put(data);
157 |
158 | request.onsuccess = function () {
159 | resolve({
160 | result: 'success',
161 | objectStore: objStore,
162 | request: request,
163 | });
164 | // console.log('Success.');
165 | };
166 |
167 | request.onerror = function () {
168 | reject({
169 | result: 'failed',
170 | objectStore: objStore,
171 | request: request,
172 | });
173 | // console.error('Failed to update into indexedDB.');
174 | };
175 | });
176 | }
177 |
178 | /*
179 | @param {IDBObjectStore} objectStore
180 | */
181 | function readAllKeys(objectStore) {
182 | return new Promise((resolve) => {
183 | const getAllKeysReq = objectStore.getAllKeys();
184 | getAllKeysReq.onsuccess = function (e) {
185 | const keyStr = e.target.result;
186 | const gettedKeys = [];
187 | keyStr.forEach((keys, index) => {
188 | const req = objectStore.get(keys);
189 | req.onsuccess = function (e) {
190 | gettedKeys.push(e.target.result);
191 | if (index == keyStr.length - 1) {
192 | resolve({
193 | result: 'success',
194 | data: gettedKeys,
195 | objectStore: objectStore,
196 | request: getAllKeysReq,
197 | e: e,
198 | });
199 | }
200 | };
201 | });
202 | };
203 | });
204 | }
205 |
206 | return {
207 | createDB,
208 | openDB,
209 | readAllKeys,
210 | readKey,
211 | updateKey,
212 | createKey,
213 | };
214 | }
215 |
216 |
217 | export { DB };
218 |
--------------------------------------------------------------------------------
/src/whilePlaying/assets/0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/0.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Back.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Drag.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Drag.ogg
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Drag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Drag.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/DragHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/DragHL.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Flick.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Flick.ogg
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Flick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Flick.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/FlickHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/FlickHL.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Hold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Hold.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/HoldEnd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/HoldEnd.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/HoldHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/HoldHL.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/HoldHead.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/HoldHead.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/HoldHeadHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/HoldHeadHL.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/JudgeLine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/JudgeLine.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Pause.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/ProgressBar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/ProgressBar.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Restart.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Resume.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/whilePlaying/assets/SongNameBar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/SongNameBar.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Tap.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Tap.ogg
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Tap.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/Tap2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/Tap2.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/TapHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/TapHL.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/clickRaw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/clickRaw.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/createImageBitmap.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";const t=["_imageBitmapOptions","resizeWidth","resizeHeight","resizeQuality","imageOrientation"],e=["_Blob","_ImageData","_SVGBlob","_SVGImageElement"],a=["resizeWidth","resizeHeight","resizeQuality","imageOrientation","premultiplyAlpha","colorSpaceConversion"],n={ResizeQuality:["pixelated","low","medium","high"],ImageOrientation:["none","flipY"]},i={COMMON_HEADER:"Failed to execute 'createImageBitmap': ",INVALID_STATE_IMAGE:"Provided image was in an invalid state.",ARGUMENT_COUNT_1:"At least one argument is required.",ARGUMENT_COUNT_N:"%s is not a valid argument count for any overload",CROP_RECT_ZERO:"The crop rect width passed to createImageBitmap must be nonzero",ALLOCATION_FAILED:"The ImageBitmap could not be allocated.",INVALID_SOURCE:"Argument 1 could not be converted to any of: HTMLImageElement, SVGImageElement, HTMLCanvasElement, HTMLVideoElement, ImageBitmap, Blob, CanvasRenderingContext2D, ImageData.",ENUM:"'%v' is not a valid value for enumeration %e",ALLOCATION:"The ImageBitmap couldn't be allocated."},r=["HTMLImageElement","SVGImageElement","HTMLVideoElement","HTMLCanvasElement","OffscreenCanvas","ImageBitmap"],o="http://www.w3.org/2000/svg",s="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";function c(t){try{return t.slice(0,0),!1}catch(t){return!0}}const h=new Int32Array(1);function l(t){return Number.isFinite(t)?(h[0]=t,h[0]):NaN}function g(t){const e=l(t);if(isNaN(e))throw new TypeError("Invalid long value");return e}function m(t,e){const a=Object.assign(document.createElement("canvas"),{width:t,height:e}).getContext("2d");if(t&&e&&!function(t){if(t.isContextLost)return t.translate(0,0),!t.isContextLost();let e=!1;try{t.fillRect(0,0,1,1),e=0!==t.getImageData(0,0,1,1).data[3]}finally{return t.clearRect(0,0,1,1),e}}(a))throw new DOMException(i.ALLOCATION,"InvalidStateError");return a}function u(t){return e=>{const a=globalThis[e];return a&&t instanceof a}}const d=globalThis.createImageBitmap,w=!!d&&((...t)=>d.call(globalThis,...t));function f(t){const e=new Image;return e.src=t,new Promise(((t,a)=>{e.onload=a=>{N(e),t(e)},e.onerror=t=>{const e=new DOMException(i.INVALID_STATE_IMAGE,"InvalidStateError");a(e)}}))}function p(t){return f(t.getAttribute("href")||t.getAttributeNS("http://www.w3.org/1999/xlink","href"))}const I=i.COMMON_HEADER;function E(t,e,a){const r=n[a],o=t[e];if(void 0!==o&&!r.includes(o)){const t=i.ENUM.replace("%v",o).replace("%e",a);throw new TypeError(t)}}function A(t,...e){let a,[n,r,o,s]=e.map(l);const c=(1===e.length?e[0]:e[4])||{},h="resizeWidth"in c&&g(c.resizeWidth),m="resizeHeight"in c&&g(c.resizeHeight);E(c,"resizeQuality","ResizeQuality"),E(c,"imageOrientation","ImageOrientation");const u=arguments.length;if(!u)throw new TypeError(I+i.ARGUMENT_COUNT_1);if(u>2&&u<5)throw i.ARGUMENT_COUNT_N.replace("%s",u),new TypeError(I+err_count_msg);if(u>=5){if(!o||!s)throw new RangeError(I+i.CROP_RECT_ZERO);n=n||0,r=r||0,a={sx:n,sy:r,sw:o,sh:s}}if(0===h||0===m){const t=I+i.ALLOCATION_FAILED;throw new DOMException(t,"InvalidStateError")}return{cropRect:a,...c}}function y(t,...e){const a=e.length;return t.has("_imageBitmapOptions")&&[2,6].includes(a)?e.slice(0,a-1):e}function T(t){const e=u(t);return e("HTMLImageElement")?{width:t.naturalWidth,height:t.naturalHeight}:e("HTMLVideoElement")?{width:t.videoWidth,height:t.videoHeight}:e("SVGImageElement")?{width:NaN,height:NaN}:e("HTMLCanvasElement")||e("OffscreenCanvas")||e("ImageBitmap")||e("ImageData")?t:{width:NaN,height:NaN}}function b(t,e){const a=function(t,e){const{width:a,height:n}=T(t),{resizeWidth:i,resizeHeight:r}=e,o=e.cropRect||{sx:0,sy:0,sw:a,sh:n},s={dx:0,dy:0,dw:i||Math.abs(o.sw),dh:r||Math.abs(o.sh)},c=function(t,e,{sx:a,sy:n,sw:i,sh:r},{dx:o,dy:s,dw:c,dh:h}){i<0&&(a+=i,i=Math.abs(i)),r<0&&(n+=r,r=Math.abs(r)),c<0&&(o+=c,c=Math.abs(c)),h<0&&(s+=h,h=Math.abs(h));const l=Math.max(a,0),g=Math.min(a+i,t),m=Math.max(n,0),u=Math.min(n+r,e),d=c/i,w=h/r;return{sx:l,sy:m,sw:g-l,sh:u-m,dx:a<0?o-a*d:o,dy:n<0?s-n*w:s,dw:(g-l)*d,dh:(u-m)*w}}(a,n,o,s);return c.resizeQuality=e.resizeQuality,c.flipY="flipY"===e.imageOrientation,c.width=s.dw,c.height=s.dh,c}(t,e),{height:n}=T(t),{sx:i,sy:r,sw:o,sh:s,dx:c,dy:h,dw:l,dh:g,flipY:u,resizeQuality:d}=a,w=m(a.width,a.height),f=w.canvas;return"pixelated"===d?w.imageSmoothingEnabled=!1:d&&(w.imageSmoothingQuality=d),w.drawImage(t,i,r,o,s,c,h,l,g),u&&(w.globalCompositeOperation="copy",w.setTransform(1,0,0,-1,0,f.height),w.drawImage(f,0,0)),f}async function O(t,e){const a=globalThis.URL.createObjectURL(t),n=await f(a);return globalThis.URL.revokeObjectURL(t),b(n,e)}async function M(t,e){const{resizeWidth:a,resizeHeight:n,resizeQuality:i,imageOrientation:r}=e,o=e.cropRect||{},s=o.sx||0,c=o.sy||0,h=o.sw||t.width,l=o.sh||t.height,g=h<0?-1*h-s:-s,u=l<0?-1*l-c:-c,d=m(Math.abs(h),Math.abs(l));d.putImageData(t,g,u,s,c,h,l);const w="flipY"===r,f=a||w;let p;a?(p=m(a,n),"pixelated"===i?p.imageSmoothingEnabled=!1:p.imageSmoothingQuality=i):(p=d,d.globalCompositeOperation="copy");const I=d.canvas,E=p.canvas;return w&&p.setTransform(1,0,0,-1,0,E.height),f&&p.drawImage(I,0,0,I.width,I.height,0,0,E.width,E.height),E}function _(){const t=i.COMMON_HEADER+i.INVALID_STATE_IMAGE;throw new DOMException(t,"InvalidStateError")}function N(t){const e=u(t);if(e("HTMLCanvasElement")||e("OffscreenCanvas")||e("ImageBitmap"))0!==t.width&&0!==t.height||_();else if(e("HTMLImageElement")||e("HTMLVideoElement")||e("SVGImageElement")){const{width:e,height:a}=T(t);0!==e&&0!==a||_();let n=!1;try{n=!m(0,0).createPattern(t,"no-repeat")}catch(t){return"maybe"}n&&_()}return!0}let v=!1;const L=function(){if(!w){const a=t.concat(e);return new Set(a)}const a=[C()],n=new Set(a.flat()),i=t.filter((t=>!n.has(t)));return new Set(i)}(),S=async function(){if(!w)return v=!0,L;const t=[B(),x(),D(),R()],a=await Promise.all(t),n=new Set(a.flat());return e.filter((t=>!n.has(t))).forEach((t=>L.add(t))),v=!0,L}();function C(){const t=[],e={};return a.forEach((a=>{Object.defineProperty(e,a,{get(){t.push(a)}})})),w(new ImageData(1,1),e).then((t=>t.close())).catch((()=>{})),t.length&&t.push("_imageBitmapOptions"),t}async function B(){const t=[],e=await fetch(s).then((t=>t.ok&&t.blob()));try{1===(await w(e)).width&&t.push("_Blob")}catch(t){console.log(t)}return t}async function x(){const t=[],e=function(t,e){try{return new ImageData(1,1)}catch(t){return create2dContext(0,0).createImageData(1,1)}}();try{1===(await w(e)).width&&t.push("_ImageData")}catch(t){}return t}async function D(){const t=[],e=new Blob([`\n \n `],{type:"image/svg+xml"});try{1===(await w(e)).width&&t.push("_SVGBlob")}catch(t){}return t}async function R(){const t=[];if(!("SVGImageElement"in globalThis))return t;const e=t=>new Promise((e=>setTimeout(e,t))),a=document.createElementNS(o,"image");a.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",s);const n=()=>{try{return N(a),!1}catch(t){return!0}};let i=0;do{await e(10)}while(n()&&++i<300);try{await w(a),t.push("_SVGImageElement")}catch(t){console.error(t)}return t}function H(t,e,a){const n=u(a);if(n("Blob")){if(t.has("_Blob"))return!0;if(a?.type,t.has("_SVGBlob"))return!0}return!(!n("ImageData")||!t.has("_ImageData"))||!(!n("SVGImageElement")||!t.has("_SVGImageElement"))||!!Object.keys(e).some((e=>t.has(e)))}function z(t,e){if(v)return!1;const a=u(t);return["Blob","ImageData","SVGImageElement"].some(a)}const V=t=>Object.getOwnPropertyDescriptor(HTMLCanvasElement.prototype,t);function G(){throw new TypeError("Illegal Constructor")}function U(t){return t instanceof globalThis.ImageBitmap?t:w?w(t):function(t){return t instanceof HTMLCanvasElement||(t=b(t)),Object.setPrototypeOf(t,G.prototype),t}(t)}G.prototype=Object.create({close(){V("width").set.call(this,0),V("height").set.call(this,0)},get width(){return V("width").get.call(this)},get height(){return V("height").get.call(this)},get[Symbol.toStringTag](){return"ImageBitmap"}}),w||(globalThis.ImageBitmap=G),globalThis.createImageBitmap=async function(t,...e){const a=A(...arguments),n=z(t)?await S:L;if(0===n.size)return w(...arguments);if(w&&!H(n,a,t))return w(...y(n,...arguments));const o=u(t);let s;if(r.some(o)?(N(t),o("SVGImageElement")&&(t=await p(t)),o("HTMLImageElement")&&(t.naturalWidth||t.naturalHeight||a.resizeWidth&&a.resizeHeight||_()),s=b(t,a)):o("Blob")?s=await O(t,a):o("ImageData")&&(c(t.data.buffer)&&_(),s=await M(t,a)),!s)throw new TypeError(i.INVALID_SOURCE);return await U(s)},globalThis.createImageBitmap._original=w})();
--------------------------------------------------------------------------------
/src/whilePlaying/assets/mute.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/mute.ogg
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/Drag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/Drag.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/Drag2HL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/Drag2HL.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/Flick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/Flick.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/Flick2HL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/Flick2HL.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/HoldBody.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/HoldBody.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/HoldEnd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/HoldEnd.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/Tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/Tap.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/Tap2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/Tap2.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/Tap2HL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/Tap2HL.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/oldui/clickRaw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yuameshi/PhiCommunity/3f1cbb5d03cbe7e3976a6492fda5c131ddd3b14d/src/whilePlaying/assets/oldui/clickRaw.png
--------------------------------------------------------------------------------
/src/whilePlaying/assets/stackblur.min.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).StackBlur={})}(this,(function(t){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var r=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259],n=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];function a(t,r,n,a,o){if("string"==typeof t&&(t=document.getElementById(t)),!t||"object"!==e(t)||!("getContext"in t))throw new TypeError("Expecting canvas with `getContext` method in processCanvasRGB(A) calls!");var i=t.getContext("2d");try{return i.getImageData(r,n,a,o)}catch(t){throw new Error("unable to access image data: "+t)}}function o(t,e,r,n,o,f){if(!(isNaN(f)||f<1)){f|=0;var g=a(t,e,r,n,o);g=i(g,e,r,n,o,f),t.getContext("2d").putImageData(g,e,r)}}function i(t,e,a,o,i,f){for(var g,l=t.data,c=2*f+1,s=o-1,v=i-1,b=f+1,x=b*(b+1)/2,d=new u,y=d,h=1;h>E;if(l[B+3]=Y,0!==Y){var Z=255/Y;l[B]=(O*C>>E)*Z,l[B+1]=(P*C>>E)*Z,l[B+2]=(q*C>>E)*Z}else l[B]=l[B+1]=l[B+2]=0;O-=k,P-=H,q-=_,z-=M,k-=p.r,H-=p.g,_-=p.b,M-=p.a;var $=X+f+1;$=w+($>E,ut>0?(ut=255/ut,l[Nt]=(bt*C>>E)*ut,l[Nt+1]=(xt*C>>E)*ut,l[Nt+2]=(dt*C>>E)*ut):l[Nt]=l[Nt+1]=l[Nt+2]=0,bt-=lt,xt-=ct,dt-=st,yt-=vt,lt-=p.r,ct-=p.g,st-=p.b,vt-=p.a,Nt=ot+((Nt=St+b)>E,l[S+1]=k*C>>E,l[S+2]=H*C>>E,W-=T,k-=j,H-=A,T-=w.r,j-=w.g,A-=w.b,p=I+((p=z+f+1)>E,l[p+1]=Y*C>>E,l[p+2]=Z*C>>E,X-=Q,Y-=U,Z-=V,Q-=w.r,U-=w.g,V-=w.b,p=F+((p=ot+b)
2 |
3 |
4 |
5 |
6 |
7 |
8 | 游玩 - PhiCommunity
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/src/whilePlaying/resource.js:
--------------------------------------------------------------------------------
1 | import JudgeLine from './assets/JudgeLine.png';
2 | import ProgressBar from './assets/ProgressBar.png';
3 | import SongsNameBar from './assets/SongNameBar.png';
4 | import Pause from './assets/Pause.png';
5 | import clickRaw from './assets/clickRaw.png';
6 | import Tap from './assets/Tap.png';
7 | import Tap2 from './assets/Tap2.png';
8 | import TapHL from './assets/TapHL.png';
9 | import Drag from './assets/Drag.png';
10 | import DragHL from './assets/DragHL.png';
11 | import HoldHead from './assets/HoldHead.png';
12 | import HoldHeadHL from './assets/HoldHeadHL.png';
13 | import Hold from './assets/Hold.png';
14 | import HoldHL from './assets/HoldHL.png';
15 | import HoldEnd from './assets/HoldEnd.png';
16 | import Flick from './assets/Flick.png';
17 | import FlickHL from './assets/FlickHL.png';
18 | import NoImage from './assets/0.png';
19 | import mute from './assets/mute.ogg';
20 | import HitSong0 from './assets/Tap.ogg';
21 | import HitSong1 from './assets/Drag.ogg';
22 | import HitSong2 from './assets/Flick.ogg';
23 |
24 | import clickRaw_old from './assets/oldui/clickRaw.png';
25 | import Drag_old from './assets/oldui/Drag.png';
26 | import DragHL_old from './assets/oldui/Drag2HL.png';
27 | import Flick_old from './assets/oldui/Flick.png';
28 | import FlickHL_old from './assets/oldui/Flick2HL.png';
29 | import Hold_old from './assets/oldui/HoldBody.png';
30 | import HoldHL_old from './assets/oldui/HoldBody.png';
31 | import HoldHead_old from './assets/oldui/Tap.png';
32 | import HoldHeadHL_old from './assets/oldui/Tap2HL.png';
33 | import HoldEnd_old from './assets/oldui/HoldEnd.png';
34 | import Tap_old from './assets/oldui/Tap.png';
35 | import Tap2_old from './assets/oldui/Tap2.png';
36 | import TapHL_old from './assets/oldui/Tap2HL.png';
37 |
38 | let resource = {
39 | JudgeLine,
40 | ProgressBar,
41 | SongsNameBar,
42 | Pause,
43 | clickRaw,
44 | Tap,
45 | Tap2,
46 | TapHL,
47 | Drag,
48 | DragHL,
49 | HoldHead,
50 | HoldHeadHL,
51 | Hold,
52 | HoldHL,
53 | HoldEnd,
54 | Flick,
55 | FlickHL,
56 | NoImage,
57 | mute,
58 | HitSong0,
59 | HitSong1,
60 | HitSong2,
61 | };
62 | if (localStorage.getItem('useOldUI') == 'true') {
63 | resource = {
64 | ...resource,
65 | clickRaw: clickRaw_old,
66 | Drag: Drag_old,
67 | DragHL: DragHL_old,
68 | Flick: Flick_old,
69 | FlickHL: FlickHL_old,
70 | Hold: Hold_old,
71 | HoldHL: HoldHL_old,
72 | HoldHead: HoldHead_old,
73 | HoldHeadHL: HoldHeadHL_old,
74 | HoldEnd: HoldEnd_old,
75 | Tap: Tap_old,
76 | Tap2: Tap2_old,
77 | TapHL: TapHL_old,
78 | };
79 | }
80 |
81 | export default resource;
82 |
--------------------------------------------------------------------------------
/src/whilePlaying/style.css:
--------------------------------------------------------------------------------
1 | @import url(assets/css/css.css);
2 | html {
3 | height: 100%;
4 | width: 100%;
5 | margin: 0;
6 | padding: 0;
7 | }
8 | body {
9 | background: var(--background) center center no-repeat;
10 | background-size: cover !important;
11 | backdrop-filter: blur(15px) contrast(0.6) brightness(0.6);
12 | height: 100%;
13 | width: 100%;
14 | margin: 0;
15 | padding: 0;
16 | overflow: hidden;
17 | display: flex;
18 | justify-content: center;
19 | align-items: center;
20 | -moz-user-select: none;
21 | -o-user-select: none;
22 | -khtml-user-select: none;
23 | -user-select: none;
24 | -ms-user-select: none;
25 | user-select: none;
26 | /*transition: all 0.3s linear;*/
27 | }
28 | body::before {
29 | content: '';
30 | position: fixed;
31 | top: 0;
32 | left: 0;
33 | right: 0;
34 | bottom: 0;
35 | background: var(--background) center center no-repeat fixed;
36 | background-size: cover;
37 | filter: blur(15px) contrast(0.6) brightness(0.6);
38 | z-index: -1;
39 | }
40 | canvas.canvas#canvas {
41 | height: 100% !important;
42 | width: auto !important;
43 | background-color: transparent;
44 | }
45 | .loadingEmbedFrame {
46 | position: fixed;
47 | height: 100%;
48 | width: 100%;
49 | left: 0;
50 | top: 0;
51 | right: 0;
52 | bottom: 0;
53 | border: none;
54 | transform: none;
55 | object-fit: cover;
56 | z-index: 999999999;
57 | background: #999;
58 | }
59 | div.tapToStartFrame {
60 | position: fixed;
61 | top: 0;
62 | left: 50%;
63 | transform: translateX(-50%);
64 | height: 100%;
65 | width: 100%;
66 | display: flex;
67 | flex-direction: column;
68 | justify-content: center;
69 | align-items: center;
70 | color: #fff;
71 | text-shadow: 0 0 5px #000;
72 | }
73 | div.tapToStartFrame > div.songName {
74 | font-size: 2.3rem;
75 | }
76 | div.tapToStartFrame > div.judgeLine {
77 | height: 0.7%;
78 | width: 100%;
79 | margin: 50px 0;
80 | background-color: #fff;
81 | }
82 | div.tapToStartFrame > div.detail {
83 | font-size: 1.2rem;
84 | text-align: center;
85 | }
86 | .hide {
87 | display: none;
88 | }
89 | div.pauseOverlay {
90 | display: none;
91 | }
92 | div.pauseOverlay.visable {
93 | height: 100vh;
94 | width: 100vw;
95 | z-index: 999;
96 | /* filter: contrast(.6)brightness(.6); */
97 | backdrop-filter: contrast(0.6) brightness(0.6);
98 | display: flex;
99 | position: fixed;
100 | top: 0;
101 | left: 0;
102 | justify-content: center;
103 | align-items: center;
104 | flex-direction: row;
105 | /* filter: blur(75px); */
106 | backdrop-filter: blur(15px);
107 | font-size: 3rem;
108 | color: #fff;
109 | animation: pauseOverlayBlur .1s linear;
110 | }
111 | @keyframes pauseOverlayBlur {
112 | 0% {
113 | backdrop-filter: blur(0px);
114 | }
115 | 100% {
116 | backdrop-filter: blur(15px);
117 | }
118 | }
119 | div.pauseOverlay.visable.readyToResume > div.resumeText {
120 | height: 3rem;
121 | width: 9rem;
122 | font-size: 3rem;
123 | display: flex;
124 | justify-content: center;
125 | align-items: center;
126 | background-image: linear-gradient(
127 | to right,
128 | transparent,
129 | white,
130 | transparent
131 | );
132 | -webkit-background-clip: text;
133 | background-clip: text;
134 | color: transparent;
135 | }
136 | div.pauseOverlay.visable.readyToResume > div.resumeText::before {
137 | font-size: 3rem;
138 | content: '\3000\3000 3 \3000\3000 2 \3000\3000 1 \3000\3000';
139 | animation: resumeTextAnimation 3s linear;
140 | white-space: nowrap;
141 | margin-left: -175%;
142 | }
143 | @keyframes resumeTextAnimation {
144 | 0% {
145 | margin-left: 175%;
146 | }
147 | 30% {
148 | margin-left: 175%;
149 | }
150 | 33% {
151 | margin-left: 0%;
152 | }
153 | 60% {
154 | margin-left: 0%;
155 | }
156 | 66% {
157 | margin-left: -175%;
158 | }
159 | }
160 | div.pauseOverlay.visable.readyToResume {
161 | animation: pauseOverlayFadeOut 3s linear;
162 | }
163 | @keyframes pauseOverlayFadeOut {
164 | 0% {
165 | backdrop-filter: blur(15px);
166 | }
167 | 60% {
168 | backdrop-filter: blur(0px);
169 | }
170 | 100% {
171 | backdrop-filter: blur(0px);
172 | }
173 | }
174 | div#backInPlayingBtn {
175 | background: url(./assets/Back.svg) center center no-repeat;
176 | background-size: 85%;
177 | }
178 | div#restartBtn {
179 | background: url(./assets/Restart.svg) center center no-repeat;
180 | background-size: 85%;
181 | }
182 | div#resumeBtn {
183 | background: url(./assets/Resume.svg) center center no-repeat;
184 | background-size: 85%;
185 | }
186 | div#backInPlayingBtn,
187 | div#restartBtn,
188 | div#resumeBtn {
189 | height: 50px;
190 | width: 50px;
191 | margin: 25px;
192 | /* background-color: #000; */
193 | }
194 |
195 | img.flick,
196 | img.tap,
197 | img.drag {
198 | width: 100px;
199 | height: 20px;
200 | border-radius: 999px;
201 | }
202 | .key2HL {
203 | box-shadow: 0 0 7.5px 3px #f0ed69, inset 0 0 20px 5px #f0ed69;
204 | }
205 | img.hitState {
206 | border-right: 20px solid transparent;
207 | height: 100px;
208 | width: 100px;
209 | object-fit: cover;
210 | object-position: center 0px;
211 | animation: splash 1s linear;
212 | }
213 | @keyframes splash {
214 | 0% {
215 | object-position: center 0px;
216 | }
217 | 25% {
218 | object-position: center 256px;
219 | }
220 | 75% {
221 | object-position: center 512px;
222 | }
223 | 100% {
224 | /* object-position: center 0px; */
225 | }
226 | }
227 | img.hitState.perfect {
228 | filter: drop-shadow(#f0ed69 1px 1px);
229 | }
230 | img.hitState.good {
231 | filter: drop-shadow(#0ac3ff 1px 1px);
232 | }
233 | /*
234 | div.judgeLine{
235 | position: absolute;
236 | height: 5px;
237 | width: 1500px;
238 | background-color: #fff;
239 | animation: testAnim 5s linear infinite;
240 | }
241 | @keyframes testAnim {
242 | 0%{
243 | transform: rotate(0deg);
244 | }
245 | 0%{
246 | transform: rotate(360deg);
247 | }
248 | } */
249 |
--------------------------------------------------------------------------------
/src/whilePlaying/tutorial.js:
--------------------------------------------------------------------------------
1 | function showText(text, showTime) {
2 | const container = document.createElement('div');
3 | container.style.display = 'flex';
4 | container.style.flexDirection = 'column';
5 | container.style.justifyContent = 'center';
6 | container.style.textAlign = 'center';
7 | container.style.pointerEvents = 'none';
8 | container.style.alignItems = 'center';
9 | container.style.position = 'fixed';
10 | container.style.top = '0';
11 | container.style.left = '0';
12 | container.style.right = '0';
13 | container.style.bottom = '0';
14 | container.style.transition = 'all linear 0.3s';
15 | setTimeout(() => {
16 | container.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
17 | }, 100);
18 | container.style.zIndex = '9999';
19 | container.style.color = 'white';
20 | container.style.width = '100vw';
21 | container.style.height = '100vh';
22 | container.innerHTML = text;
23 | document.body.appendChild(container);
24 | const clearTextTimeOut = setTimeout(() => {
25 | container.remove();
26 | clearTimeout(clearTextTimeOut);
27 | }, showTime || Infinity);
28 | }
29 | function drawTutorial(stage) {
30 | switch (stage) {
31 | case 0:
32 | showText(
33 | '欢迎来到PhiCommunity! 现在将进行基本的游戏玩法教学。',
34 | 2000
35 | );
36 | break;
37 | case 1:
38 | showText(
39 | '左上角是暂停按钮,右上角是您的分数 下方是歌曲和难度信息 屏幕中央的是判定线 音符将落在判定线上',
40 | 2000
41 | );
42 | break;
43 | case 2:
44 | showText('首先是Tap音符 当它们落到判定线上时点击它们', 1000);
45 | break;
46 | case 3:
47 | showText(
48 | '接下来是Hold音符 当它们落到判定线上时长按它们直至结束',
49 | 2000
50 | );
51 | break;
52 | case 4:
53 | showText('然后是Drag音符 当它们落到判定线上时接住它们', 3000);
54 | break;
55 | case 5:
56 | showText(
57 | '最后是Flick音符 当它们落到判定线上向任意方向滑动',
58 | 4000
59 | );
60 | break;
61 | case 6:
62 | showText('让我们把所有的元素综合起来', 2000);
63 | break;
64 | case 7:
65 | showText('判定线还会旋转,移动,显现,消失 还有另一面可以判定', 4000);
66 | break;
67 | case 8:
68 | showText(
69 | '最后,希望你在PhiCommunity游玩愉快 有任何Bug及时到GitHub反馈噢',
70 | 2000
71 | );
72 | break;
73 |
74 | default:
75 | break;
76 | }
77 | }
78 |
79 | function renderTutorialByTime(time) {
80 | time = parseInt(time) - 3;
81 | if (time > 0 && time < 2) {
82 | if (window.tutorialStage == undefined) {
83 | drawTutorial(0);
84 | window.tutorialStage = 1;
85 | }
86 | }
87 | if (time > 2 && time < 4) {
88 | if (window.tutorialStage == 1) {
89 | drawTutorial(1);
90 | window.tutorialStage = 2;
91 | }
92 | }
93 | if (time > 4 && time < 6) {
94 | if (window.tutorialStage == 2) {
95 | drawTutorial(2);
96 | window.tutorialStage = 3;
97 | }
98 | }
99 | if (time > 18 && time < 20.5) {
100 | if (window.tutorialStage == 3) {
101 | drawTutorial(3);
102 | window.tutorialStage = 4;
103 | }
104 | }
105 | if (time > 38 && time < 43) {
106 | if (window.tutorialStage == 4) {
107 | drawTutorial(4);
108 | window.tutorialStage = 5;
109 | }
110 | }
111 | if (time > 54 && time < 60) {
112 | if (window.tutorialStage == 5) {
113 | drawTutorial(5);
114 | window.tutorialStage = 6;
115 | }
116 | }
117 | if (time > 60 && time < 70) {
118 | if (window.tutorialStage == 6) {
119 | drawTutorial(7);
120 | window.tutorialStage = 7;
121 | }
122 | }
123 | if (time > 78.5 && time < 82) {
124 | if (window.tutorialStage == 7) {
125 | drawTutorial(6);
126 | window.tutorialStage = 8;
127 | }
128 | }
129 | if (time > 122 && time < Infinity) {
130 | if (window.tutorialStage == 8) {
131 | drawTutorial(8);
132 | window.tutorialStage = 9;
133 | }
134 | }
135 | }
136 | function drawTutorialSP(stage) {
137 | switch (stage) {
138 | case 0:
139 | showText(
140 | '欢迎来到PhiCommunity! 现在将进行基本的 游戏玩法教学。
',
141 | 2000
142 | );
143 | break;
144 | case 1:
145 | showText(
146 | '左上角是暂停按钮,右上角是您的分数 下方是歌曲和难度信息 屏幕中央的是判定线 音符将落在判定线上',
147 | 2000
148 | );
149 | break;
150 | case 2:
151 | showText('首先是Tap音符 当它们落到判定线上时点击它们', 1000);
152 | break;
153 | case 3:
154 | showText('然后是Drag音符 当它们落到判定线上时接住它们', 2000);
155 | break;
156 | case 4:
157 | showText(
158 | '接下来是Hold音符 当它们落到判定线上时长按它们直至结束',
159 | 2000
160 | );
161 | break;
162 | case 5:
163 | showText('那先来熟悉一下这三种音符吧', 4000);
164 | break;
165 | case 6:
166 | showText(
167 | '最后是Flick音符 当它们落到判定线上向任意方向滑动',
168 | 2000
169 | );
170 | break;
171 | case 7:
172 | showText(
173 | '判定线还会旋转,移动,显现,消失 还有另一面可以判定',
174 | 2000
175 | );
176 | break;
177 | case 8:
178 | showText('让我们把所有的元素综合起来', 2000);
179 | break;
180 | case 9:
181 | showText('来试试判定线的另一面', 2000);
182 | break;
183 | case 10:
184 | showText(
185 | '最后,希望你在PhiCommunity游玩愉快 有任何Bug及时到GitHub反馈噢',
186 | 2000
187 | );
188 | break;
189 |
190 | default:
191 | break;
192 | }
193 | }
194 | function renderTutorialSPByTime(time) {
195 | time = parseInt(time) - 3;
196 | if (time > 0 && time < 2) {
197 | if (window.tutorialStage == undefined) {
198 | drawTutorialSP(0);
199 | window.tutorialStage = 1;
200 | }
201 | }
202 | if (time > 2 && time < 4) {
203 | if (window.tutorialStage == 1) {
204 | drawTutorialSP(1);
205 | window.tutorialStage = 2;
206 | }
207 | }
208 | if (time > 4 && time < 6) {
209 | if (window.tutorialStage == 2) {
210 | drawTutorialSP(2);
211 | window.tutorialStage = 3;
212 | }
213 | }
214 | if (time > 18.5 && time < 20) {
215 | if (window.tutorialStage == 3) {
216 | drawTutorialSP(3);
217 | window.tutorialStage = 4;
218 | }
219 | }
220 | if (time > 29.5 && time < 30.5) {
221 | if (window.tutorialStage == 4) {
222 | drawTutorialSP(4);
223 | window.tutorialStage = 5;
224 | }
225 | }
226 | if (time > 30.5 && time < 32.5) {
227 | if (window.tutorialStage == 5) {
228 | drawTutorialSP(5);
229 | window.tutorialStage = 6;
230 | }
231 | }
232 | if (time > 82 && time < 86) {
233 | if (window.tutorialStage == 6) {
234 | drawTutorialSP(6);
235 | window.tutorialStage = 7;
236 | }
237 | }
238 | if (time > 84 && time < 86) {
239 | if (window.tutorialStage == 7) {
240 | drawTutorialSP(7);
241 | window.tutorialStage = 8;
242 | }
243 | }
244 | if (time > 104 && time < 106) {
245 | if (window.tutorialStage == 8) {
246 | drawTutorialSP(8);
247 | window.tutorialStage = 9;
248 | }
249 | }
250 | if (time > 117 && time < 120) {
251 | if (window.tutorialStage == 9) {
252 | drawTutorialSP(9);
253 | window.tutorialStage = 10;
254 | }
255 | }
256 | if (time > 148 && time < 150) {
257 | if (window.tutorialStage == 10) {
258 | drawTutorialSP(10);
259 | window.tutorialStage = 11;
260 | }
261 | }
262 | }
263 |
264 | export { renderTutorialSPByTime, renderTutorialByTime };
265 |
--------------------------------------------------------------------------------