Add SUPERCOMMAND component and integrate it into the App; enhance Page with additional text
This commit is contained in:
parent
16414a6b51
commit
e7b464b9fe
3 changed files with 108 additions and 0 deletions
|
|
@ -5,6 +5,7 @@ import NotFound from "./utils/NotFound";
|
||||||
import { ThemeProvider } from "@/components/theme-provider";
|
import { ThemeProvider } from "@/components/theme-provider";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useNavigate } from "react-router";
|
import { useNavigate } from "react-router";
|
||||||
|
import SUPERCOMMAND from "@/components/SUPERCOMMAND";
|
||||||
|
|
||||||
function TimelineRedirect() {
|
function TimelineRedirect() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
@ -15,6 +16,7 @@ function TimelineRedirect() {
|
||||||
export default function App() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider defaultTheme="system">
|
<ThemeProvider defaultTheme="system">
|
||||||
|
<SUPERCOMMAND />
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Page />} />
|
<Route path="/" element={<Page />} />
|
||||||
|
|
|
||||||
|
|
@ -178,6 +178,8 @@ export function Page() {
|
||||||
<Timeline />
|
<Timeline />
|
||||||
<Seperator />
|
<Seperator />
|
||||||
<Contact />
|
<Contact />
|
||||||
|
<p>Press tab to access easily surf the page</p>
|
||||||
|
<p>But where am I? Who was I? Today is November 8th.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
104
src/components/SUPERCOMMAND.tsx
Normal file
104
src/components/SUPERCOMMAND.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default function SUPERCOMMAND() {
|
||||||
|
const [visible, setVisible] = React.useState(false);
|
||||||
|
const [tabCount, setTabCount] = React.useState(0);
|
||||||
|
const [showPressSpace, setShowPressSpace] = React.useState(false);
|
||||||
|
const audioContextRef = React.useRef<AudioContext | null>(null);
|
||||||
|
|
||||||
|
// Beep 소리를 생성하는 함수 (Web Audio API 사용)
|
||||||
|
const playBeep = (count: number) => {
|
||||||
|
if (!audioContextRef.current) {
|
||||||
|
audioContextRef.current = new (window.AudioContext || (window as any).webkitAudioContext)();
|
||||||
|
}
|
||||||
|
const context = audioContextRef.current;
|
||||||
|
const oscillator = context.createOscillator();
|
||||||
|
const gainNode = context.createGain();
|
||||||
|
|
||||||
|
oscillator.connect(gainNode);
|
||||||
|
gainNode.connect(context.destination);
|
||||||
|
|
||||||
|
const frequency = 440 + (count - 1); // 콤보 수에 따라 주파수 증가 (440Hz부터 10Hz씩 상승)
|
||||||
|
oscillator.frequency.setValueAtTime(frequency, context.currentTime);
|
||||||
|
oscillator.type = 'sine';
|
||||||
|
|
||||||
|
gainNode.gain.setValueAtTime(0.1, context.currentTime); // 볼륨
|
||||||
|
gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 0.5); // 0.5초 후 페이드 아웃
|
||||||
|
|
||||||
|
oscillator.start(context.currentTime);
|
||||||
|
oscillator.stop(context.currentTime + 0.5); // 0.5초로 길게 설정
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
// Tab 누르면 보이게 (브라우저 기본 Tab 이동 방지)
|
||||||
|
if (event.key === "Tab") {
|
||||||
|
// event.preventDefault();
|
||||||
|
setVisible(true);
|
||||||
|
playBeep(tabCount); // Tab 누를 때마다 beep 소리 재생
|
||||||
|
} else {
|
||||||
|
setVisible(false);
|
||||||
|
setTabCount(0);
|
||||||
|
setShowPressSpace(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'Tab' 키를 누를 때마다 카운트(연속 또는 누적)
|
||||||
|
if (event.key === "Tab") {
|
||||||
|
setTabCount((prev) => {
|
||||||
|
const next = prev + 1;
|
||||||
|
if (next === 8 || next === 1108) {
|
||||||
|
setShowPressSpace(true);
|
||||||
|
} else if (next > 8) {
|
||||||
|
setShowPressSpace(false);
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 스페이스바 눌렀을 때, 메시지가 보이는 상태이면 페이지 이동
|
||||||
|
if ((event.code === "Space" || event.key === " ") && showPressSpace) {
|
||||||
|
// 원하는 이동 경로로 변경하세요:
|
||||||
|
if (tabCount === 8) {
|
||||||
|
const redirectList = [
|
||||||
|
"https://www.youtube.com/watch?v=DjGxGMxvg4M",
|
||||||
|
"https://www.youtube.com/watch?v=oQcaPVGUtuA",
|
||||||
|
"https://www.youtube.com/watch?v=E6RQgBwcmG8",
|
||||||
|
"https://www.youtube.com/watch?v=xUNFDn2my68",
|
||||||
|
"https://www.youtube.com/watch?v=W-_90c08AIY",
|
||||||
|
"https://www.youtube.com/watch?v=GyEIHPyIQQg",
|
||||||
|
"https://www.youtube.com/watch?v=xZi12DkWkHA"
|
||||||
|
]
|
||||||
|
window.location.href = redirectList[Math.floor(Math.random() * redirectList.length)];
|
||||||
|
} else if (tabCount === 1108) {
|
||||||
|
window.location.href = "https://whoamiandwhoareyou.imnya.ng";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// const handleKeyUp = (event: KeyboardEvent) => {
|
||||||
|
// // Tab에서 손을 떼면 숨김
|
||||||
|
// if (event.key === "Tab") {
|
||||||
|
// setVisible(false);
|
||||||
|
// // 필요하면 카운트/메시지 리셋
|
||||||
|
// // setEightCount(0);
|
||||||
|
// // setShowPressSpace(false);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
// window.addEventListener("keyup", handleKeyUp);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
// window.removeEventListener("keyup", handleKeyUp);
|
||||||
|
};
|
||||||
|
}, [showPressSpace]);
|
||||||
|
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div id="supercommand" className="fixed bottom-5 left-5">
|
||||||
|
<h1 className="text-2xl"><span className="text-3xl font-bold">{tabCount}</span> Combo</h1>
|
||||||
|
{showPressSpace ? <p>스페이스바를 눌러주세요</p> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue