Moved tokens to be relatively positioned and scaled to the current map
This commit is contained in:
parent
c25566f682
commit
33ea19fef6
@ -1,5 +1,13 @@
|
|||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState, useEffect } from "react";
|
||||||
import { IconButton, Box, Button, Image, Flex, Label, Input } from "theme-ui";
|
import {
|
||||||
|
IconButton,
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
Image as UIImage,
|
||||||
|
Flex,
|
||||||
|
Label,
|
||||||
|
Input
|
||||||
|
} from "theme-ui";
|
||||||
|
|
||||||
import Modal from "./Modal";
|
import Modal from "./Modal";
|
||||||
|
|
||||||
@ -22,11 +30,26 @@ function AddMapButton({ handleMapChange }) {
|
|||||||
setIsAddModalOpen(false);
|
setIsAddModalOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [imageLoaded, setImageLoaded] = useState(false);
|
||||||
|
|
||||||
const mapDataRef = useRef(null);
|
const mapDataRef = useRef(null);
|
||||||
const [mapSource, setImageSource] = useState(null);
|
const [mapSource, setMapSource] = useState(null);
|
||||||
function handleImageUpload(event) {
|
function handleImageUpload(event) {
|
||||||
mapDataRef.current = { file: event.target.files[0], rows, columns };
|
const file = event.target.files[0];
|
||||||
setImageSource(URL.createObjectURL(mapDataRef.current.file));
|
const url = URL.createObjectURL(file);
|
||||||
|
let image = new Image();
|
||||||
|
image.onload = function() {
|
||||||
|
mapDataRef.current = {
|
||||||
|
file,
|
||||||
|
rows,
|
||||||
|
columns,
|
||||||
|
width: image.width,
|
||||||
|
height: image.height
|
||||||
|
};
|
||||||
|
setImageLoaded(true);
|
||||||
|
};
|
||||||
|
image.src = url;
|
||||||
|
setMapSource(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDone() {
|
function handleDone() {
|
||||||
@ -38,6 +61,12 @@ function AddMapButton({ handleMapChange }) {
|
|||||||
|
|
||||||
const [rows, setRows] = useState(defaultMapSize);
|
const [rows, setRows] = useState(defaultMapSize);
|
||||||
const [columns, setColumns] = useState(defaultMapSize);
|
const [columns, setColumns] = useState(defaultMapSize);
|
||||||
|
useEffect(() => {
|
||||||
|
if (mapDataRef.current) {
|
||||||
|
mapDataRef.current.rows = rows;
|
||||||
|
mapDataRef.current.columns = columns;
|
||||||
|
}
|
||||||
|
}, [rows, columns]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -70,7 +99,7 @@ function AddMapButton({ handleMapChange }) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Flex sx={{ flexDirection: "column" }}>
|
<Flex sx={{ flexDirection: "column" }}>
|
||||||
<Image
|
<UIImage
|
||||||
my={2}
|
my={2}
|
||||||
sx={{
|
sx={{
|
||||||
width: "500px",
|
width: "500px",
|
||||||
@ -104,7 +133,9 @@ function AddMapButton({ handleMapChange }) {
|
|||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
{mapSource ? (
|
{mapSource ? (
|
||||||
<Button variant="primary">Done</Button>
|
<Button variant="primary" disabled={!imageLoaded}>
|
||||||
|
Done
|
||||||
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
varient="primary"
|
varient="primary"
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import React from "react";
|
import React, { useRef } from "react";
|
||||||
import { Image, Flex, Box } from "theme-ui";
|
import { Box, Image } from "theme-ui";
|
||||||
|
|
||||||
import Token from "../components/Token";
|
import Token from "../components/Token";
|
||||||
import ProxyToken from "../components/ProxyToken";
|
import ProxyToken from "../components/ProxyToken";
|
||||||
|
|
||||||
const mapTokenClassName = "map-token";
|
const mapTokenClassName = "map-token";
|
||||||
|
const defaultTokenSize = 48;
|
||||||
|
|
||||||
function Map({ imageSource, tokens, onMapTokenMove, onMapTokenRemove }) {
|
function Map({ mapSource, mapData, tokens, onMapTokenMove, onMapTokenRemove }) {
|
||||||
function handleProxyDragEnd(isOnMap, token) {
|
function handleProxyDragEnd(isOnMap, token) {
|
||||||
if (isOnMap && onMapTokenMove) {
|
if (isOnMap && onMapTokenMove) {
|
||||||
onMapTokenMove(token);
|
onMapTokenMove(token);
|
||||||
@ -17,43 +18,76 @@ function Map({ imageSource, tokens, onMapTokenMove, onMapTokenRemove }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mapRef = useRef(null);
|
||||||
|
const rows = mapData && mapData.rows;
|
||||||
|
const tokenSizePercent = (1 / rows) * 100;
|
||||||
|
const aspectRatio = (mapData && mapData.width / mapData.height) || 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Flex
|
<Box
|
||||||
className="map"
|
className="map"
|
||||||
sx={{ justifyContent: "center", flexGrow: 1 }}
|
sx={{ flexGrow: 1, position: "relative", overflow: "hidden" }}
|
||||||
bg="background"
|
bg="background"
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: "absolute",
|
position: "relative",
|
||||||
top: 0,
|
overflow: "hidden",
|
||||||
left: 0,
|
top: "50%",
|
||||||
right: 0,
|
left: "50%",
|
||||||
userSelect: "none"
|
transform: "translate(-50%, -50%)"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{Object.values(tokens).map(token => (
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
key={token.id}
|
width: "100%",
|
||||||
sx={{
|
height: 0,
|
||||||
position: "absolute",
|
paddingBottom: `${(1 / aspectRatio) * 100}%`
|
||||||
transform: `translate(${token.x}px, ${token.y}px)`
|
}}
|
||||||
}}
|
/>
|
||||||
>
|
|
||||||
<Token
|
<Box
|
||||||
tokenId={token.id}
|
sx={{ position: "absolute", top: 0, right: 0, bottom: 0, left: 0 }}
|
||||||
image={token.image}
|
>
|
||||||
className={mapTokenClassName}
|
<Image ref={mapRef} id="map" src={mapSource} />
|
||||||
/>
|
</Box>
|
||||||
</Box>
|
<Box
|
||||||
))}
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{Object.values(tokens).map(token => (
|
||||||
|
<Box
|
||||||
|
key={token.id}
|
||||||
|
style={{
|
||||||
|
left: `${token.x * 100}%`,
|
||||||
|
top: `${token.y * 100}%`,
|
||||||
|
width: `${tokenSizePercent}%`
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
display: "flex"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Token
|
||||||
|
tokenId={token.id}
|
||||||
|
image={token.image}
|
||||||
|
className={mapTokenClassName}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
<Image src={imageSource} sx={{ objectFit: "contain" }} />
|
</Box>
|
||||||
</Flex>
|
|
||||||
<ProxyToken
|
<ProxyToken
|
||||||
tokenClassName={mapTokenClassName}
|
tokenClassName={mapTokenClassName}
|
||||||
onProxyDragEnd={handleProxyDragEnd}
|
onProxyDragEnd={handleProxyDragEnd}
|
||||||
|
size={defaultTokenSize}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -5,7 +5,7 @@ import interact from "interactjs";
|
|||||||
|
|
||||||
import usePortal from "../helpers/usePortal";
|
import usePortal from "../helpers/usePortal";
|
||||||
|
|
||||||
function ProxyToken({ tokenClassName, onProxyDragEnd }) {
|
function ProxyToken({ tokenClassName, onProxyDragEnd, size }) {
|
||||||
const proxyContainer = usePortal("root");
|
const proxyContainer = usePortal("root");
|
||||||
|
|
||||||
const [imageSource, setImageSource] = useState("");
|
const [imageSource, setImageSource] = useState("");
|
||||||
@ -67,8 +67,20 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) {
|
|||||||
let proxy = imageRef.current;
|
let proxy = imageRef.current;
|
||||||
if (proxy) {
|
if (proxy) {
|
||||||
if (onProxyDragEnd) {
|
if (onProxyDragEnd) {
|
||||||
const x = parseFloat(proxy.getAttribute("data-x")) || 0;
|
// TODO: look at cleaning this up with a props instead of
|
||||||
const y = parseFloat(proxy.getAttribute("data-y")) || 0;
|
// hard coding an id
|
||||||
|
const map = document.getElementById("map");
|
||||||
|
const mapRect = map.getBoundingClientRect();
|
||||||
|
|
||||||
|
let x = parseFloat(proxy.getAttribute("data-x")) || 0;
|
||||||
|
let y = parseFloat(proxy.getAttribute("data-y")) || 0;
|
||||||
|
// Convert coordiantes to be relative to the map
|
||||||
|
x = x - mapRect.left;
|
||||||
|
y = y - mapRect.top;
|
||||||
|
// Normalize to map width
|
||||||
|
x = x / (mapRect.right - mapRect.left);
|
||||||
|
y = y / (mapRect.bottom - mapRect.top);
|
||||||
|
|
||||||
const id = target.getAttribute("data-token-id");
|
const id = target.getAttribute("data-token-id");
|
||||||
onProxyDragEnd(proxyOnMap.current, {
|
onProxyDragEnd(proxyOnMap.current, {
|
||||||
image: imageSource,
|
image: imageSource,
|
||||||
@ -109,11 +121,10 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
p={2}
|
|
||||||
src={imageSource}
|
src={imageSource}
|
||||||
sx={{
|
sx={{
|
||||||
width: "64px",
|
width: `${size}px`,
|
||||||
height: "64px",
|
height: `${size}px`,
|
||||||
touchAction: "none",
|
touchAction: "none",
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
position: "absolute"
|
position: "absolute"
|
||||||
@ -125,4 +136,6 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProxyToken.defaultProps = { size: 48 };
|
||||||
|
|
||||||
export default ProxyToken;
|
export default ProxyToken;
|
||||||
|
@ -4,18 +4,7 @@ import { Image } from "theme-ui";
|
|||||||
function Token({ image, className, tokenId }) {
|
function Token({ image, className, tokenId }) {
|
||||||
// Store the token id in the html element for the drag code to pick it up
|
// Store the token id in the html element for the drag code to pick it up
|
||||||
const idProp = tokenId ? { "data-token-id": tokenId } : {};
|
const idProp = tokenId ? { "data-token-id": tokenId } : {};
|
||||||
return (
|
return <Image className={className} src={image} {...idProp} />;
|
||||||
<Image
|
|
||||||
p={2}
|
|
||||||
className={className}
|
|
||||||
src={image}
|
|
||||||
sx={{
|
|
||||||
width: "64px",
|
|
||||||
height: "64px"
|
|
||||||
}}
|
|
||||||
{...idProp}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Token;
|
export default Token;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Flex } from "theme-ui";
|
import { Flex, Box } from "theme-ui";
|
||||||
import shortid from "shortid";
|
import shortid from "shortid";
|
||||||
|
|
||||||
import * as tokens from "../tokens";
|
import * as tokens from "../tokens";
|
||||||
@ -30,7 +30,9 @@ function Tokens({ onCreateMapToken }) {
|
|||||||
px={2}
|
px={2}
|
||||||
>
|
>
|
||||||
{Object.entries(tokens).map(([id, image]) => (
|
{Object.entries(tokens).map(([id, image]) => (
|
||||||
<Token key={id} image={image} className={listTokenClassName} />
|
<Box key={id} m={2} sx={{ width: "48px", height: "48px" }}>
|
||||||
|
<Token image={image} className={listTokenClassName} />
|
||||||
|
</Box>
|
||||||
))}
|
))}
|
||||||
</Flex>
|
</Flex>
|
||||||
<ProxyToken
|
<ProxyToken
|
||||||
|
@ -97,6 +97,19 @@ function Game() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex sx={{ flexDirection: "column", height: "100vh" }}>
|
<Flex sx={{ flexDirection: "column", height: "100vh" }}>
|
||||||
|
<Flex
|
||||||
|
sx={{ justifyContent: "space-between", flexGrow: 1, height: "100%" }}
|
||||||
|
>
|
||||||
|
<Party streams={streams} localStreamId={peerId} />
|
||||||
|
<Map
|
||||||
|
mapSource={mapSource}
|
||||||
|
mapData={mapDataRef.current}
|
||||||
|
tokens={mapTokens}
|
||||||
|
onMapTokenMove={handleEditMapToken}
|
||||||
|
onMapTokenRemove={handleRemoveMapToken}
|
||||||
|
/>
|
||||||
|
<Tokens onCreateMapToken={handleEditMapToken} />
|
||||||
|
</Flex>
|
||||||
<Box
|
<Box
|
||||||
p={2}
|
p={2}
|
||||||
sx={{
|
sx={{
|
||||||
@ -108,18 +121,6 @@ function Game() {
|
|||||||
>
|
>
|
||||||
<AddMapButton handleMapChange={handleMapChange} />
|
<AddMapButton handleMapChange={handleMapChange} />
|
||||||
</Box>
|
</Box>
|
||||||
<Flex
|
|
||||||
sx={{ justifyContent: "space-between", flexGrow: 1, height: "100%" }}
|
|
||||||
>
|
|
||||||
<Party streams={streams} localStreamId={peerId} />
|
|
||||||
<Map
|
|
||||||
imageSource={mapSource}
|
|
||||||
tokens={mapTokens}
|
|
||||||
onMapTokenMove={handleEditMapToken}
|
|
||||||
onMapTokenRemove={handleRemoveMapToken}
|
|
||||||
/>
|
|
||||||
<Tokens onCreateMapToken={handleEditMapToken} />
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user