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

フィールド上のメッセージのUI #33

Merged
merged 3 commits into from
Jan 22, 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
2 changes: 2 additions & 0 deletions client/src/assets/icons/messageIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions client/src/pixi/World.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Rectangle from "./components/Rectangle";
import "@pixi/events";
import { DisplayPosition, Position } from "./Position";
import React from "react";
import Message from "./components/Message";

interface Props {
userPosition: Position;
Expand Down Expand Up @@ -55,6 +56,14 @@ const World: React.FC<Props> = ({ userPosition, userDisplayPosition }) => {
width={100}
height={100}
/>
<Message
messageText={"メッセージ".repeat(20)}
displayPosition={{ left: 100, top: 100 }}
user={{
name: "ikura-hamu",
iconUrl: "https://q.trap.jp/api/v3/public/icon/ikura-hamu",
}}
/>
</Container>
);
};
Expand Down
113 changes: 113 additions & 0 deletions client/src/pixi/components/Message.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { Container, Graphics, Sprite, Text } from "@pixi/react";
import messageIcon from "/src/assets/icons/messageIcon.svg";
import React, { useRef, useState, useEffect, useCallback } from "react";
import { Graphics as PIXIGraphics, TextStyle } from "pixi.js";
import PIXI from "pixi.js";
import { DisplayPosition } from "../Position";
import { themeColors } from "../theme";

const messageIconSize = 30;

interface MessageBubbleProps {
width: number;
height: number;
}

const MessageBubble: React.FC<MessageBubbleProps> = (props) => {
const draw = useCallback(
(g: PIXIGraphics) => {
g.clear();
g.lineStyle(2, 0x000000);
g.beginFill(themeColors.backgroundPrimary);
g.drawRoundedRect(0, 0, props.width, props.height, 10);
g.endFill();
},
[props],
);
return <Graphics draw={draw} />;
};

interface Props {
messageText: string;
displayPosition: DisplayPosition;
user: {
name: string;
iconUrl: string;
};
}

const userNameTextStyle = new TextStyle({ fontSize: 14, fill: "black" });
const messageTextStyle = new TextStyle({
fontSize: 16,
fill: "black",
wordWrap: true,
wordWrapWidth: 180,
breakWords: true,
});

const Message: React.FC<Props> = ({ messageText, displayPosition, user }) => {
const [showMessage, setShowMessage] = useState(false);
const textRef = useRef<PIXI.Text>(null);
const [bubbleSize, setBubbleSize] = useState({ width: 200, height: 100 });

const handleMouseOver = useCallback(() => setShowMessage(true), []);
const handleMouseOut = useCallback(() => setShowMessage(false), []);

useEffect(() => {
if (!showMessage) return;
if (textRef.current) {
const { width, height } = textRef.current;
setBubbleSize({
width: width + 20,
height: height + 20,
});
}
}, [showMessage]);
Comment on lines +56 to +65
Copy link
Contributor

Choose a reason for hiding this comment

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

余裕なかったらこのままでいいけど、setShowMessage をラップして、setShowMessage(true) するときにsetBubbleSize するのがよさそう

Copy link
Member Author

Choose a reason for hiding this comment

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

これshowMessage=falseのときtextRef.currentがnullなのでできなさそうです

Copy link
Contributor

Choose a reason for hiding this comment

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

たしかに


const iconImageSrc = showMessage ? user.iconUrl : messageIcon;

return (
<Container
x={displayPosition.left}
y={displayPosition.top}
interactive
mouseout={handleMouseOut}
mouseover={handleMouseOver}
>
<Sprite
image={iconImageSrc}
x={0}
y={0}
width={messageIconSize}
height={messageIconSize}
interactive={true}
/>
{showMessage && (
<>
<Text
text={user.name}
x={messageIconSize + 10}
y={messageIconSize - 10}
anchor={{ x: 0, y: 1 }}
style={userNameTextStyle}
/>
<Container x={0} y={messageIconSize}>
<MessageBubble
width={bubbleSize.width}
height={bubbleSize.height}
/>
<Text
ref={textRef}
text={messageText}
x={10}
y={10}
style={messageTextStyle}
/>
</Container>
</>
)}
</Container>
);
};

export default Message;
31 changes: 31 additions & 0 deletions client/src/pixi/theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// @ts-expect-error tailwind config
import tailwindConfig from "../../tailwind.config";

const { colors } = tailwindConfig.theme.extend;

type ThemeColors = Record<
| "backgroundPrimary"
| "backgroundSecondary"
| "backgroundTertiary"
| "textPrimary"
| "textSecondary"
| "textTertiary"
| "accentPrimary"
| "accentSecondary"
| "accentTertiary"
| "accentHover",
string
>;

export const themeColors: ThemeColors = {
backgroundPrimary: colors["background-primary"],
backgroundSecondary: colors["background-secondary"],
backgroundTertiary: colors["background-tertiary"],
textPrimary: colors["text-primary"],
textSecondary: colors["text-secondary"],
textTertiary: colors["text-tertiary"],
accentPrimary: colors["accent-primary"],
accentSecondary: colors["accent-secondary"],
accentTertiary: colors["accent-tertiary"],
accentHover: colors["accent-hover"],
};
Loading