diff --git a/src/App.tsx b/src/App.tsx index 54fb4a9..7e0f682 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,6 +5,7 @@ import { LAST_LEVEL_STORAGE_KEY, LAST_SCORE_STORAGE_KEY, LAST_TIME_STORAGE_KEY, + PLAYING_THEME_ID_STORAGE_KEY, wrapThemeDefaultSounds, } from './utils'; import { Theme } from './themes/interface'; @@ -30,6 +31,7 @@ const App: FC<{ theme: Theme }> = ({ theme: initTheme }) => { const [diyDialogShow, setDiyDialogShow] = useState(false); const changeTheme = (theme: Theme) => { + sessionStorage.setItem(PLAYING_THEME_ID_STORAGE_KEY, theme.title); wrapThemeDefaultSounds(theme); domRelatedOptForTheme(theme); setTheme({ ...theme }); diff --git a/src/components/Fireworks.module.scss b/src/components/Fireworks.module.scss new file mode 100644 index 0000000..a860ddc --- /dev/null +++ b/src/components/Fireworks.module.scss @@ -0,0 +1,133 @@ +@use 'sass:math'; + +// 烟花动画,来源 https://codepen.io/yshlin/pen/WNMmQX + +$particles: 50; +$width: 500; +$height: 500; + +// Create the explosion... +$box-shadow: (); +$box-shadow2: (); +@for $i from 0 through $particles { + $box-shadow: $box-shadow, + math.random($width) - + math.div($width, 2) + + px + math.random($height) - + math.div($height, 1.2) + + px + hsl(math.random(360) 100% 50%); + $box-shadow2: $box-shadow2, 0 0 #fff; +} +@mixin keyframes($animationName) { + @keyframes #{$animationName} { + @content; + } + + @keyframes #{$animationName} { + @content; + } + + @keyframes #{$animationName} { + @content; + } + + @keyframes #{$animationName} { + @content; + } + + @keyframes #{$animationName} { + @content; + } +} + +@mixin animation-delay($settings) { + animation-delay: $settings; +} + +@mixin animation-duration($settings) { + animation-duration: $settings; +} + +@mixin animation($settings) { + animation: $settings; +} + +@mixin transform($settings) { + transform: $settings; +} + +.pyro { + position: absolute; + left: 0; + top: 0; + width: 100%; +} + +.pyro > .before, +.pyro > .after { + position: absolute; + width: 10px; + height: 10px; + border-radius: 50%; + box-shadow: $box-shadow2; + @include animation( + ( + 1s bang ease-out infinite backwards, + 1s gravity ease-in infinite backwards, + 5s position linear infinite backwards + ) + ); +} + +.pyro > .after { + @include animation-delay((1.25s, 1.25s, 1.25s)); + @include animation-duration((1.25s, 1.25s, 6.25s)); +} + +@include keyframes(bang) { + to { + box-shadow: $box-shadow; + } +} + +@include keyframes(gravity) { + to { + @include transform(translateY(200px)); + + opacity: 0; + } +} + +@include keyframes(position) { + 0%, + 19.9% { + margin-top: 10%; + margin-left: 40%; + } + + 20%, + 39.9% { + margin-top: 40%; + margin-left: 30%; + } + + 40%, + 59.9% { + margin-top: 20%; + margin-left: 70%; + } + + 60%, + 79.9% { + margin-top: 30%; + margin-left: 20%; + } + + 80%, + 99.9% { + margin-top: 30%; + margin-left: 80%; + } +} diff --git a/src/components/Fireworks.tsx b/src/components/Fireworks.tsx new file mode 100644 index 0000000..d6e541c --- /dev/null +++ b/src/components/Fireworks.tsx @@ -0,0 +1,12 @@ +import React, { FC } from 'react'; +import style from './Fireworks.module.scss'; +const Fireworks: FC = () => { + return ( +
+
+
+
+ ); +}; + +export default Fireworks; diff --git a/src/components/Game.scss b/src/components/Game.scss index 0a72322..04c273e 100644 --- a/src/components/Game.scss +++ b/src/components/Game.scss @@ -74,21 +74,6 @@ } } -.modal { - position: fixed; - width: 100vw; - height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - backdrop-filter: blur(10px); - background-color: rgb(0 0 0 / 10%); - top: 0; - left: 0; - z-index: 10 !important; -} - .bgm-button { position: fixed; left: 8px; diff --git a/src/components/Game.tsx b/src/components/Game.tsx index 662fefd..62b4e85 100644 --- a/src/components/Game.tsx +++ b/src/components/Game.tsx @@ -4,6 +4,7 @@ import React, { useEffect, useRef, useState, + Suspense, } from 'react'; import './Game.scss'; import { @@ -11,9 +12,11 @@ import { LAST_SCORE_STORAGE_KEY, LAST_TIME_STORAGE_KEY, randomString, + timestampToUsedTimeString, waitTimeout, } from '../utils'; import { Icon, Theme } from '../themes/interface'; +import Score from './Score'; interface MySymbol { id: string; @@ -488,17 +491,20 @@ const Game: FC<{
得分{score}
- 用时{(usedTime / 1000).toFixed(3)}秒 + 用时{timestampToUsedTimeString(usedTime)}
- {/*提示弹窗*/} - {finished && ( -
-

{tipText}

-

得分{score}

-

用时{(usedTime / 1000).toFixed(3)}秒

- -
- )} + {/*积分、排行榜*/} + rank list}> + {finished && ( + + )} + {/*bgm*/} {theme.bgm && ( +
+ )} + +
{tip}
+ + {!__DIY__ && ( +
+

TOP 50

+ {rankList.length ? ( +
+ + + + + + {/**/} + {/**/} + {/**/} + + + + + {rankList.map((rank, idx) => ( + + + + {/**/} + {/**/} + {/**/} + + + ))} + +
名次名称通关数用时得分综合评分
{idx + 1} + {rank.username} + {rank.userId === userId && + '(你)'} + {rank.level}*/} + {/* {timestampToUsedTimeString(*/} + {/* rank.time*/} + {/* )}*/} + {/*{rank.score}{rank.rating}
+
+ ) : ( +
+ 暂无排行,速速霸榜! +
+ )} + +
+ )} + + + + ); +}; + +export default Score; diff --git a/src/main.tsx b/src/main.tsx index 8ca80ad..b266524 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,6 +8,7 @@ import { DEFAULT_BGM_STORAGE_KEY, domRelatedOptForTheme, parsePathCustomThemeId, + PLAYING_THEME_ID_STORAGE_KEY, wrapThemeDefaultSounds, } from './utils'; import { getDefaultTheme } from './themes/default'; @@ -33,6 +34,11 @@ const errorTip = (tip: string) => { // 加载成功后数据转换(runtime)以及转场 const successTrans = (theme: Theme) => { + sessionStorage.setItem( + PLAYING_THEME_ID_STORAGE_KEY, + customThemeIdFromPath || theme.title + ); + wrapThemeDefaultSounds(theme); setTimeout(() => { @@ -72,7 +78,7 @@ const loadTheme = () => { Bmob.Query('config') .get(customThemeIdFromPath) .then((res) => { - const { content } = res as any; + const { content, increment } = res as any; localStorage.setItem(customThemeIdFromPath, content); try { const customTheme = JSON.parse(content); @@ -80,6 +86,10 @@ const loadTheme = () => { } catch (e) { errorTip('主题配置解析失败'); } + // 统计访问次数 + increment('visitNum'); + // @ts-ignore + res.save(); }) .catch(({ error }) => { errorTip(error); diff --git a/src/themes/default/index.ts b/src/themes/default/index.ts index e1054f4..5b73025 100644 --- a/src/themes/default/index.ts +++ b/src/themes/default/index.ts @@ -25,6 +25,7 @@ export const getDefaultTheme: () => Theme = () => { title: '有解的羊了个羊', desc: '真的可以通关~', dark: true, + maxLevel: 5, backgroundColor: '#8dac85', icons: icons.map((icon) => ({ name: icon, diff --git a/src/utils.ts b/src/utils.ts index 7bd6a70..1846256 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,7 @@ import { Theme } from './themes/interface'; import { getDefaultTheme } from './themes/default'; +// local export const LAST_LEVEL_STORAGE_KEY = 'lastLevel'; export const LAST_SCORE_STORAGE_KEY = 'lastScore'; export const LAST_TIME_STORAGE_KEY = 'lastTime'; @@ -11,6 +12,10 @@ export const CUSTOM_THEME_FILE_VALIDATE_STORAGE_KEY = 'customThemeFileValidate'; export const DEFAULT_BGM_STORAGE_KEY = 'defaultBgm'; export const DEFAULT_TRIPLE_SOUND_STORAGE_KEY = 'defaultTripleSound'; export const DEFAULT_CLICK_SOUND_STORAGE_KEY = 'defaultClickSound'; +export const USER_NAME_STORAGE_KEY = 'username'; +export const USER_ID_STORAGE_KEY = 'userId'; +// session +export const PLAYING_THEME_ID_STORAGE_KEY = 'playingThemeId'; export const linkReg = /^(https|data):+/; @@ -142,3 +147,25 @@ export const getFileBase64String: (file: File) => Promise = ( }; }); }; + +export const timestampToUsedTimeString: (time: number) => string = (time) => { + try { + const hours = Math.floor(time / (1000 * 60 * 60)); + const minutes = Math.floor( + (time - 1000 * 60 * 60 * hours) / (1000 * 60) + ); + const seconds = ( + (time - 1000 * 60 * 60 * hours - 1000 * 60 * minutes) / + 1000 + ).toFixed(3); + if (hours) { + return `${hours}小时${minutes}分${seconds}秒`; + } else if (minutes) { + return `${minutes}分${seconds}秒`; + } else { + return `${seconds}秒`; + } + } catch (e) { + return '时间转换出错'; + } +};