🆙 Fix Zoom & Update animations

This commit is contained in:
duckietm 2025-03-14 11:26:24 +01:00
parent 7901ca5718
commit 96806c33ba
8 changed files with 94 additions and 54 deletions

View File

@ -1,6 +1,6 @@
import { AnimatePresence, motion } from 'framer-motion';
import { FC, useEffect, useMemo, useState } from 'react'; import { FC, useEffect, useMemo, useState } from 'react';
import { Flex, FlexProps } from '../Flex'; import { Flex, FlexProps } from '../Flex';
import { TransitionAnimation, TransitionAnimationTypes } from '../transitions';
export interface LayoutNotificationBubbleViewProps extends FlexProps export interface LayoutNotificationBubbleViewProps extends FlexProps
{ {
@ -45,8 +45,14 @@ export const LayoutNotificationBubbleView: FC<LayoutNotificationBubbleViewProps>
}, [ fadesOut, timeoutMs, onClose ]); }, [ fadesOut, timeoutMs, onClose ]);
return ( return (
<TransitionAnimation inProp={ isVisible } timeout={ 300 } type={ TransitionAnimationTypes.FADE_IN }> <AnimatePresence>
<Flex classNames={ getClassNames } overflow={ overflow } onClick={ onClose } { ...rest } /> { isVisible &&
</TransitionAnimation> <motion.div
initial={ { opacity: 0 }}
animate={ { opacity: 1 }}
exit={ { opacity: 0 }}>
<Flex overflow={ overflow } classNames={ getClassNames } onClick={ onClose } { ...rest } />
</motion.div> }
</AnimatePresence>
); );
}; };

View File

@ -1,6 +1,6 @@
import { AddLinkEventTracker, GetCommunication, HabboWebTools, ILinkEventTracker, RemoveLinkEventTracker, RoomSessionEvent } from '@nitrots/nitro-renderer'; import { AddLinkEventTracker, GetCommunication, HabboWebTools, ILinkEventTracker, RemoveLinkEventTracker, RoomSessionEvent } from '@nitrots/nitro-renderer';
import { AnimatePresence, motion } from 'framer-motion';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { TransitionAnimation, TransitionAnimationTypes } from '../common';
import { useNitroEvent } from '../hooks'; import { useNitroEvent } from '../hooks';
import { AchievementsView } from './achievements/AchievementsView'; import { AchievementsView } from './achievements/AchievementsView';
import { AvatarEditorView } from './avatar-editor'; import { AvatarEditorView } from './avatar-editor';
@ -80,9 +80,15 @@ export const MainView: FC<{}> = props =>
return ( return (
<> <>
<TransitionAnimation inProp={ landingViewVisible } timeout={ 300 } type={ TransitionAnimationTypes.FADE_IN }> <AnimatePresence>
{ landingViewVisible &&
<motion.div
initial={ { opacity: 0 }}
animate={ { opacity: 1 }}
exit={ { opacity: 0 }}>
<HotelView /> <HotelView />
</TransitionAnimation> </motion.div> }
</AnimatePresence>
<ToolbarView isInRoom={ !landingViewVisible } /> <ToolbarView isInRoom={ !landingViewVisible } />
<ModToolsView /> <ModToolsView />
<RoomView /> <RoomView />

View File

@ -1,4 +1,5 @@
import { GetRenderer } from '@nitrots/nitro-renderer'; import { GetRenderer, RoomSession } from '@nitrots/nitro-renderer';
import { AnimatePresence, motion } from 'framer-motion';
import { FC, useEffect, useRef } from 'react'; import { FC, useEffect, useRef } from 'react';
import { DispatchMouseEvent, DispatchTouchEvent } from '../../api'; import { DispatchMouseEvent, DispatchTouchEvent } from '../../api';
import { useRoom } from '../../hooks'; import { useRoom } from '../../hooks';
@ -13,6 +14,8 @@ export const RoomView: FC<{}> = (props) =>
useEffect(() => useEffect(() =>
{ {
if(!roomSession) return;
const canvas = GetRenderer().canvas; const canvas = GetRenderer().canvas;
if(!canvas) return; if(!canvas) return;
@ -34,19 +37,23 @@ export const RoomView: FC<{}> = (props) =>
canvas.classList.add('bg-black'); canvas.classList.add('bg-black');
element.appendChild(canvas); element.appendChild(canvas);
}, []); }, [roomSession]);
return ( return (
<div <AnimatePresence>
ref={elementRef} {
className={classNames('size-full', !roomSession && 'hidden')} <motion.div
> initial={ { opacity: 0 }}
{roomSession && ( animate={ { opacity: 1 }}
exit={ { opacity: 0 }}>
<div ref={ elementRef } className="w-100 h-100">
{ roomSession instanceof RoomSession &&
<> <>
<RoomWidgetsView /> <RoomWidgetsView />
{roomSession.isSpectator && <RoomSpectatorView />} { roomSession.isSpectator && <RoomSpectatorView /> }
</> </> }
)}
</div> </div>
</motion.div> }
</AnimatePresence>
); );
}; };

