-
-
Notifications
You must be signed in to change notification settings - Fork 141
perf: draw portrait with canvas #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
6425d8c
9331080
a3f0f3b
e2e993f
ef766b5
8b698e1
5183c96
f48f2fb
ad95d2b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,12 @@ | ||
| import { spriteURL } from '@deities/art/Sprites.tsx'; | ||
| import { spriteImage } from '@deities/art/Sprites.tsx'; | ||
| import { UnitInfo } from '@deities/athena/info/Unit.tsx'; | ||
| import { | ||
| DynamicPlayerID, | ||
| encodeDynamicPlayerID, | ||
| } from '@deities/athena/map/Player.tsx'; | ||
| import clipBorder from '@deities/ui/clipBorder.tsx'; | ||
| import { CSSVariables } from '@deities/ui/cssVar.tsx'; | ||
| import { css, cx, keyframes } from '@emotion/css'; | ||
| import { memo } from 'react'; | ||
| import { css, cx } from '@emotion/css'; | ||
| import { memo, useLayoutEffect, useRef } from 'react'; | ||
| import { useSprites } from '../hooks/useSprites.tsx'; | ||
|
|
||
| export const PortraitWidth = 55; | ||
|
|
@@ -30,43 +29,64 @@ export default memo(function Portrait({ | |
| unit: UnitInfo; | ||
| variant?: number; | ||
| }) { | ||
| const ref = useRef<HTMLCanvasElement>(null); | ||
| const hasPortraits = useSprites('portraits'); | ||
| const sprite = hasPortraits | ||
| ? spriteURL('Portraits', encodeDynamicPlayerID(player)) | ||
| : ''; | ||
|
|
||
| const { position } = unit.sprite.portrait; | ||
| const y = -(position.y + (variant || 0)) * PortraitHeight + 'px'; | ||
| const alternateY = -(position.y + (variant || 0) + 6) * PortraitHeight + 'px'; | ||
| const keyFrameYs = [ | ||
| (position.y + (variant || 0)) * PortraitHeight, | ||
| (position.y + (variant || 0) + 6) * PortraitHeight, | ||
| ]; | ||
| let curKeyFrameY = 0; | ||
|
||
|
|
||
| useLayoutEffect(() => { | ||
| if (!hasPortraits) { | ||
| return; | ||
| } | ||
|
|
||
| const canvas = ref.current; | ||
| if (!canvas) { | ||
| return; | ||
| } | ||
|
|
||
| const image = spriteImage('Portraits', encodeDynamicPlayerID(player)); | ||
| const context = canvas.getContext('2d')!; | ||
|
|
||
| function animate() { | ||
| context.drawImage( | ||
| image, | ||
| position.x * PortraitWidth, | ||
| keyFrameYs[curKeyFrameY], | ||
| PortraitWidth, | ||
| PortraitHeight, | ||
| 0, | ||
| 0, | ||
| PortraitWidth, | ||
| PortraitHeight, | ||
| ); | ||
|
|
||
| curKeyFrameY = (curKeyFrameY + 1) % keyFrameYs.length; | ||
| } | ||
|
|
||
| if (animate) { | ||
| setInterval(animate, 1000 / keyFrameYs.length); | ||
| } | ||
|
||
| }, [hasPortraits]); | ||
|
|
||
| return ( | ||
| <div | ||
| className={cx(portraitStyle, clip && clipStyle)} | ||
| style={{ | ||
| [vars.set('x')]: -position.x * PortraitWidth + 'px', | ||
| [vars.set('y')]: y, | ||
| [vars.set('alternate-y')]: alternateY, | ||
| height: PortraitHeight, | ||
| width: PortraitWidth, | ||
| zoom: scale, | ||
| }} | ||
| > | ||
| {sprite && ( | ||
| <img | ||
| className={cx( | ||
| spriteStyle, | ||
| animate && animateStyle, | ||
| paused && pausedStyle, | ||
| )} | ||
| src={sprite} | ||
| /> | ||
| )} | ||
| <canvas ref={ref}></canvas> | ||
| </div> | ||
| ); | ||
| }); | ||
|
|
||
| const vars = new CSSVariables<'x' | 'y' | 'alternate-y'>('p'); | ||
|
|
||
| const portraitStyle = css` | ||
| contain: content; | ||
|
||
| image-rendering: pixelated; | ||
|
|
@@ -76,22 +96,3 @@ const portraitStyle = css` | |
| const clipStyle = css` | ||
| ${clipBorder(2)} | ||
| `; | ||
|
|
||
| const spriteStyle = css` | ||
| transform: translate3d(${vars.apply('x')}, ${vars.apply('y')}, 0); | ||
| `; | ||
|
|
||
| const animateStyle = css` | ||
| animation: ${keyframes` | ||
| 0%, 100% { | ||
| transform: translate3d(${vars.apply('x')}, ${vars.apply('y')}, 0); | ||
| } | ||
| 50% { | ||
| transform: translate3d(${vars.apply('x')}, ${vars.apply('alternate-y')}, 0); | ||
| } | ||
| `} 1s infinite steps(1); | ||
| `; | ||
|
|
||
| const pausedStyle = css` | ||
| animation-play-state: paused; | ||
| `; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can call this
positions?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refactor: rename variables