Skip to content

Commit

Permalink
feat: pixel mode complete for raster images (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomchen committed Sep 26, 2021
1 parent 59c89ef commit de1aa65
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 124 deletions.
14 changes: 14 additions & 0 deletions __test__/components/Pixelation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,17 @@ it('Pixelation with fit cover is good', () => {
jest.runAllTimers()
wrapper.unmount()
})

it('Pixelation with width and height is good', () => {
const wrapper = mount(
<Pixelation src="bg.webp" level={4} width="200%" height="300%" />,
)
jest.runAllTimers()
wrapper.unmount()
})

it('Pixelation with cleanup is good', () => {
const wrapper = mount(<Pixelation src="bg.webp" level={4} cleanup />)
jest.runAllTimers()
wrapper.unmount()
})
111 changes: 78 additions & 33 deletions src/components/Tower.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo, useContext, useRef } from 'react'
import React, { memo, useContext, useEffect, useRef, useState } from 'react'
import cx from 'classnames'
import { createUseStyles } from 'react-jss'
import { GameSizeContext } from '../utils/GameSizeContext'
Expand All @@ -10,6 +10,11 @@ import towerBlue from '../../assets/img/tower_blue.webp'
import { I18nContext } from '../i18n/I18nContext'
import { useAppSelector } from '../utils/useAppDispatch'
import TooltipAll from './special/TooltipAll'
import Pixelation from './effects/Pixelation'
import {
towerPixelationFallbackHeight,
towerWallHeightDelay,
} from '../constants/visuals'