View File

@ -14,7 +14,7 @@ export const FurnitureCraftingView: FC<{}> = props =>
{ {
const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, objectId, RoomObjectCategory.FLOOR); const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, objectId, RoomObjectCategory.FLOOR);
return IsOwnerOfFurniture(roomObject); return IsOwnerOfFurniture(roomObject);
}, [ objectId, roomSession.roomId ]); }, [ objectId, roomSession ]);
const canCraft = useMemo(() => const canCraft = useMemo(() =>
{ {

View File

@ -1,9 +1,11 @@
import { CreateLinkEvent, GetGuestRoomResultEvent, GetRoomEngine, NavigatorSearchComposer, RateFlatMessageComposer } from '@nitrots/nitro-renderer'; import { CreateLinkEvent, GetGuestRoomResultEvent, GetRoomEngine, NavigatorSearchComposer, RateFlatMessageComposer } from '@nitrots/nitro-renderer';
import { AnimatePresence, motion } from 'framer-motion';
import { classNames } from '../../../../layout';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../../../api'; import { GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../../../api';
import { Text, TransitionAnimation, TransitionAnimationTypes } from '../../../../common'; import { Text } from '../../../../common';
import { useMessageEvent, useNavigator, useRoom } from '../../../../hooks'; import { useMessageEvent, useNavigator, useRoom } from '../../../../hooks';
import { classNames } from '../../../../layout';
export const RoomToolsWidgetView: FC<{}> = props => export const RoomToolsWidgetView: FC<{}> = props =>
{ {
@ -33,10 +35,8 @@ export const RoomToolsWidgetView: FC<{}> = props =>
else else
{ {
const geometry = GetRoomEngine().getRoomInstanceGeometry(roomSession.roomId, 1); const geometry = GetRoomEngine().getRoomInstanceGeometry(roomSession.roomId, 1);
if(geometry) geometry.performZoom(); if(geometry) geometry.performZoom();
} }
return !prevValue; return !prevValue;
}); });
return; return;
@ -59,7 +59,6 @@ export const RoomToolsWidgetView: FC<{}> = props =>
useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, event => useMessageEvent<GetGuestRoomResultEvent>(GetGuestRoomResultEvent, event =>
{ {
const parser = event.getParser(); const parser = event.getParser();
if(!parser.roomEnter || (parser.data.roomId !== roomSession.roomId)) return; if(!parser.roomEnter || (parser.data.roomId !== roomSession.roomId)) return;
if(roomName !== parser.data.roomName) setRoomName(parser.data.roomName); if(roomName !== parser.data.roomName) setRoomName(parser.data.roomName);
@ -70,9 +69,7 @@ export const RoomToolsWidgetView: FC<{}> = props =>
useEffect(() => useEffect(() =>
{ {
setIsOpen(true); setIsOpen(true);
const timeout = setTimeout(() => setIsOpen(false), 5000); const timeout = setTimeout(() => setIsOpen(false), 5000);
return () => clearTimeout(timeout); return () => clearTimeout(timeout);
}, [ roomName, roomOwner, roomTags ]); }, [ roomName, roomOwner, roomTags ]);
@ -86,7 +83,14 @@ export const RoomToolsWidgetView: FC<{}> = props =>
<div className="cursor-pointer nitro-icon icon-like-room" title={ LocalizeText('room.like.button.text') } onClick={ () => handleToolClick('like_room') } /> } <div className="cursor-pointer nitro-icon icon-like-room" title={ LocalizeText('room.like.button.text') } onClick={ () => handleToolClick('like_room') } /> }
</div> </div>
<div className="flex flex-col justify-center"> <div className="flex flex-col justify-center">
<TransitionAnimation inProp={ isOpen } timeout={ 300 } type={ TransitionAnimationTypes.SLIDE_LEFT }> <AnimatePresence>
{ isOpen &&
<motion.div
initial={{ x: -100 }}
animate={{ x: 0 }}
exit={{ x: -100 }}
transition={{ duration: 0.3 }}
>
<div className="flex flex-col items-center justify-center"> <div className="flex flex-col items-center justify-center">
<div className="flex flex-col px-3 py-2 rounded nitro-room-tools-info"> <div className="flex flex-col px-3 py-2 rounded nitro-room-tools-info">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
@ -95,11 +99,23 @@ export const RoomToolsWidgetView: FC<{}> = props =>
</div> </div>
{ roomTags && roomTags.length > 0 && { roomTags && roomTags.length > 0 &&
<div className="flex gap-2"> <div className="flex gap-2">
{ roomTags.map((tag, index) => <Text key={ index } pointer small className="p-1 rounded bg-primary" variant="white" onClick={ () => handleToolClick('navigator_search_tag', tag) }>#{ tag }</Text>) } { roomTags.map((tag, index) => (
<Text
key={ index }
pointer
small
className="p-1 rounded bg-primary"
variant="white"
onClick={ () => handleToolClick('navigator_search_tag', tag) }
>
#{ tag }
</Text>
)) }
</div> } </div> }
</div> </div>
</div> </div>
</TransitionAnimation> </motion.div> }
</AnimatePresence>
</div> </div>
</div> </div>
); );

