Skip to content
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

フィールドにいろいろを表示 #104

Merged
merged 8 commits into from
Jan 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
49 changes: 31 additions & 18 deletions client/src/pixi/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "../model/position";
import Explorer from "./components/Explorer";
import PIXI from "pixi.js";
import useExplorerDispatcher from "../api/explorer";

const mountHandler = import.meta.env.DEV
? (app: PIXI.Application) => {
Expand All @@ -40,6 +41,7 @@ const Canvas: React.FC<Props> = (props) => {
} | null>(null);
const [intervalID, setIntervalID] = useState<number | null>(null);
const stageRef = useRef<HTMLDivElement>(null);
const dispatcher = useExplorerDispatcher();

useEffect(() => {
const width = (window.innerWidth * 3) / 5;
Expand All @@ -66,25 +68,36 @@ const Canvas: React.FC<Props> = (props) => {
};
}, [fieldSize]);

const updateUserPosition = useCallback((targetPosition: Position) => {
setUserPosition((position) => {
if (position === null) {
return null;
}
const diff = {
x: targetPosition.x - position.x,
y: targetPosition.y - position.y,
};
if (Math.abs(diff.x) < 3 && Math.abs(diff.y) < 3) {
return targetPosition;
}
const nextPosition = calcNewPosition(position, {
x: diff.x / 10,
y: diff.y / 10,
const updateUserPosition = useCallback(
(targetPosition: Position) => {
setUserPosition((position) => {
if (position === null || fieldSize === null) {
return null;
}
const diff = {
x: targetPosition.x - position.x,
y: targetPosition.y - position.y,
};
if (Math.abs(diff.x) < 3 && Math.abs(diff.y) < 3) {
dispatcher({
position: targetPosition,
size: fieldSize,
});
return targetPosition;
}
const nextPosition = calcNewPosition(position, {
x: diff.x / 10,
y: diff.y / 10,
});
dispatcher({
position: nextPosition,
size: fieldSize,
});
return nextPosition;
});
return nextPosition;
});
}, []);
},
[dispatcher, fieldSize],
);

const onFieldClick = useCallback(
(e: React.MouseEvent<HTMLCanvasElement, MouseEvent>) => {
Expand Down
144 changes: 92 additions & 52 deletions client/src/pixi/World.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import messagesAtom from "../state/message";
import MessageIcon from "./components/MessageIcon";
import useMessageExpanded from "./hooks/message";
import { isInsideField } from "../util/field";
import speakerPhonesAtom from "../state/speakerPhone";
import reactionsAtom from "../state/reactions";
import explorersAtom from "../state/explorer";
import OtherExplorer from "./components/OtherExplorer";

interface Props {
userPosition: Position;
Expand All @@ -27,7 +31,11 @@ const World: React.FC<Props> = ({
const { expanded, collapseMessage, expandMessage, message } =
useMessageExpanded();
const messages = useAtomValue(messagesAtom);
const messageNodes = [];
const speakerPhones = useAtomValue(speakerPhonesAtom);
const reactions = useAtomValue(reactionsAtom);
const explorers = useAtomValue(explorersAtom);

const messageNodes: JSX.Element[] = [];
for (const message of messages.values()) {
if (!isInsideField(message.position, fieldSize, userPosition)) {
continue;
Expand All @@ -42,24 +50,88 @@ const World: React.FC<Props> = ({
);
}

//TODO: モック用なので後で消す
for (let i = 1; i <= 3; i++) {
messageNodes.push(
<MessageIcon
currentExpandedMessageId={message?.id}
expander={expandMessage}
key={i}
message={{
id: i.toString(),
position: { x: 100 * i + 10, y: 100 * i },
userId: "ikura-hamu",
content: "Hello, World!".repeat(i * 5),
createdAt: new Date(),
updatedAt: new Date(),
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 1 day later
const speakerPhoneNodes = Array.from(speakerPhones.values())
.filter((speakerPhone) =>
isInsideField(speakerPhone.position, fieldSize, userPosition),
)
.map((speakerPhone) => (
<SpeakerPhone
key={speakerPhone.name}
position={speakerPhone.position}
name={speakerPhone.name}
radius={100}
/>
));

const reactionsNodes = Array.from(reactions.values())
.filter((reaction) =>
isInsideField(reaction.position, fieldSize, userPosition),
)
.map((reaction) => (
<Reaction
key={reaction.id}
position={reaction.position}
reaction={reaction.kind}
user={{
name: reaction.userId,
iconURL: traqIconURL(reaction.userId),
}}
/>,
/>
));

const explorerNodes = Array.from(explorers.values()).map((explorer) => {
return (
<OtherExplorer
key={explorer.userId}
explorer={explorer}
previousPosition={explorer.previousPosition}
/>
);
});

//TODO: モック用なので後で消す
{
for (let i = 1; i <= 3; i++) {
messageNodes.push(
<MessageIcon
currentExpandedMessageId={message?.id}
expander={expandMessage}
key={i}
message={{
id: i.toString(),
position: { x: 100 * i + 10, y: 100 * i },
userId: "ikura-hamu",
content: "Hello, World!".repeat(i * 5),
createdAt: new Date(),
updatedAt: new Date(),
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 1 day later
}}
/>,
);
}
for (let i = 4; i <= 6; i++) {
speakerPhoneNodes.push(
<SpeakerPhone
key={i}
position={{ x: 100 * i + 10, y: 100 * i }}
name="SpeakerPhone"
radius={100}
/>,
);
}
for (let i = 7; i <= 9; i++) {
reactionsNodes.push(
<Reaction
key={i}
position={{ x: 100 * i + 10, y: 100 * i }}
reaction="iine"
user={{
name: "ikura-hamu",
iconURL: traqIconURL("ikura-hamu"),
}}
/>,
);
}
}

return (
Expand All @@ -79,42 +151,10 @@ const World: React.FC<Props> = ({
fillColor={0xeeeeee}
fillAlpha={1}
/>
<SpeakerPhone
position={{ x: 1700, y: 1700 }}
name="#gps/times/ikura-hamu"
radius={100}
/>
<SpeakerPhone
position={{ x: 200, y: 200 }}
name="#gps/times/ikura-hamu"
radius={100}
/>

{speakerPhoneNodes}
{messageNodes}
<Reaction
position={{ x: 300, y: 300 }}
reaction="kusa"
user={{
name: "SSlime",
iconURL: traqIconURL("SSlime"),
}}
/>
<Reaction
position={{ x: 200, y: 500 }}
reaction="iine"
user={{
name: "Ras",
iconURL: traqIconURL("Ras"),
}}
/>
<Reaction
position={{ x: 250, y: 500 }}
reaction="pro"
user={{
name: "H1rono_K",
iconURL: traqIconURL("H1rono_K"),
}}
/>
{explorerNodes}
{reactionsNodes}
<Message
expanded={expanded}
message={message}
Expand Down
60 changes: 60 additions & 0 deletions client/src/pixi/components/OtherExplorer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Position } from "../../model/position";
import ExplorerModel from "../../model/explorer";
import Explorer from "./Explorer";
import { useUser } from "../../api/user";
import { traqIconURL } from "../../util/icon";
import { useTick } from "@pixi/react";
import { useState } from "react";

interface Props {
explorer: ExplorerModel;
previousPosition?: Position;
}

const OtherExplorer: React.FC<Props> = ({ explorer, previousPosition }) => {
// previousPosition: 1つ前の受信時の位置
const { position: targetPosition, userId } = explorer; // 目標の位置
const { data, error, isLoading } = useUser(userId);
const [position, setPosition] = useState(targetPosition); // 実際の現在の位置

useTick(() => {
if (previousPosition) {
if (position.x === targetPosition.x && position.y === targetPosition.y) {
return;
}
setPosition((pos) => {
const diff = {
x: targetPosition.x - pos.x,
y: targetPosition.y - pos.y,
};
if (Math.abs(diff.x) < 3 && Math.abs(diff.y) < 3) {
return targetPosition;
}
const speed = 0.1;
return {
x: position.x + diff.x * speed,
y: position.y + diff.y * speed,
};
});
}
});

if (isLoading || error || !data) {
return null;
}
const user = data.user;
if (!user) {
return null;
}

return (
<Explorer
position={position}
imgURL={traqIconURL(user.name)}
isMe={false}
name={user.name}
/>
);
};

export default OtherExplorer;
Loading