diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/ATRIUM.webm b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/ATRIUM.webm new file mode 100644 index 000000000..f7b65653a Binary files /dev/null and b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/ATRIUM.webm differ diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/FORGE.webm b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/FORGE.webm new file mode 100644 index 000000000..744217980 Binary files /dev/null and b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/FORGE.webm differ diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/GALLERY.webm b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/GALLERY.webm new file mode 100644 index 000000000..4a5114d16 Binary files /dev/null and b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/GALLERY.webm differ diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/HARBOUR.webm b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/HARBOUR.webm new file mode 100644 index 000000000..f018f890b Binary files /dev/null and b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/HARBOUR.webm differ diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/LIBRARY.webm b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/LIBRARY.webm new file mode 100644 index 000000000..aedf6b9d6 Binary files /dev/null and b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/LIBRARY.webm differ diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/MARKET.webm b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/MARKET.webm new file mode 100644 index 000000000..f0efc3daa Binary files /dev/null and b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/MARKET.webm differ diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/PRAYER HALL.webm b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/PRAYER HALL.webm new file mode 100644 index 000000000..d8582d83d Binary files /dev/null and b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/PRAYER HALL.webm differ diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/backgrounds/temple-temp-background.png b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/backgrounds/temple-temp-background.png new file mode 100644 index 000000000..b9d0ab2a8 Binary files /dev/null and b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/assets/backgrounds/temple-temp-background.png differ diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/components/SmartClickableVideo.tsx b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/components/SmartClickableVideo.tsx new file mode 100644 index 000000000..180d67dcb --- /dev/null +++ b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/components/SmartClickableVideo.tsx @@ -0,0 +1,105 @@ +import React, { useRef, useState, useEffect } from 'react'; +import styled from 'styled-components'; + +interface Props { + videoSrc: string; + x: number; + y: number; + width: number; + height: number; + onClick: () => void; +} + +const SmartClickableVideo: React.FC = ({ + videoSrc, + x, + y, + width, + height, + onClick, +}) => { + const videoRef = useRef(null); + const canvasRef = useRef(null); + const [canClick, setCanClick] = useState(false); + + const handleMouseMove = (e: React.MouseEvent) => { + const video = videoRef.current; + const canvas = canvasRef.current; + if (!video || !canvas) return; + + const rect = e.currentTarget.getBoundingClientRect(); + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; + + const canvasWidth = canvas.width; + const canvasHeight = canvas.height; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + // Draw current video frame to canvas + ctx.drawImage(video, 0, 0, canvasWidth, canvasHeight); + + // Convert mouseX/Y into canvas space + const xCanvas = (mouseX / rect.width) * canvasWidth; + const yCanvas = (mouseY / rect.height) * canvasHeight; + + const pixel = ctx.getImageData(xCanvas, yCanvas, 1, 1).data; + const [r, g, b] = pixel; + + // If it's not solid black, allow click + const brightness = r + g + b; + const isVisible = brightness > 10; + + setCanClick(isVisible); + (e.currentTarget as HTMLDivElement).style.cursor = isVisible + ? 'pointer' + : 'default'; + }; + + const handleClick = () => { + if (canClick) { + onClick(); + } + }; + + return ( + + + + + ); +}; + +export default SmartClickableVideo; + +const Wrapper = styled.div` + position: absolute; + cursor: pointer; + z-index: 2; +`; + +const Video = styled.video.attrs({ + disablePictureInPicture: true, +})` + width: 100%; + height: 100%; + object-fit: cover; + pointer-events: none; + display: block; +`; + +const HiddenCanvas = styled.canvas` + display: none; +`; diff --git a/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/index.tsx b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/index.tsx new file mode 100644 index 000000000..041cb4756 --- /dev/null +++ b/apps/dapp/src/components/Pages/Core/DappPages/OverworldMap/index.tsx @@ -0,0 +1,243 @@ +import SmartClickableVideo from './components/SmartClickableVideo'; +import styled from 'styled-components'; +import { useMediaQuery } from 'react-responsive'; +import { queryPhone } from 'styles/breakpoints'; + +interface AnimatedSection { + id: string; + x: number; + y: number; + width: number; + height: number; + videoSrc: string; + targetRoute: string; + title: string; +} + +const animatedSections: AnimatedSection[] = [ + { + id: 'forge', + x: 25.8, + y: 43, + width: 17.5, + height: 21.3, + videoSrc: + '/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/FORGE.webm', + targetRoute: '/dapp/spice/overview', + title: 'Forge', + }, + { + id: 'prayer-hall', + x: 64, + y: 5, + width: 25.3, + height: 38.1, + videoSrc: + '/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/PRAYER HALL.webm', + targetRoute: '/dapp/spice/overview', + title: 'Prayer Hall', + }, + { + id: 'market', + x: 47.6, + y: 35, + width: 20.9, + height: 44.3, + videoSrc: + '/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/MARKET.webm', + targetRoute: '/dapp/spice/overview', + title: 'Market', + }, + { + id: 'harbour', + x: 22, + y: 71, + width: 18.6, + height: 23.9, + videoSrc: + '/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/HARBOUR.webm', + targetRoute: '/dapp/spice/overview', + title: 'Harbour', + }, + { + id: 'gallery', + x: 64, + y: 68, + width: 22.4, + height: 34.1, + videoSrc: + '/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/GALLERY.webm', + targetRoute: '/dapp/spice/overview', + title: 'Gallery', + }, + { + id: 'atrium', + x: 3.8, + y: 2.8, + width: 29.7, + height: 47.0, + videoSrc: + '/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/ATRIUM.webm', + targetRoute: '/dapp/spice/overview', + title: 'Atrium', + }, + { + id: 'library', + x: 41.8, + y: 21, + width: 14.2, + height: 27.2, + videoSrc: + '/src/components/Pages/Core/DappPages/OverworldMap/assets/animations/LIBRARY.webm', + targetRoute: '/dapp/spice/overview', + title: 'Library', + }, +]; + +const OverworldMap = () => { + const isPhoneOrAbove = useMediaQuery({ + query: queryPhone, + }); + + const handleSectionClick = (section: AnimatedSection) => { + window.location.href = section.targetRoute; + }; + + const getBackgroundImage = () => + '/src/components/Pages/Core/DappPages/OverworldMap/assets/backgrounds/temple-temp-background.png'; + + if (!isPhoneOrAbove) { + return ( + + {animatedSections.map((section, index) => ( + + + + + + + + {section.title} + + + ))} + + ); + } + + return ( + + {/* */} + {animatedSections.map((section) => ( + handleSectionClick(section)} + /> + ))} + + ); +}; + +export default OverworldMap; + +const MapContainer = styled.div` + position: relative; + width: 100%; + max-width: 1920px; + aspect-ratio: 16 / 9; + margin: 0 auto; + background-color: grey; + overflow: hidden; +`; + +const BackgroundImage = styled.img` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; + z-index: 0; +`; + +const AnimatedSectionWrapper = styled.div<{ + x: number; + y: number; + width: number; + height: number; +}>` + position: absolute; + left: ${(props) => props.x}%; + top: ${(props) => props.y}%; + width: ${(props) => props.width}%; + height: ${(props) => props.height}%; + cursor: pointer; + background: transparent; + z-index: 1; +`; + +const VideoElement = styled.video` + width: 100%; + height: 100%; + object-fit: cover; + background-color: transparent; + display: block; + // opacity: 0.5; + z-index: 2; + pointer-events: none; /* Make overlay transparent to clicks */ +`; + +const MobileContainer = styled.div` + width: 100%; + max-width: 480px; + margin: 0 auto; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 1rem; +`; + +const MobileSection = styled.div<{ isReversed: boolean }>` + display: flex; + flex-direction: ${(props) => (props.isReversed ? 'row-reverse' : 'row')}; + align-items: center; + background: rgba(255, 255, 255, 0.1); + border-radius: 8px; + overflow: hidden; + min-height: 120px; +`; + +const MobileVideoContainer = styled.div` + width: 50%; + height: 120px; + overflow: hidden; +`; + +const MobileVideoElement = styled.video` + width: 100%; + height: 100%; + object-fit: cover; + background-color: transparent; + display: block; +`; + +const MobileTitleContainer = styled.div` + width: 50%; + padding: 1rem; + display: flex; + align-items: center; + justify-content: center; +`; + +const MobileTitle = styled.h3` + margin: 0; + color: white; + font-size: 1.2rem; + text-align: center; + font-weight: 600; +`; diff --git a/apps/dapp/src/main.tsx b/apps/dapp/src/main.tsx index 530efa09d..b6064fb3b 100644 --- a/apps/dapp/src/main.tsx +++ b/apps/dapp/src/main.tsx @@ -17,6 +17,7 @@ import { LegacyPage } from 'components/Pages/Core/DappPages/LegacyPage'; import V2Layout from 'components/Layouts/V2Layout'; import { SpiceBazaarPage } from 'components/Pages/Core/DappPages/SpiceBazaar/index'; import { SpiceBazaarTopNav } from 'components/Pages/Core/DappPages/SpiceBazaar/TopNav'; +import OverworldMap from 'components/Pages/Core/DappPages/OverworldMap'; import { Overview } from 'components/Pages/Core/DappPages/SpiceBazaar/Overview'; import { StakeTemple } from 'components/Pages/Core/DappPages/SpiceBazaar/Earn/StakeTemple'; import { Stake } from 'components/Pages/Core/DappPages/SpiceBazaar/Earn/StakeTemple/Stake'; @@ -105,6 +106,7 @@ root.render( path="/dapp" element={} /> + } /> }> } /> } />