const calcBaseRatio = (height: number): string =>
`(${height}px - (1.75rem + 0.25rem * 2)) / (282 + 600)`
Expand Down Expand Up @@ -49,35 +54,29 @@ const useStyles = createUseStyles<string, { height: number; goal: number }>({
'transition-property': 'height',
'transition-timing-function': 'linear',
'transition-duration': '0.4s',

'&:before': {
content: '""',
position: 'absolute',
top: ({ height }) => `calc(0px - ${calcWidth(height)} / 204 * 282 + 1px)`,
left: ({ height }) => `calc(0px - ${calcPaddingX(height)})`,
right: ({ height }) => `calc(0px - ${calcPaddingX(height)})`,
width: ({ height }) => `calc(${calcWidth(height)})`,
height: ({ height }) => `calc(${calcWidth(height)} / 204 * 282)`,
display: 'block',
background: {
repeat: 'no-repeat',
size: '100%',
position: 'center 0',
},
},
towerbodytop: {
position: 'absolute',
top: ({ height }) => `calc(0px - ${calcWidth(height)} / 204 * 282 + 1px)`,
left: ({ height }) => `calc(0px - ${calcPaddingX(height)})`,
right: ({ height }) => `calc(0px - ${calcPaddingX(height)})`,
width: ({ height }) => `calc(${calcWidth(height)})`,
height: ({ height }) => `calc(${calcWidth(height)} / 204 * 282)`,
display: 'block',
background: {
repeat: 'no-repeat',
size: '100%',
position: 'center 0',
},
},
towerbodyred: {
'&:before': {
background: {
image: `url(${towerRed})`,
},
towerbodytopred: {
background: {
image: `url(${towerRed})`,
},
},
towerbodyblue: {
'&:before': {
background: {
image: `url(${towerBlue})`,
},
towerbodytopblue: {
background: {
image: `url(${towerBlue})`,
},
},
})
Expand All @@ -91,8 +90,6 @@ const Tower = ({ isOpponent = false, goal }: PropType) => {
const size = useContext(GameSizeContext)
const height = size.height * (size.narrowMobile ? 1 / 2 : 2 / 3)

const towerBody = useRef<HTMLDivElement | null>(null)

const classes = useStyles({ height, goal })

// Force TailwindCSS to aware of these classes:
Expand All @@ -108,6 +105,34 @@ const Tower = ({ isOpponent = false, goal }: PropType) => {
.replace('%s1', towerTitle)
.replace('%s2', winTower.toString(10))

const pixelationLevel = useAppSelector((state) => state.visual.pixelation)

const towerBody = useRef<HTMLDivElement | null>(null)
const [towerBodyMaxHeight, setTowerBodyMaxHeight] = useState(
towerPixelationFallbackHeight,
)
useEffect(() => {
const handleResize = () => {
setTimeout(() => {
if (pixelationLevel !== 0 && towerBody.current) {
setTowerBodyMaxHeight(
window
.getComputedStyle(towerBody.current)
.getPropertyValue('max-height'),
)
}
}, towerWallHeightDelay)
}

window.addEventListener('resize', handleResize)
window.addEventListener('orientationchange', handleResize)
handleResize()
return () => {
window.removeEventListener('resize', handleResize)
window.removeEventListener('orientationchange', handleResize)
}
}, [])

return (
<div
className={cx(
Expand All @@ -121,12 +146,32 @@ const Tower = ({ isOpponent = false, goal }: PropType) => {
<div className={cx('z-20 w-full absolute', classes.towerwrapper)}>
<div
ref={towerBody}
className={cx(
'absolute bottom-0',
classes.towerbody,
classes[isOpponent ? 'towerbodyblue' : 'towerbodyred'],
className={cx('absolute bottom-0', classes.towerbody)}
>
<div
className={cx(
classes.towerbodytop,
classes[isOpponent ? 'towerbodytopblue' : 'towerbodytopred'],
)}
>
{pixelationLevel !== 0 && (
<Pixelation
src={isOpponent ? towerBlue : towerRed}
level={pixelationLevel}
cleanup
/>
)}
</div>
{pixelationLevel !== 0 && (
<div className="w-full h-full overflow-hidden">
<Pixelation
src={tower}
level={pixelationLevel}
height={towerBodyMaxHeight}
/>
</div>
)}
></div>
</div>
</div>
<div className="bg-black bg-opacity-50 p-1 shadow-lg w-full absolute bottom-0">
<div className="border border-yellow-400 border-opacity-25 text-yellow-400 text-center h-7 leading-7 font-mono">
Expand Down
47 changes: 41 additions & 6 deletions src/components/Wall.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import React, { memo, useContext, useRef } from 'react'
import React, { memo, useContext, useEffect, useRef, useState } from 'react'
import cx from 'classnames'
import { createUseStyles } from 'react-jss'
import { GameSizeContext } from '../utils/GameSizeContext'
import TowerOrWallNumber from './TowerOrWallNumber'
import { maxWallOnScreen } from '../constants/visuals'
import {
maxWallOnScreen,
towerWallHeightDelay,
wallPixelationFallbackHeight,
} from '../constants/visuals'

import wall from '../../assets/img/wall.webp'
import { I18nContext } from '../i18n/I18nContext'
Expand Down Expand Up @@ -51,8 +55,6 @@ const Wall = ({ isOpponent = false }: PropType) => {
const size = useContext(GameSizeContext)
const height = size.height * (size.narrowMobile ? 1 / 2 : 2 / 3)

const wallBody = useRef<HTMLDivElement | null>(null)

const classes = useStyles(height)

// Force TailwindCSS to aware of these classes:
Expand All @@ -66,6 +68,32 @@ const Wall = ({ isOpponent = false }: PropType) => {

const pixelationLevel = useAppSelector((state) => state.visual.pixelation)

const wallBody = useRef<HTMLDivElement | null>(null)
const [wallBodyMaxHeight, setWallBodyMaxHeight] = useState(
wallPixelationFallbackHeight,
)
useEffect(() => {
const handleResize = () => {
setTimeout(() => {
if (pixelationLevel !== 0 && wallBody.current) {
setWallBodyMaxHeight(
window
.getComputedStyle(wallBody.current)
.getPropertyValue('max-height'),
)
}
}, towerWallHeightDelay)
}

window.addEventListener('resize', handleResize)
window.addEventListener('orientationchange', handleResize)
handleResize()
return () => {
window.removeEventListener('resize', handleResize)
window.removeEventListener('orientationchange', handleResize)
}
}, [])

return (
<div
className={cx(
Expand All @@ -79,10 +107,17 @@ const Wall = ({ isOpponent = false }: PropType) => {
<div className={cx('z-20 w-full absolute px-4', classes.wallwrapper)}>
<div
ref={wallBody}
className={cx('absolute bottom-0', classes.wallbody)}
className={cx(
'absolute bottom-0 overflow-hidden',
classes.wallbody,
)}
>
{pixelationLevel !== 0 && (
<Pixelation src={wall} level={pixelationLevel} />
<Pixelation
src={wall}
level={pixelationLevel}
height={wallBodyMaxHeight}
/>
)}
</div>
</div>
Expand Down
Loading

0 comments on commit de1aa65

Please sign in to comment.