View File

@ -4,14 +4,11 @@ import { DispatchUiEvent, GetConfigurationValue, GetRoomSession, GetUserProfile
import { Flex, LayoutItemCountView } from '../../common'; import { Flex, LayoutItemCountView } from '../../common';
import { GuideToolEvent } from '../../events'; import { GuideToolEvent } from '../../events';
interface ToolbarMeViewProps export const ToolbarMeView: FC<PropsWithChildren<{
{
useGuideTool: boolean; useGuideTool: boolean;
unseenAchievementCount: number; unseenAchievementCount: number;
setMeExpanded: Dispatch<SetStateAction<boolean>>; setMeExpanded: Dispatch<SetStateAction<boolean>>;
} }>> = props =>
export const ToolbarMeView: FC<PropsWithChildren<ToolbarMeViewProps>> = props =>
{ {
const { useGuideTool = false, unseenAchievementCount = 0, setMeExpanded = null, children = null, ...rest } = props; const { useGuideTool = false, unseenAchievementCount = 0, setMeExpanded = null, children = null, ...rest } = props;
const elementRef = useRef<HTMLDivElement>(); const elementRef = useRef<HTMLDivElement>();

View File

@ -1,7 +1,8 @@
import { CreateLinkEvent, Dispose, DropBounce, EaseOut, GetSessionDataManager, JumpBy, Motions, NitroToolbarAnimateIconEvent, PerkAllowancesMessageEvent, PerkEnum, Queue, Wait } from '@nitrots/nitro-renderer'; import { CreateLinkEvent, Dispose, DropBounce, EaseOut, GetSessionDataManager, JumpBy, Motions, NitroToolbarAnimateIconEvent, PerkAllowancesMessageEvent, PerkEnum, Queue, Wait } from '@nitrots/nitro-renderer';
import { AnimatePresence, motion } from 'framer-motion';
import { FC, useState } from 'react'; import { FC, useState } from 'react';
import { GetConfigurationValue, MessengerIconState, OpenMessengerChat, VisitDesktop } from '../../api'; import { GetConfigurationValue, MessengerIconState, OpenMessengerChat, VisitDesktop } from '../../api';
import { Flex, LayoutAvatarImageView, LayoutItemCountView, TransitionAnimation, TransitionAnimationTypes } from '../../common'; import { Flex, LayoutAvatarImageView, LayoutItemCountView } from '../../common';
import { useAchievements, useFriends, useInventoryUnseenTracker, useMessageEvent, useMessenger, useNitroEvent, useSessionInfo } from '../../hooks'; import { useAchievements, useFriends, useInventoryUnseenTracker, useMessageEvent, useMessenger, useNitroEvent, useSessionInfo } from '../../hooks';
import { ToolbarItemView } from './ToolbarItemView'; import { ToolbarItemView } from './ToolbarItemView';
import { ToolbarMeView } from './ToolbarMeView'; import { ToolbarMeView } from './ToolbarMeView';
@ -64,13 +65,18 @@ export const ToolbarView: FC<{ isInRoom: boolean }> = props =>
return ( return (
<> <>
<TransitionAnimation inProp={ isMeExpanded } timeout={ 300 } type={ TransitionAnimationTypes.FADE_IN }> <AnimatePresence> { isMeExpanded && ( <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} transition={{ duration: 0.3 }}>
<ToolbarMeView setMeExpanded={ setMeExpanded } unseenAchievementCount={ getTotalUnseen } useGuideTool={ useGuideTool } /> <ToolbarMeView setMeExpanded={ setMeExpanded } unseenAchievementCount={ getTotalUnseen } useGuideTool={ useGuideTool } />
</TransitionAnimation> </motion.div> )}
</AnimatePresence>
<Flex alignItems="center" className="absolute bottom-[0] left-[0] w-full h-[55px] bg-[rgba(28,_28,_32,_.95)] [box-shadow:inset_0_5px_#22222799,_inset_0_-4px_#12121599] py-1 px-3" gap={ 2 } justifyContent="between"> <Flex alignItems="center" className="absolute bottom-[0] left-[0] w-full h-[55px] bg-[rgba(28,_28,_32,_.95)] [box-shadow:inset_0_5px_#22222799,_inset_0_-4px_#12121599] py-1 px-3" gap={ 2 } justifyContent="between">
<Flex alignItems="center" gap={ 2 }> <Flex alignItems="center" gap={ 2 }>
<Flex alignItems="center" gap={ 2 }> <Flex alignItems="center" gap={ 2 }>
<Flex center pointer className={ 'relative w-[50px] h-[45px] overflow-hidden ' + (isMeExpanded ? 'active ' : '') } onClick={ event => setMeExpanded(!isMeExpanded) }> <Flex center pointer className={ 'relative w-[50px] h-[45px] overflow-hidden ' + (isMeExpanded ? 'active ' : '') } onClick={ event =>
{
setMeExpanded(!isMeExpanded);
event.stopPropagation();
} }>
<LayoutAvatarImageView className="-ml-[5px] mt-[25px]" direction={ 2 } figure={ userFigure } position="absolute" /> <LayoutAvatarImageView className="-ml-[5px] mt-[25px]" direction={ 2 } figure={ userFigure } position="absolute" />
{ (getTotalUnseen > 0) && { (getTotalUnseen > 0) &&
<LayoutItemCountView count={ getTotalUnseen } /> } <LayoutItemCountView count={ getTotalUnseen } /> }

View File

@ -346,6 +346,8 @@ const useAvatarInfoWidgetState = () =>
useEffect(() => useEffect(() =>
{ {
if(!roomSession) return;
roomSession.isDecorating = isDecorating; roomSession.isDecorating = isDecorating;
}, [ roomSession, isDecorating ]); }, [ roomSession, isDecorating ]);