Skip to content

Commit

Permalink
refactor: 당첨자 부분 표시
Browse files Browse the repository at this point in the history
  • Loading branch information
eunji0714 committed Aug 17, 2024
1 parent 518b56f commit 078529e
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 93 deletions.
2 changes: 1 addition & 1 deletion src/app/_components/user/workation/WkResultInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const WkResultInfo = ({ id }: { id: number }) => {
return (
<div>
<WkSlider id={id} />
<div className="flex flex-col">
<div className="mt-10 flex flex-col">
<UserSubtitleAtom subtitle="추첨 결과" />
<div className="flex w-full gap-3.5 overflow-x-auto scrollbar-hide">
{data?.wktWinningUserInfos.map(
Expand Down
231 changes: 139 additions & 92 deletions src/app/_components/user/workation/WkSlider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,21 @@ const Slider = ({ id }: { id: number }) => {
const [animate, setAnimate] = useState(false);
const [hoveredAccount, setHoveredAccount] = useState<string | null>(null);
const [tooltipPosition, setTooltipPosition] = useState({ left: '0%' });
const sliderRef = useRef(null);
const [visibleWinners, setVisibleWinners] = useState<number[]>([]);
const sliderRef = useRef<HTMLDivElement | null>(null);
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
const [visibleWinner, setVisibleWinner] = useState(null);
const { data: sessionData } = useSession();
const userId = String(sessionData?.accountId);

const restartAnimation = () => {
setAnimate(false);
setTimeout(() => {
setAnimate(true);
setVisibleWinner(null); // Ensure winner is reset on animation restart
}, 10);
};

useEffect(() => {
setAnimate(true);
}, []);

const { data, isLoading, isError } = useGetWkSimulationQuery({ wktId: id });
if (isLoading || isError || !data) return;

const totalRange = data?.raffleMemberIndexInfos.reduce((max, member) => {
return Math.max(max, member.raffleIndex);
}, 0);
const totalRange =
data?.raffleMemberIndexInfos?.reduce((max, member) => {
return Math.max(max, member.raffleIndex);
}, 0) ?? 0;

const getSliderWidth = (index: number) => {
if (!data) return 0;
if (index === 0) {
return (data.raffleMemberIndexInfos[0].raffleIndex / totalRange) * 100;
}
Expand All @@ -44,13 +33,86 @@ const Slider = ({ id }: { id: number }) => {

const calculateLeftPosition = (index: number) => {
let left = 0;
/* eslint-disable no-plusplus */
// eslint-disable-next-line no-plusplus
for (let i = 0; i < index; i++) {
left += getSliderWidth(i);
}
return left;
};

const sortedRafflePickedIndexInfos = [
...(data?.rafflePickedIndexInfos || []),
].sort((a, b) => a.pickedIndex - b.pickedIndex);

const pausePositions = sortedRafflePickedIndexInfos.map((winner) => {
return {
position: (winner.pickedIndex / totalRange) * 100,
};
});

const totalDuration = 30;
const holdTime = 2;
const runningTime = totalDuration - holdTime * pausePositions.length;

const generateKeyframes = () => {
const keyframes: { [key: string]: { left: string } } = {
'0%': { left: '0%' },
};

let totalPauseTime = 0;

pausePositions.forEach((positionObj) => {
const positionKey = `${(((positionObj.position * runningTime) / 100 + totalPauseTime) / totalDuration) * 100}%`;
keyframes[positionKey] = { left: `${positionObj.position}%` };

totalPauseTime += holdTime;
const holdKey = `${(((positionObj.position * runningTime) / 100 + totalPauseTime) / totalDuration) * 100}%`;
keyframes[holdKey] = { left: `${positionObj.position}%` };
});

keyframes['100%'] = { left: '100%' };
return keyframes;
};

const triggerWinnerVisibility = () => {
let totalPauseTime = 0;

pausePositions.forEach((_, index) => {
const delay =
(pausePositions[index].position * runningTime) / 100 +
totalPauseTime * 1000;

setTimeout(
() => {
setVisibleWinners((prev) => [...prev, index]);
},
delay + holdTime * 1000,
);

totalPauseTime += holdTime;
});
};

const restartAnimation = () => {
setAnimate(false);
setVisibleWinners([]);
setTimeout(() => {
setAnimate(true);
triggerWinnerVisibility();
}, 10);
};

useEffect(() => {
if (data && !isLoading && !isError) {
setAnimate(false);
setVisibleWinners([]);
setTimeout(() => {
setAnimate(true);
triggerWinnerVisibility();
}, 10);
}
}, [data, isLoading, isError]);

const handleMouseEnter = (accountId: string, index: number) => {
setHoveredAccount(accountId);
setTooltipPosition({
Expand All @@ -64,21 +126,20 @@ const Slider = ({ id }: { id: number }) => {
setHoveredIndex(null);
};

const handleMouseMove = (event) => {
const handleMouseMove = (event: React.MouseEvent) => {
if (!data || !sliderRef.current) return;

const rect = sliderRef.current.getBoundingClientRect();
const xPos = event.clientX - rect.left; // Calculate mouse position within the slider
const relativePosition = (xPos / rect.width) * 100; // Convert to relative percentage position
const xPos = event.clientX - rect.left;
const relativePosition = (xPos / rect.width) * 100;

// Find the closest account based on mouse position percentage
let closestAccount = null;
let closestAccount: string | null = null;
let minDiff = Infinity;

data.raffleMemberIndexInfos.forEach((member, index) => {
// Calculate the start and end percentage positions for this index
const startPercent = index === 0 ? 0 : calculateLeftPosition(index);
const endPercent = startPercent + getSliderWidth(index);

// Check if the current mouse position falls within this range
if (relativePosition >= startPercent && relativePosition < endPercent) {
const diff = Math.min(
Math.abs(startPercent - relativePosition),
Expand All @@ -97,45 +158,10 @@ const Slider = ({ id }: { id: number }) => {
}
};

// pausePositions 계산 로직 변경
const pausePositions = data.rafflePickedIndexInfos
.map((winner) => {
return {
position: (winner.pickedIndex / totalRange) * 100,
accountId: winner.accountId,
pickedIndex: winner.pickedIndex,
};
})
.sort((a, b) => a.pickedIndex - b.pickedIndex) // pickedIndex를 기준으로 오름차순 정렬
.map((winner) => winner.position); // 최종적으로 포지션만 추출

// 동적으로 키프레임 생성
const generateKeyframes = () => {
const keyframes = {
'0%': { left: '0%' },
};

let totalPauseTime = 0;
const totalDuration = 15; // 전체 애니메이션 시간 (초)
const holdTime = 2; // 각 위치에서 멈출 시간 (초 단위)
const runningTime = totalDuration - holdTime * pausePositions.length; // 멈추지 않는 동안의 총 시간

pausePositions.forEach((position, i) => {
const positionKey = `${(((position * runningTime) / 100 + totalPauseTime) / totalDuration) * 100}%`;
keyframes[positionKey] = { left: `${position}%` };

totalPauseTime += holdTime;
const holdKey = `${(((position * runningTime) / 100 + totalPauseTime) / totalDuration) * 100}%`;
keyframes[holdKey] = { left: `${position}%` };
});

keyframes['100%'] = { left: '100%' };
return keyframes;
};

// 동적으로 생성된 키프레임을 스타일 태그에 주입
const customKeyframes = generateKeyframes();

if (isLoading || isError || !data) return null;

return (
<div>
{data.rafflePickedIndexInfos[0] && (
Expand All @@ -152,7 +178,7 @@ const Slider = ({ id }: { id: number }) => {
/>
</div>
<div
className="relative h-5 w-full overflow-visible rounded-full bg-gray-300"
className="relative my-8 h-5 w-full overflow-visible rounded-full bg-gray-300"
ref={sliderRef}
onMouseMove={handleMouseMove}
>
Expand All @@ -172,51 +198,72 @@ const Slider = ({ id }: { id: number }) => {
`}
</style>
<div
className={`absolute top-0 h-full rounded-full bg-[#FFF5A5] ${animate ? 'animate-fillTrack' : ''}`}
className={`absolute top-0 h-full rounded-full bg-[#FFF5A5] ${
animate ? 'animate-fillTrack' : ''
}`}
/>

<div
className={`${animate ? 'linear animate-slideHandle' : ''} absolute z-50 h-6 w-6 rounded-full border-[2px] border-[#FFFBE0] bg-primary`}
className={`${
animate ? 'linear animate-slideHandle' : ''
} absolute z-50 h-6 w-6 rounded-full border-[2px] border-[#FFFBE0] bg-primary`}
style={{
top: '50%',
transform: 'translate(-50%, -50%)',
}}
/>
{data?.raffleMemberIndexInfos.map((member, index) => (
<div
key={member.accountId}
className="absolute top-0 h-full cursor-pointer rounded-full"
style={{
left: `${calculateLeftPosition(index)}%`,
width: `${getSliderWidth(index)}%`,
zIndex: 10,
backgroundColor:
member.accountId === userId ? '#FDE000' : 'transparent',
}}
onMouseEnter={() => handleMouseEnter(member.accountId, index)}
onMouseLeave={handleMouseLeave}
>
{member.accountId === userId && (
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 transform text-xs text-sub-200">
{userId}
</div>
)}
{hoveredIndex === index && (
<div className="absolute top-0 z-0 h-full w-full rounded-full bg-yellow-800/20" />
)}
</div>
))}
{data?.rafflePickedIndexInfos.map((winner, index) => (
{data?.raffleMemberIndexInfos.map((member, index) => {
const isWinner = visibleWinners.includes(
sortedRafflePickedIndexInfos.findIndex(
(winner) => winner.accountId === member.accountId,
),
);

return (
<div
key={member.accountId}
className="absolute top-0 h-full cursor-pointer rounded-full"
style={{
left: `${calculateLeftPosition(index)}%`,
width: `${getSliderWidth(index)}%`,
zIndex: 10,
backgroundColor: isWinner
? '#FBD501'
: member.accountId === userId
? '#FDE000'
: 'transparent',
}}
onMouseEnter={() =>
handleMouseEnter(member.accountId, index)
}
onMouseLeave={handleMouseLeave}
>
{member.accountId === userId && (
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 transform text-xs text-sub-200">
{userId}
</div>
)}
{hoveredIndex === index && (
<div className="absolute top-0 z-0 h-full w-full rounded-full bg-yellow-800/20" />
)}
</div>
);
})}
{sortedRafflePickedIndexInfos.map((winner, index) => (
<div
key={winner.accountId}
className="absolute z-50 rounded-regular bg-button px-3.5 pb-1"
style={{
left: `${(winner.pickedIndex / totalRange) * 100}%`,
top: '-40px',
transform: 'translateX(-50%)',
display: visibleWinners.includes(index) ? 'block' : 'none',
}}
>
<span className="text-xs text-white">당첨</span>
<div className="relative">
<div className="absolute left-1/2 top-full h-0 w-0 -translate-x-1/2 transform border-x-8 border-t-8 border-x-transparent border-t-button" />
</div>
</div>
))}
{hoveredAccount && (
Expand All @@ -226,7 +273,7 @@ const Slider = ({ id }: { id: number }) => {
left: tooltipPosition.left,
}}
>
ID {hoveredAccount}
{hoveredAccount}
</div>
)}
</div>
Expand Down

0 comments on commit 078529e

Please sign in to comment.