Added token animations back, edited token change event to support multiple edits

This stopped mounted vehicle animations from lagging
This commit is contained in:
Mitchell McCaffrey 2020-05-22 22:17:30 +10:00
parent ef1c875088
commit 09e423fd56
5 changed files with 64 additions and 40 deletions

View File

@ -237,7 +237,7 @@ function Map({
<TokenMenu <TokenMenu
isOpen={isTokenMenuOpen} isOpen={isTokenMenuOpen}
onRequestClose={() => setIsTokenMenuOpen(false)} onRequestClose={() => setIsTokenMenuOpen(false)}
onTokenChange={onMapTokenStateChange} onTokenStateChange={onMapTokenStateChange}
tokenState={mapState && mapState.tokens[tokenMenuOptions.tokenStateId]} tokenState={mapState && mapState.tokens[tokenMenuOptions.tokenStateId]}
tokenImage={tokenMenuOptions.tokenImage} tokenImage={tokenMenuOptions.tokenImage}
/> />

View File

@ -1,9 +1,11 @@
import React, { useContext, useState, useEffect, useRef } from "react"; import React, { useContext, useState, useEffect, useRef } from "react";
import { Image as KonvaImage, Group } from "react-konva"; import { Image as KonvaImage, Group } from "react-konva";
import Konva from "konva";
import useImage from "use-image"; import useImage from "use-image";
import useDataSource from "../../helpers/useDataSource"; import useDataSource from "../../helpers/useDataSource";
import useDebounce from "../../helpers/useDebounce"; import useDebounce from "../../helpers/useDebounce";
import usePrevious from "../../helpers/usePrevious";
import AuthContext from "../../contexts/AuthContext"; import AuthContext from "../../contexts/AuthContext";
import MapInteractionContext from "../../contexts/MapInteractionContext"; import MapInteractionContext from "../../contexts/MapInteractionContext";
@ -80,6 +82,7 @@ function MapToken({
function handleDragEnd(event) { function handleDragEnd(event) {
const tokenImage = event.target; const tokenImage = event.target;
const mountChanges = {};
if (token.isVehicle) { if (token.isVehicle) {
const layer = tokenImage.getLayer(); const layer = tokenImage.getLayer();
const mountedTokens = tokenImage.find(".token"); const mountedTokens = tokenImage.find(".token");
@ -88,21 +91,24 @@ function MapToken({
const position = mountedToken.absolutePosition(); const position = mountedToken.absolutePosition();
mountedToken.moveTo(layer); mountedToken.moveTo(layer);
mountedToken.absolutePosition(position); mountedToken.absolutePosition(position);
onTokenStateChange({ mountChanges[mountedToken.id()] = {
...mapState.tokens[mountedToken.id()], ...mapState.tokens[mountedToken.id()],
x: mountedToken.x() / mapWidth, x: mountedToken.x() / mapWidth,
y: mountedToken.y() / mapHeight, y: mountedToken.y() / mapHeight,
lastEditedBy: userId, lastEditedBy: userId,
}); };
} }
} }
setPreventMapInteraction(false); setPreventMapInteraction(false);
onTokenStateChange({ onTokenStateChange({
...tokenState, ...mountChanges,
x: tokenImage.x() / mapWidth, [tokenState.id]: {
y: tokenImage.y() / mapHeight, ...tokenState,
lastEditedBy: userId, x: tokenImage.x() / mapWidth,
y: tokenImage.y() / mapHeight,
lastEditedBy: userId,
},
}); });
onTokenDragEnd(event); onTokenDragEnd(event);
} }
@ -147,7 +153,12 @@ function MapToken({
const imageRef = useRef(); const imageRef = useRef();
useEffect(() => { useEffect(() => {
const image = imageRef.current; const image = imageRef.current;
if (image && tokenSourceStatus === "loaded") { if (
image &&
tokenSourceStatus === "loaded" &&
tokenWidth > 0 &&
tokenHeight > 0
) {
image.cache({ image.cache({
pixelRatio: debouncedStageScale * window.devicePixelRatio, pixelRatio: debouncedStageScale * window.devicePixelRatio,
}); });
@ -157,16 +168,35 @@ function MapToken({
} }
}, [debouncedStageScale, tokenWidth, tokenHeight, tokenSourceStatus]); }, [debouncedStageScale, tokenWidth, tokenHeight, tokenSourceStatus]);
if (!tokenWidth || !tokenHeight || tokenSourceStatus === "loading") { // Animate to new token positions if edited by others
return null; const containerRef = useRef();
} const tokenX = tokenState.x * mapWidth;
const tokenY = tokenState.y * mapHeight;
const previousWidth = usePrevious(mapWidth);
const previousHeight = usePrevious(mapHeight);
const resized = mapWidth !== previousWidth || mapHeight !== previousHeight;
useEffect(() => {
const container = containerRef.current;
if (container) {
if (tokenState.lastEditedBy === userId || resized) {
container.x(tokenX);
container.y(tokenY);
} else {
container.to({
x: tokenX,
y: tokenY,
duration: 0.3,
easing: Konva.Easings.EaseInOut,
});
}
}
}, [tokenX, tokenY, tokenState.lastEditedBy, userId, resized]);
return ( return (
<Group <Group
width={tokenWidth} width={tokenWidth}
height={tokenHeight} height={tokenHeight}
x={tokenState.x * mapWidth}
y={tokenState.y * mapHeight}
draggable={draggable} draggable={draggable}
onMouseDown={handlePointerDown} onMouseDown={handlePointerDown}
onMouseUp={handlePointerUp} onMouseUp={handlePointerUp}
@ -180,6 +210,7 @@ function MapToken({
opacity={tokenOpacity} opacity={tokenOpacity}
name={token && token.isVehicle ? "vehicle" : "token"} name={token && token.isVehicle ? "vehicle" : "token"}
id={tokenState.id} id={tokenState.id}
ref={containerRef}
> >
<KonvaImage <KonvaImage
ref={imageRef} ref={imageRef}

View File

@ -30,10 +30,12 @@ function TokenDragOverlay({
mountedToken.moveTo(layer); mountedToken.moveTo(layer);
mountedToken.absolutePosition(position); mountedToken.absolutePosition(position);
onTokenStateChange({ onTokenStateChange({
...mapState.tokens[mountedToken.id()], [mountedToken.id()]: {
x: mountedToken.x() / mapWidth, ...mapState.tokens[mountedToken.id()],
y: mountedToken.y() / mapHeight, x: mountedToken.x() / mapWidth,
lastEditedBy: userId, y: mountedToken.y() / mapHeight,
lastEditedBy: userId,
},
}); });
} }
} }

View File

@ -8,25 +8,12 @@ import colors, { colorOptions } from "../../helpers/colors";
import usePrevious from "../../helpers/usePrevious"; import usePrevious from "../../helpers/usePrevious";
const defaultTokenMaxSize = 6; const defaultTokenMaxSize = 6;
/**
* @callback onTokenChange
* @param {Object} token the token that was changed
*/
/**
*
* @param {string} tokenClassName The class name to attach the interactjs handler to
* @param {onProxyDragEnd} onTokenChange Called when the the token data is changed
* @param {Object} tokens An mapping of tokens to use as a base when calling onTokenChange
* @param {Object} disabledTokens An optional mapping of tokens that shouldn't allow interaction
*/
function TokenMenu({ function TokenMenu({
isOpen, isOpen,
onRequestClose, onRequestClose,
tokenState, tokenState,
tokenImage, tokenImage,
onTokenChange, onTokenStateChange,
}) { }) {
const wasOpen = usePrevious(isOpen); const wasOpen = usePrevious(isOpen);
@ -39,7 +26,7 @@ function TokenMenu({
function handleLabelChange(event) { function handleLabelChange(event) {
const label = event.target.value; const label = event.target.value;
onTokenChange({ ...tokenState, label: label }); onTokenStateChange({ [tokenState.id]: { ...tokenState, label: label } });
} }
const [menuLeft, setMenuLeft] = useState(0); const [menuLeft, setMenuLeft] = useState(0);
@ -66,17 +53,21 @@ function TokenMenu({
} else { } else {
newStatuses = [...statuses, status]; newStatuses = [...statuses, status];
} }
onTokenChange({ ...tokenState, statuses: newStatuses }); onTokenStateChange({
[tokenState.id]: { ...tokenState, statuses: newStatuses },
});
} }
function handleSizeChange(event) { function handleSizeChange(event) {
const newSize = parseInt(event.target.value); const newSize = parseInt(event.target.value);
onTokenChange({ ...tokenState, size: newSize }); onTokenStateChange({ [tokenState.id]: { ...tokenState, size: newSize } });
} }
function handleRotationChange(event) { function handleRotationChange(event) {
const newRotation = parseInt(event.target.value); const newRotation = parseInt(event.target.value);
onTokenChange({ ...tokenState, rotation: newRotation }); onTokenStateChange({
[tokenState.id]: { ...tokenState, rotation: newRotation },
});
} }
function handleModalContent(node) { function handleModalContent(node) {

View File

@ -244,10 +244,11 @@ function Game() {
peer.connection.send({ id: "token", data: rest }); peer.connection.send({ id: "token", data: rest });
} }
} }
handleMapTokenStateChange(tokenState); handleMapTokenStateChange({ [tokenState.id]: tokenState });
} }
function handleMapTokenStateChange(tokenState) { function handleMapTokenStateChange(change) {
console.log(change);
if (currentMapState === null) { if (currentMapState === null) {
return; return;
} }
@ -255,12 +256,11 @@ function Game() {
...prevMapState, ...prevMapState,
tokens: { tokens: {
...prevMapState.tokens, ...prevMapState.tokens,
[tokenState.id]: tokenState, ...change,
}, },
})); }));
for (let peer of Object.values(peers)) { for (let peer of Object.values(peers)) {
const data = { [tokenState.id]: tokenState }; peer.connection.send({ id: "tokenStateEdit", data: change });
peer.connection.send({ id: "tokenStateEdit", data });
} }
} }