diff --git a/src/assets/images/car.png b/src/assets/images/car.png new file mode 100644 index 0000000..1129388 Binary files /dev/null and b/src/assets/images/car.png differ diff --git a/src/components/MainMenu/NighShift/index.js b/src/components/MainMenu/NighShift/index.js new file mode 100644 index 0000000..6029f47 --- /dev/null +++ b/src/components/MainMenu/NighShift/index.js @@ -0,0 +1,41 @@ +import React from 'react'; +import { + Bulb, + InnerLight, + LightBar, + NightShiftOverlay, + StrobeLight, + CarImage, + CruiserLight +} from './styles'; +import Car from '../../../assets/images/car.png'; + +const NightShiftIntro = () => ( + + + + + + + + {[...Array(3).keys()].map(order => ( + + + {[...Array(18).keys()].map(bulbOrder => ( + + ))} + + ))} + {[...Array(3).keys()].map(order => ( + + + {[...Array(18).keys()].map(bulbOrder => ( + + ))} + + ))} + + +); + +export default NightShiftIntro; diff --git a/src/components/MainMenu/NighShift/styles.js b/src/components/MainMenu/NighShift/styles.js new file mode 100644 index 0000000..3594dcb --- /dev/null +++ b/src/components/MainMenu/NighShift/styles.js @@ -0,0 +1,346 @@ +import styled from '@emotion/styled'; +import { css, keyframes } from '@emotion/core'; + +// Police Lights adapted from the original of Sébastien THIBAUD +// https://codepen.io/DrSeb/pen/wzrQqp + +const ANIMATION_SPEED = 800; +const LIGHTS_SIZE = 44; + +const bulbGradient = + 'linear-gradient(155deg, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0.14) 25%, rgba(255, 255, 255, 0.18) 49%, rgba(0, 0, 0, 0) 78%, rgba(0, 0, 0, 0.8) 100%)'; + +const bulbDimShadow = '0px 0px 3px #222'; +const bulbLitShadow = '0px 0px 10px 4px #fff'; + +const LightsOff = css` + background-color: #222; + box-shadow: 0px 0px 0px 0px #000; + border: 1px solid #111; + opacity: 0; +`; + +const LightsOn = (border, bg, shadow) => css` + border: 1px solid ${border}; + background-color: ${bg}; + box-shadow: 0px 0px 100px 25px ${shadow}; + opacity: 1; +`; + +const Strobe = (border, bg, shadow) => keyframes` + 0%, 25% { + ${LightsOff}; + } + 28%, 50% { + ${LightsOn(border, bg, shadow)}; + } + 52%, 55%º { + ${LightsOff}; + } + 57%, 69% { + ${LightsOn(border, bg, shadow)}; + } + 70%, 71% { + ${LightsOff}; + } + 72%, 75% { + ${LightsOn(border, bg, shadow)}; + } + 77%, 100% { + ${LightsOff}; + } +`; + +const StrobeBulb = (border, bg, shadow) => keyframes` + 0%, 25% { + background: ${bulbGradient}; box-shadow: ${bulbDimShadow} + } + 28%, 50% { + background: white; box-shadow: ${bulbLitShadow} + } + 52%, 55% { + background: ${bulbGradient}; box-shadow: ${bulbDimShadow} + } + 57%, 69% { + background: white; box-shadow: ${bulbLitShadow} + } + 70%, 71% { + background: ${bulbGradient}; box-shadow: ${bulbDimShadow} + } + 72%, 75% { + background: white; box-shadow: ${bulbLitShadow} + } + 77%, 100% { + background: ${bulbGradient}; box-shadow: ${bulbDimShadow} + } +`; + +export const Bulb = styled.span` + label: Bulb; + display: inline-block; + position: relative; + box-shadow: 0px 0px 3px #222; + border-radius: 50%; + line-height: 0.2; + height: ${`${LIGHTS_SIZE / 20}px`}; + width: ${`${LIGHTS_SIZE / 20}px`}; + z-index: 999; + animation-duration: ${`${ANIMATION_SPEED + 80}ms`}; + animation-name: ${StrobeBulb}; + animation-iteration-count: infinite; + background: ${bulbGradient}; + + ${({ delay }) => + delay && + css` + animation-delay: ${`${ANIMATION_SPEED / 2 + 40}ms`}; + `} + + @media all and (min-width: 701px) { + height: ${`${LIGHTS_SIZE / 10}px`}; + width: ${`${LIGHTS_SIZE / 10}px`}; + animation-duration: ${`${ANIMATION_SPEED}ms`}; + + ${({ delay }) => + delay && + css` + animation-delay: ${`${ANIMATION_SPEED / 2}ms`}; + `} + } +`; +Bulb.displayName = 'Bulb'; + +export const CarImage = styled.img` + label: CarImage; + display: none; + width: 30%; + position: absolute; + top: 50px; + left: 50%; + transform: translate(-50%, 0); + filter: brightness(0.45); + + @media all and (min-width: 1200px) { + display: block; + } +`; +CarImage.displayName = 'CarImage'; + +export const CruiserLight = styled.div` + label: CruiserLight; + height: 27px; + width: 45px; + position: absolute; + top: 135px; + left: 50%; + transform: translate(-50%, 0) rotate(13deg); + margin-left: -80px; + border-radius: 6px; + border: 1px solid rgba(255, 255, 255, 0.2); + background-color: rgba(255, 255, 240, 0.85); + box-shadow: 0px 0px 100px 25px rgba(255, 255, 240, 0.85); + opacity: 1; + z-index: 10; + filter: blur(5px) opacity(0.8); + + ${({ side }) => + side === 'right' && + css` + margin-left: 80px; + transform: translate(-50%, 0) rotate(-13deg); + `} + + @media all and (min-width: 701px) { + top: 235px; + height: 45px; + width: 90px; + margin-left: -160px; + + ${({ side }) => + side === 'right' && + css` + margin-left: 160px; + transform: translate(-50%, 0) rotate(-13deg); + `} + } + + @media all and (min-width: 1200px) { + top: 195px; + + margin-left: -110px; + + ${({ side }) => + side === 'right' && + css` + margin-left: 110px; + transform: translate(-50%, 0) rotate(-13deg); + `} + } + + @media all and (min-width: 1400px) { + top: 235px; + margin-left: -160px; + + ${({ side }) => + side === 'right' && + css` + margin-left: 160px; + transform: translate(-50%, 0) rotate(-13deg); + `} + } +`; +CruiserLight.displayName = 'CruiserLight'; + +export const InnerLight = styled.div` + label: InnerLight; + position: absolute; + width: 100%; + height: 100%; + background-color: transparent; + top: -1px; + left: -1px; + border-radius: 6px; + border: 1px solid transparent; + opacity: 0; + + ${({ delay }) => + delay && + css` + animation-delay: ${`${ANIMATION_SPEED / 2 + 40}ms`}; + `} + + @media all and (min-width: 701px) { + ${({ delay }) => + delay && + css` + animation-delay: ${`${ANIMATION_SPEED / 2}ms`}; + `} + } +`; +InnerLight.displayName = 'InnerLight'; + +export const BottomLight = styled.div` + label: BottomLight; + position: absolute; + top: 235px; +`; +BottomLight.displayName = 'BottomLight'; + +export const LightBar = styled.div` + label: LightBar; + position: relative; + padding: 2px 5px; + top: -40%; + margin: 0 auto; + border-radius: 3px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + + &:after { + top: 0; + left: 0; + position: absolute; + z-index: 10000; + display: block; + content: ''; + width: 100%; + height: 100%; + border-radius: 3px; + background: linear-gradient( + to bottom, + rgba(255, 255, 255, 0.24) 0%, + rgba(255, 255, 255, 0.24) 1%, + rgba(255, 255, 255, 0.14) 43%, + rgba(255, 255, 255, 0) 58%, + rgba(255, 255, 255, 0) 100% + ); + } + + @media all and (min-width: 360px) { + top: -45%; + } + + @media all and (min-width: 701px) { + top: -42%; + } + + @media (min-width: 1024px) and (min-height: 1300px) { + top: -570px; + } + + @media (min-width: 1300px) and (min-height: 1024px) { + top: -390px; + } + + @media all and (min-width: 1200px) { + top: -160px; + } + + @media all and (min-width: 1400px) { + top: -265px; + } +`; +LightBar.displayName = 'LightBar'; + +export const NightShiftOverlay = styled.div` + label: NightShiftOverlay; + height: 100%; + width: 100%; + background: none; + border: 0; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +`; +NightShiftOverlay.displayName = 'NightShiftOverlay'; + +export const StrobeLight = styled.div` + label: StrobeLight; + position: relative; + flex-direction: row; + align-items: center; + justify-content: center; + padding-top: ${LIGHTS_SIZE > 150 && `${LIGHTS_SIZE / 25}px`}; + width: ${`${LIGHTS_SIZE / 1.6}px`}; + height: ${`${LIGHTS_SIZE / 4}px`}; + line-height: ${`${LIGHTS_SIZE / 12}px`}; + background-color: #222; + border-top: 2px solid #111; + border-right: 1px solid #333; + border-bottom: 1px solid #333; + border-left: 2px solid #111; + border-radius: 6px; + animation-duration: ${`${ANIMATION_SPEED + 80}ms`}; + animation-name: light; + animation-iteration-count: infinite; + display: inline-block; + + ${({ color }) => { + if (color === 'red') { + return css` + animation-name: ${Strobe('#ee2819', '#ff3c2d', '#ff4444')}; + animation-delay: ${`${ANIMATION_SPEED + 40}ms`}; + `; + } + if (color === 'blue') { + return css` + animation-name: ${Strobe('#139eff', '#66d2ff', '#0078ff')}; + `; + } + return null; + }} + + @media all and (min-width: 701px) { + padding-top: ${LIGHTS_SIZE > 150 && `${LIGHTS_SIZE / 25}px`}; + width: ${`${LIGHTS_SIZE}px`}; + height: ${`${LIGHTS_SIZE / 2.7}px`}; + line-height: ${`${LIGHTS_SIZE / 6}px`}; + animation-duration: ${`${ANIMATION_SPEED}ms`}; + animation-delay: ${({ color }) => + color === 'red' && `${ANIMATION_SPEED / 2}ms`}; + } +`; +StrobeLight.displayName = 'StrobeLight'; diff --git a/src/components/MainMenu/index.js b/src/components/MainMenu/index.js index c2a57f6..cf12b14 100644 --- a/src/components/MainMenu/index.js +++ b/src/components/MainMenu/index.js @@ -18,16 +18,20 @@ import { ZombieImage, ZombieImageShadow } from './styles'; +import NightShiftIntro from './NighShift'; import { ZOMBIES_INTRO } from '../../setup/zombies'; import { CONTINUE, NEW_GAME, STOP_SOUND, TEST_SOUND } from '../../constants'; import { SOUNDS } from '../../assets/sounds'; const MainMenu = ({ loadedGame, setInitialCharacters }) => { const [testSound, toggleTestSound] = useStateWithLabel(false, 'testSound'); + const [nightShift, toggleNightShift] = useStateWithLabel(false, 'testSound'); + const APP_VERSION = appInfo.version; - const zombieImage = useRef( - ZOMBIES_INTRO[Math.floor(Math.random() * ZOMBIES_INTRO.length)] - ); + // const zombieImage = useRef( + // ZOMBIES_INTRO[Math.floor(Math.random() * ZOMBIES_INTRO.length)] + // ); + const zombieImage = useRef(ZOMBIES_INTRO[5]); useEffect(() => { const storm = new Audio(SOUNDS.intro); @@ -49,15 +53,22 @@ const MainMenu = ({ loadedGame, setInitialCharacters }) => { }; }, [testSound]); + useEffect(() => { + if (zombieImage.current.includes('ZombieCop')) { + toggleNightShift(true); + } + }, [zombieImage, toggleNightShift]); + return ( - + {nightShift && } + PARTY - - + + {NEW_GAME} diff --git a/src/components/MainMenu/styles.js b/src/components/MainMenu/styles.js index 43222b7..d7d4a2b 100644 --- a/src/components/MainMenu/styles.js +++ b/src/components/MainMenu/styles.js @@ -90,10 +90,43 @@ export const LogoArea = styled.div` justify-content: center; width: 80%; - @media all and (min-width: 768px) { + ${({ nightShift }) => + nightShift && + css` + top: -10px; + animation-name: ${Appear}; + animation-delay: 4s; + animation-duration: 3s; + animation-iteration-count: 1; + animation-fill-mode: both; + `} + + @media all and (min-width: 701px) { align-items: flex-end; justify-content: center; width: auto; + + ${({ nightShift }) => + nightShift && + css` + top: 10px; + `} + } + + @media all and (min-width: 1200px) { + ${({ nightShift }) => + nightShift && + css` + top: 0; + `} + } + + @media all and (min-width: 1400px) { + ${({ nightShift }) => + nightShift && + css` + top: 20px; + `} } `; LogoArea.displayName = 'LogoArea'; @@ -265,24 +298,30 @@ export const ZombieImage = styled.img` position: absolute; top: 10%; width: 300px; - animation-name: ${Appear}; animation-duration: 4s; animation-iteration-count: 1; animation-fill-mode: forwards; + ${({ nightShift }) => + !nightShift && + css` + animation-name: ${Appear}; + `} + @media (min-width: 320px) and (min-height: 640px) { - top: 10%; + top: 20%; width: 90%; } - @media all and (min-width: 768px) { + @media all and (min-width: 701px) { top: 150px; width: 576px; } @media all and (min-width: 1024px) { - top: 100px; - width: 400px; + top: unset; + bottom: 5%; + width: 450px; } @media (min-width: 320px) and (min-height: 1300px) { @@ -295,6 +334,11 @@ export const ZombieImage = styled.img` width: 400px; } + @media (min-width: 1300px) and (min-height: 1024px) { + top: 160px; + width: 600px; + } + @media all and (min-width: 1500px) { top: 60px; width: 500px; @@ -309,33 +353,70 @@ export const ZombieImageShadow = styled(ZombieImage)` top: 60px; width: 300px; margin: 60px 0 0 -60px; - animation-name: ${Appear}; animation-duration: 4s; animation-iteration-count: 1; animation-fill-mode: forwards; ${Shadow} + ${({ nightShift }) => + nightShift && + css` + top: unset; + bottom: -270px; + width: 320px; + transform: skew(10deg, -7deg) rotate(7deg) scaleY(0.4) scaleX(0.85) + rotateX(180deg); + `} + @media (min-width: 320px) and (min-height: 640px) { top: 10%; height: 30%; width: 90%; margin: 230px 0 0 -50px; + + ${({ nightShift }) => + nightShift && + css` + top: unset; + bottom: 11%; + width: 400px; + margin: 0 0 0 8px; + `} } - @media all and (min-width: 710px) { + @media all and (min-width: 701px) { top: 460px; height: 480px; width: 576px; margin: 0 0 0 -75px; + + ${({ nightShift }) => + nightShift && + css` + top: unset; + bottom: -160px; + width: 540px; + margin: 0 0 0 14px; + `} } @media all and (min-width: 1024px) { top: 100px; width: 400px; margin: 85px 0 0 -82px; + + ${({ nightShift }) => + nightShift && + css` + top: unset; + bottom: -25%; + width: 540px; + margin: 0 0 0 14px; + `} } + /* ipad pro */ @media (min-width: 1024px) and (min-height: 1300px) { top: 5%; height: 480px; @@ -348,13 +429,46 @@ export const ZombieImageShadow = styled(ZombieImage)` height: 340px; width: 400px; margin: 0 0 0 -50px; + + ${({ nightShift }) => + nightShift && + css` + top: unset; + bottom: -130px; + width: 540px; + margin: 0 0 0 14px; + `} } - @media all and (min-width: 1500px) { + @media (min-width: 1300px) and (min-height: 1024px) { + top: unset; + bottom: 60px; + width: 500px; + margin: 0 0 0 -60px; + + ${({ nightShift }) => + nightShift && + css` + top: unset; + bottom: -60px; + width: 640px; + margin: 0 0 0 14px; + `} + } + + @media all and (min-width: 1400px) { top: 360px; height: 390px; width: 500px; margin: 0 0 0 -60px; + ${({ nightShift }) => + nightShift && + css` + top: unset; + bottom: -100px; + width: 540px; + margin: 0 0 0 14px; + `} } `; ZombieImageShadow.displayName = 'ZombieImageShadow';