diff --git a/index.html b/index.html index 8b2f65c..1329559 100644 --- a/index.html +++ b/index.html @@ -8,7 +8,7 @@ - +
diff --git a/package.json b/package.json index 73932db..d43729a 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "bootstrap-icons": "^1.10.2", - "solid-js": "^1.6.5" + "solid-js": "^1.6.5", + "solid-transition-group": "^0.0.12" } } diff --git a/src/App.tsx b/src/App.tsx index 2be0469..962ec39 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,49 @@ -import { createSignal } from 'solid-js' +import { createEffect, createSignal, onMount } from 'solid-js' import { render } from 'solid-js/web' +import { Transition } from 'solid-transition-group' +import { ConnectingWell } from './components/ConnectingWell' +import { HeightTransition } from './components/HeightTransition' +import { PairWell } from './components/PairWell' +import { PlayerActivationWell } from './components/PlayerActivationWell' +import { PlayerWell } from './components/PlayerWell' +import { ServerStatus, ServerStatusType } from './components/ServerStatus' export const App = () => { - const [isConnected, setIsConnected] = createSignal(false) + const [enabled, setEnabled] = createSignal(false) + const [firstConnected, setFirstConneted] = createSignal(false) + const [serverStatus, setServerStatus] = createSignal('connecting') + + const [boundUser, setBoundUser] = createSignal() + + createEffect(() => { + if (serverStatus() === 'connected') { + setFirstConneted(true) + } + }, serverStatus()) + + onMount(() => { + setTimeout(() => { + setServerStatus('connected') + }, 3000) + }) + + const renderWell = () => { + if (!enabled() || !firstConnected()) { + return ( + setEnabled(true)} connecting={!firstConnected()} /> + ) + } else { + if (!boundUser()) { + return ( + + ) + } else { + return ( + + ) + } + } + } return ( <> @@ -17,23 +58,15 @@ export const App = () => {
-
- {!isConnected() ? ( -
-

已连接到服务器

-

播放器需要连接到游戏

-

在游戏中输入以下指令:

-
- /ipm bind 123456 -
-
- ) : ( -
-

播放器已准备好

-

点击下方按钮开始播放

- -
- )} +
+
+

+ +

+ + {renderWell()} + +
diff --git a/src/components/Card.tsx b/src/components/Card.tsx deleted file mode 100644 index 3846407..0000000 --- a/src/components/Card.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { createEffect, createSignal, JSXElement, onCleanup, onMount, useTransition } from "solid-js" - -export type CardProps = { - title?: string | JSXElement, - children?: any -} - -export const Card = (props: CardProps) => { - const [outerHeight, setOuterHeight] = createSignal(0) - let innerElem: HTMLDivElement | undefined - - onMount(() => { - if (innerElem) { - setOuterHeight(innerElem?.offsetHeight) - } - }) - - createEffect(() => { - if (innerElem) { - setOuterHeight(innerElem?.offsetHeight) - } - }) - - return ( -
-
-
-
-

{props.title}

-

点击下方按钮开始播放

- -
-
-
-
- ) -} \ No newline at end of file diff --git a/src/components/ConnectingWell.tsx b/src/components/ConnectingWell.tsx new file mode 100644 index 0000000..ed3ccde --- /dev/null +++ b/src/components/ConnectingWell.tsx @@ -0,0 +1,8 @@ +export const ConnectingWell = () => { + return ( +
+

正在准备

+

播放器正在连接到服务器

+
+ ) +} \ No newline at end of file diff --git a/src/components/HeightTransition/index.scss b/src/components/HeightTransition/index.scss new file mode 100644 index 0000000..171773a --- /dev/null +++ b/src/components/HeightTransition/index.scss @@ -0,0 +1,11 @@ +.height-transition-container { + overflow: hidden; + position: relative; + transition: height 350ms ease-in-out; + height: auto; +} + +.height-transition-inner { + width: 100%; + height: auto; +} \ No newline at end of file diff --git a/src/components/HeightTransition/index.tsx b/src/components/HeightTransition/index.tsx new file mode 100644 index 0000000..feb1a23 --- /dev/null +++ b/src/components/HeightTransition/index.tsx @@ -0,0 +1,31 @@ +import { createSignal, JSXElement } from "solid-js" +import { Transition } from "solid-transition-group" +import "./index.scss"; + +export type HeightTransitionProps = { + children: JSXElement +} + +export const HeightTransition = (props: HeightTransitionProps) => { + const [outerHeight, setOuterHeight] = createSignal() + + return ( +
+
+ { + setOuterHeight((el as any).offsetHeight); + setTimeout(done, 400); + }} + onExit={(el, done) => { + setOuterHeight((el as any).offsetHeight); + setTimeout(done, 400); + }} + > + {props.children} + +
+
+ ) +} \ No newline at end of file diff --git a/src/components/PairWell.tsx b/src/components/PairWell.tsx new file mode 100644 index 0000000..dc45515 --- /dev/null +++ b/src/components/PairWell.tsx @@ -0,0 +1,11 @@ +export const PairWell = () => { + return ( +
+

播放器需要连接到游戏

+

在游戏中输入以下指令:

+
+ /ipm bind 123456 +
+
+ ) +} \ No newline at end of file diff --git a/src/components/PlayerActivationWell.tsx b/src/components/PlayerActivationWell.tsx new file mode 100644 index 0000000..5aaae08 --- /dev/null +++ b/src/components/PlayerActivationWell.tsx @@ -0,0 +1,41 @@ +import { createSignal } from "solid-js" +import { SuccessAnimateIcon } from "./SuccessAnimateIcon" + +export type PlayerActivationWellProps = { + connecting?: boolean, + onEnabled?: () => any +} + +export const PlayerActivationWell = (props: PlayerActivationWellProps) => { + const [processing, setProcessing] = createSignal(false) + const [enabled, setEnabled] = createSignal(false) + const [error, setError] = createSignal() + + const handleEnableClick = () => { + setProcessing(true) + setEnabled(true) + // 等待成功动画结束 + setTimeout(() => { + props.onEnabled?.() + }, 1500) + } + + return ( +
+

播放器需要激活

+

由于浏览器限制,网页不能自动播放音频,需要手动点击下方按钮

+
+ + {enabled() && ( + + )} + {!error() && enabled() && props.connecting && ( + 正在等待连接服务器 + )} + {error() && ( + 启动播放器错误: {error()} + )} +
+
+ ) +} \ No newline at end of file diff --git a/src/components/PlayerWell.tsx b/src/components/PlayerWell.tsx new file mode 100644 index 0000000..0d017f7 --- /dev/null +++ b/src/components/PlayerWell.tsx @@ -0,0 +1,8 @@ +export const PlayerWell = () => { + return ( +
+

播放器已准备完成

+

当前状态: 等待播放

+
+ ) +} \ No newline at end of file diff --git a/src/components/ServerStatus.tsx b/src/components/ServerStatus.tsx new file mode 100644 index 0000000..e3eb723 --- /dev/null +++ b/src/components/ServerStatus.tsx @@ -0,0 +1,36 @@ +import { Match, mergeProps, Switch } from "solid-js"; +import { Transition } from "solid-transition-group" + +export type ServerStatusType = 'connecting' | 'connected' | 'error' + +export type ServerStatusProps = { + type?: ServerStatusType, + error?: string +} + +export const ServerStatus = (srcProps: ServerStatusProps) => { + const props = mergeProps({ + type: 'connecting', + }, srcProps) + + return ( +
+ + + +
+
+ 正在连接服务器 +
+
+ +
已连接到服务器
+
+ +
无法连接到服务器{props.error && (': ' + props.error)}
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/SuccessAnimateIcon/index.scss b/src/components/SuccessAnimateIcon/index.scss new file mode 100644 index 0000000..160d124 --- /dev/null +++ b/src/components/SuccessAnimateIcon/index.scss @@ -0,0 +1,45 @@ +.success-animation { + display: inline-block; + padding: 6px; + + .checkmark { + width: 36px; + height: 36px; + border-radius: 50%; + --bs-bg-opacity: 1; + stroke-width: 3px; + stroke: rgb(var(--bs-success-rgb)); + stroke-miterlimit: 10; + box-shadow: inset 0px 0px 0px rgb(var(--bs-success-rgb)); + animation: successAnimIconFill .4s ease-in-out .4s forwards; + + &__circle { + stroke-dasharray: 166; + stroke-dashoffset: 166; + stroke-width: 3px; + stroke-miterlimit: 10; + stroke: rgb(var(--bs-success-rgb)); + fill: rgb(var(--bs-white-rgb)); + animation: successAnimIconStroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards; + } + + &__check { + transform-origin: 50% 50%; + stroke-dasharray: 48; + stroke-dashoffset: 48; + animation: successAnimIconStroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.6s forwards; + } + } +} + +@keyframes successAnimIconStroke { + 100% { + stroke-dashoffset: 0; + } +} + +@keyframes successAnimIconFill { + 100% { + box-shadow: inset 0px 0px 0px 30px #4bb71b; + } +} \ No newline at end of file diff --git a/src/components/SuccessAnimateIcon/index.tsx b/src/components/SuccessAnimateIcon/index.tsx new file mode 100644 index 0000000..a2bb197 --- /dev/null +++ b/src/components/SuccessAnimateIcon/index.tsx @@ -0,0 +1,12 @@ +import "./index.scss" + +export const SuccessAnimateIcon = () => { + return ( +
+ + + + +
+ ) +}; \ No newline at end of file diff --git a/src/libs/bootstrap/scss/bootstrap.scss b/src/libs/bootstrap/scss/bootstrap.scss index 8f8296d..e329d4c 100644 --- a/src/libs/bootstrap/scss/bootstrap.scss +++ b/src/libs/bootstrap/scss/bootstrap.scss @@ -17,31 +17,31 @@ @import "images"; @import "containers"; @import "grid"; -@import "tables"; +// @import "tables"; @import "forms"; @import "buttons"; -@import "transitions"; +// @import "transitions"; @import "dropdown"; @import "button-group"; @import "nav"; @import "navbar"; @import "card"; -@import "accordion"; -@import "breadcrumb"; -@import "pagination"; -@import "badge"; -@import "alert"; -@import "progress"; -@import "list-group"; +// @import "accordion"; +// @import "breadcrumb"; +// @import "pagination"; +// @import "badge"; +// @import "alert"; +// @import "progress"; +// @import "list-group"; @import "close"; -@import "toasts"; +// @import "toasts"; @import "modal"; -@import "tooltip"; -@import "popover"; -@import "carousel"; +// @import "tooltip"; +// @import "popover"; +// @import "carousel"; @import "spinners"; -@import "offcanvas"; -@import "placeholders"; +// @import "offcanvas"; +// @import "placeholders"; // Helpers @import "helpers"; diff --git a/src/stores/Player.ts b/src/stores/Player.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/style.scss b/src/style.scss index 237b81d..f22d99e 100644 --- a/src/style.scss +++ b/src/style.scss @@ -1,2 +1,4 @@ @import "./libs/bootstrap/scss/bootstrap.scss"; -@import "bootstrap-icons/font/bootstrap-icons.css"; \ No newline at end of file +@import "bootstrap-icons/font/bootstrap-icons.css"; + +@import "./styles/animate.scss"; \ No newline at end of file diff --git a/src/styles/animate.scss b/src/styles/animate.scss new file mode 100644 index 0000000..a6cd75f --- /dev/null +++ b/src/styles/animate.scss @@ -0,0 +1,94 @@ +.anim-scroll-container { + overflow-y: hidden; + position: relative; +} + +.slide-up { + &-enter-active { + transform: translate3d(0, 100%, 0); + transition: transform 350ms ease-in-out; + } + + &-enter-to { + transform: translate3d(0, 0, 0); + } + + &-exit-active { + position: absolute; + top: 0; + left: 0; + width: 100%; + transform: translate3d(0, 0, 0); + transition: transform 350ms ease-in-out; + } + + &-exit-to { + transform: translate3d(0, -100%, 0); + } +} + +.fade { + &-enter { + opacity: 0; + } + + &-enter-active { + display: inherit; + opacity: 0; + transition: opacity 150ms linear; + } + + &-enter-to { + opacity: 1; + } + + &-exit { + opacity: 1; + } + + &-exit-active { + opacity: 1; + transition: opacity 150ms linear; + } + + &-exit-to { + opacity: 0; + } +} + +.fade-left { + &-enter { + opacity: 0; + } + + &-enter-active { + display: inherit; + opacity: 0; + transform: translate3d(5%, 0, 0); + transition: opacity 250ms linear, transform 250ms ease-in-out; + } + + &-enter-to { + opacity: 1; + transform: translate3d(0, 0, 0); + } + + &-exit { + opacity: 1; + } + + &-exit-active { + position: absolute; + top: 0; + left: 0; + width: 100%; + opacity: 1; + transform: translate3d(0, 0, 0); + transition: opacity 250ms linear, transform 250ms ease-in-out; + } + + &-exit-to { + opacity: 0; + transform: translate3d(-5%, 0, 0); + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 109805d..3b62042 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "moduleResolution": "node", "strict": true, "jsx": "preserve", + "jsxImportSource": "solid-js", "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true,