├── .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 | 21 | 22 | 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 |
23 |
24 |
25 |
26 |
27 |
28 | 34 |
35 |
36 |
37 |
38 |
39 |
1000000
40 | 46 |
47 |
48 |
609
49 |
100%
50 |
51 |
52 |
609
53 |
0
54 |
0
55 |
0
56 |
57 |
0
58 |
0
59 |
60 |
61 |
62 |
63 |
64 |
本曲最终RKS:0
65 |
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 |
16 |
17 |
操作 
18 |
19 |
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 |
17 |
获取谱面
18 |
缓存管理
19 |
20 |
21 |
设置
22 |
上传谱面
23 |
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 |
14 |
15 |
16 |
17 |
18 |
获取谱面
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
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 | 25 | 26 | 27 |
28 | 29 | 30 | 31 | 32 |
33 |
34 |
35 |
36 |

变更记录(仅显示最近30次)

37 | 点击此处查看所有提交 43 | 变更情况总览 47 |

48 |
49 |
50 |
51 |
52 |

设备要求

53 | 54 | 55 | 56 | 61 | 62 | 63 | 64 | 68 | 69 | 70 | 71 | 74 | 75 | 76 | 77 | 80 | 81 |
Windows 57 | Chromium内核为90以上(经测试测试MSEdge 58 | 98无问题),FireFox内核90+(经测试测试FireFox98(Iceraven 59 | 1.15)无问题) 60 |
Android 65 | Chromium内核为90以上(经测试测试MSEdge 66 | 97无问题),FireFox内核90+(经测试测试FireFox98无问题) 67 |
iOS 72 | iOS版本15以上,由于Apple系统限制,您无法通过安装其他浏览器解决问题 73 |
macOS 78 | Safari版本15以上,或者安装其他浏览器,要求参见Windows部分 79 |
82 |
83 |
84 |
85 |
86 |

联系

87 |
92 |
93 | 94 | 95 |
96 |
97 | 98 | 99 |
100 |
101 | 102 | 107 |
108 |
    109 |
  • 110 | 115 |
  • 116 |
  • 117 |
118 |
119 |
120 |
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 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 23 |
24 |
25 |
26 |
27 |
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 |
18 |
19 |
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 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
30 |
默认
31 |
35 | 设置 36 |
37 |
38 | 39 |
40 |
41 |
42 | 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
0000000
58 |
59 |
60 |
61 |
62 |
63 | 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 | PhiCommunity 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 |
22 |
23 |
24 |
25 |
26 | 36 |
37 | 38 | 39 | 40 | 50 | 57 | 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 | --------------------------------------------------------------------------------