Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions art/VariantConfiguration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export default new Map<SpriteVariant, SpriteVariantConfiguration>([
-2,
-3,
]),
asImage: true,
},
],
[
Expand Down
87 changes: 44 additions & 43 deletions hera/character/Portrait.tsx
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;
Expand All @@ -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 = [
Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(position.y + (variant || 0)) * PortraitHeight,
(position.y + (variant || 0) + 6) * PortraitHeight,
];
let curKeyFrameY = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call this currentPosition, and move it inside the useLayoutEffect call. We can't mutate variables created in render like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


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);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to return () => clearInterval(interval) so that it gets stopped properly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also the effect needs to support the paused flag and ensure the animation is not started.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}, [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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need this line since we aren't overflowing the element anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image-rendering: pixelated;
Expand All @@ -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;
`;