Web: 360-tour updated to work properly on IOS

This commit is contained in:
2026-02-09 12:08:52 +05:00
parent 92f88e6f00
commit 0e59e44a15
5 changed files with 83 additions and 26 deletions
@@ -74,7 +74,7 @@ export default function DisplacementCard({
rotate: rotate,
}}
className={
`w-[15.486vw] aspect-[223/240] rounded-[16px] bg-[radial-gradient(circle,rgba(0,0,0,1)_-70%,rgba(34,36,37,1)_100%);] flex flex-col items-center justify-between p-[1.111vw] lg:absolute max-lg:relative max-lg:w-full max-lg:h-full overflow-hidden md:max-lg:p-[2.083vw] max-md:p-[4.444vw] max-md:aspect-[165/240] ` +
`w-[15.486vw] aspect-[223/240] rounded-[16px] bg-[radial-gradient(circle,rgba(0,0,0,1)_-70%,rgba(34,36,37,1)_100%);] flex flex-col items-center justify-between p-[1.111vw] lg:absolute max-lg:relative max-lg:w-full max-lg:h-full overflow-hidden md:max-lg:p-[2.083vw] max-md:p-[4.444vw] max-md:aspect-[165/240] max-md:h-auto ` +
className
}
onClick={onClick}
@@ -25,6 +25,7 @@ function Iphone({ active }: { active: boolean }) {
muted
autoPlay
loop
playsInline
className={`mx-auto w-[98%] h-[99%] translate-y-[0.5%] z-[7] lg:rounded-[3vw] rounded-2xl md:max-lg:rounded-[4vw] max-md:rounded-[8vw]`}
/>
) : (
+60 -9
View File
@@ -31,6 +31,7 @@ export default function WebDemo() {
const [showPopup, setShowPopup] = useState(false);
const [iframeSrc, setIframeSrc] = useState<string>("");
const isMobile = useMediaQuery("(max-width: 768px)");
const [isPseudoFullscreen, setIsPseudoFullscreen] = useState(false);
useEffect(() => {
if (isMobile) {
@@ -53,17 +54,47 @@ export default function WebDemo() {
!isMobile && setShowPopup(false);
}, [isMobile]);
const enablePseudoFullScreen = () => {
setIsPseudoFullscreen(true);
onFullScreenEnter();
};
const disablePseudoFullScreen = () => {
setIsPseudoFullscreen(false);
onFullScreenExit();
};
const enableDemoFullscreenMode = useCallback(
(enableFullscreen: boolean) => {
async (enableFullscreen: boolean) => {
const cur = demoRef.current;
if (!cur) return;
if (enableFullscreen) {
cur.requestFullscreen();
onFullScreenEnter();
// Проверяем поддержку Fullscreen API
if (cur.requestFullscreen) {
try {
await cur.requestFullscreen();
setIsPseudoFullscreen(false);
onFullScreenEnter();
} catch (error) {
console.log(
"Fullscreen API not supported, using pseudo-fullscreen instead"
);
enablePseudoFullScreen();
}
} else {
enablePseudoFullScreen();
}
} else if (document.fullscreenElement) {
document.exitFullscreen();
onFullScreenExit();
try {
await document.exitFullscreen();
disablePseudoFullScreen();
} catch (error) {
console.log("Error exiting fullscreen:", error);
disablePseudoFullScreen();
}
} else {
disablePseudoFullScreen();
}
},
[onFullScreenEnter, onFullScreenExit]
@@ -114,7 +145,7 @@ export default function WebDemo() {
index={1}
className="lg:top-[8.333vw] lg:left-0 md:max-lg:!translate-y-[3.125vw]"
>
<div className="flex lg:w-[11.361vw] lg:h-[11.361vw] max-lg:my-auto md:w-[23.26vw] md:h-[23.26vw] w-[33.26vw] h-[33.26vw] overflow-hidden rounded-full aspect-square">
<div className="flex lg:w-[11.361vw] lg:h-[11.361vw] max-lg:my-auto md:w-[23.26vw] md:h-[23.26vw] w-[33.26vw] h-[33.26vw] overflow-hidden z-[1] rounded-full aspect-square">
<video
src="/videos/pages/web/demo/cars.mp4"
muted
@@ -208,7 +239,7 @@ export default function WebDemo() {
index={5}
className="lg:bottom-0 lg:right-[16.389vw] "
>
<div className="flex lg:w-[11.361vw] lg:h-[11.361vw] max-lg:my-auto md:w-[23.26vw] md:h-[23.26vw] w-[33.26vw] h-[33.26vw] overflow-hidden rounded-full aspect-square">
<div className="flex lg:w-[11.361vw] lg:h-[11.361vw] max-lg:my-auto md:w-[23.26vw] md:h-[23.26vw] w-[33.26vw] h-[33.26vw] overflow-hidden z-[1] rounded-full aspect-square">
<video
src="/videos/pages/web/demo/appartaments.mp4"
loop
@@ -235,11 +266,30 @@ export default function WebDemo() {
animate={{
width: demoActive ? "100vw" : isMobile ? "100%" : "80vw",
}}
style={
isPseudoFullscreen
? {
position: "fixed",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
zIndex: 9999,
margin: 0,
borderRadius: 0,
backgroundColor: "#000",
aspectRatio: "unset",
}
: undefined
}
className="relative max-w-full aspect-[1400/784] mx-auto rounded-[1.111vw] overflow-clip
md:max-lg:h-screen md:max-lg:rounded-[2.083vw]
max-md:w-[94.444vw] max-md:aspect-[340/600] max-md:rounded-[4.444vw]"
>
<div className="relative w-full h-full overflow-clip rounded-[1.111vw] md:max-lg:rounded-[2.083vw] max-md:rounded-[4.444vw]">
<div
style={isPseudoFullscreen ? { borderRadius: 0 } : undefined}
className="relative w-full h-full overflow-clip rounded-[1.111vw] md:max-lg:rounded-[2.083vw] max-md:rounded-[4.444vw]"
>
{!demoActive ? (
<div className="absolute top-0 left-0 z-[8] w-full h-full bg-[#00000066] flex flex-col items-center justify-center max-md:p-[4.444vw]">
<span className="line2 font-medium text-center whitespace-pre-line">
@@ -275,7 +325,8 @@ export default function WebDemo() {
<iframe
ref={iframeRef}
src={iframeSrc}
className=" relative w-full h-full rounded-[1.111vw] text-center"
className=" relative w-full h-full rounded-[1.111vw] text-center mix-blend-screen"
style={isPseudoFullscreen ? { borderRadius: 0 } : undefined}
onLoad={() => console.log("Iframe loaded successfully")}
onError={(e) => console.error("Iframe error:", e)}
title="Virtual Tour"
@@ -28,8 +28,8 @@ export default function WebDevelopmentTimeline() {
return (
<div
className="self-center w-max mx-auto btn-m text-black bg-[#B5F54E] rounded-[1.111vw] px-[1.389vw] py-[0.764vw] text-[0.972vw] z-10
md:max-lg:text-[1.563vw] md:max-lg:px-[1.563vw] md:max-lg:py-[0.911vw] md:max-lg:rounded-[2.083vw]
max-md:text-[3.333vw] max-md:px-[3.333vw] max-md:py-[1.944vw] max-md:rounded-[4.444vw]"
md:max-lg:text-[1.563vw] md:max-lg:px-[1.563vw] md:max-lg:py-[0.911vw] md:max-lg:rounded-[2.083vw]
max-md:text-[3.333vw] max-md:px-[3.333vw] max-md:py-[1.944vw] max-md:rounded-[4.444vw]"
>
{title}
</div>
@@ -90,7 +90,7 @@ export default function WebDevelopmentTimeline() {
<div
className="w-full min-h-[35vw] flex flex-col justify-between border-[#37393B] border-l px-[0.833vw] last:border-x gap-[4.167vw] relative
md:max-lg:flex-shrink-0 md:max-lg:w-[39.063vw] md:max-lg:border md:max-lg:p-[2.083vw] md:max-lg:rounded-[2.083vw] md:max-lg:flex-col-reverse md:max-lg:justify-end md:max-lg:gap-[2.083vw] md:max-lg:min-h-[66.469vw]
max-md:flex-shrink-0 max-md:w-[83.333vw] max-md:border max-md:p-[4.444vw] max-md:rounded-[4.444vw] max-md:flex-col-reverse max-md:justify-end max-md:gap-[4.444vw] max-md:overflow-clip max-md:min-h-[125vw] max-lg:snap-center"
max-md:flex-shrink-0 max-md:w-[83.333vw] max-md:border max-md:p-[4.444vw] max-md:rounded-[4.444vw] max-md:flex-col-reverse max-md:justify-end max-md:gap-[4.444vw] max-md:min-h-[125vw] max-lg:snap-center max-md:overflow-hidden"
>
<AccordeonGroup
isNewClient={isNewClient}
@@ -122,7 +122,7 @@ export default function WebDevelopmentTimeline() {
<div
className="w-full min-h-[35vw] flex flex-col justify-between border-[#37393B] border-l px-[0.833vw] last:border-x gap-[4.167vw] relative
md:max-lg:flex-shrink-0 md:max-lg:w-[39.063vw] md:max-lg:border md:max-lg:p-[2.083vw] md:max-lg:rounded-[2.083vw] md:max-lg:flex-col-reverse md:max-lg:justify-end md:max-lg:gap-[2.083vw]
max-md:flex-shrink-0 max-md:w-[83.333vw] max-md:border max-md:p-[4.444vw] max-md:rounded-[4.444vw] max-md:flex-col-reverse max-md:justify-end max-md:gap-[4.444vw] max-md:min-h-[125vw] max-lg:snap-center"
max-md:flex-shrink-0 max-md:w-[83.333vw] max-md:border max-md:p-[4.444vw] max-md:rounded-[4.444vw] max-md:flex-col-reverse max-md:justify-end max-md:gap-[4.444vw] max-md:min-h-[125vw] max-lg:snap-center max-md:overflow-hidden"
>
<AccordeonGroup
isNewClient={isNewClient}
@@ -150,7 +150,7 @@ export default function WebDevelopmentTimeline() {
<div
className="w-full min-h-[35vw] flex flex-col justify-between border-[#37393B] border-l px-[0.833vw] last:border-x gap-[4.167vw] relative
md:max-lg:flex-shrink-0 md:max-lg:w-[39.063vw] md:max-lg:border md:max-lg:p-[2.083vw] md:max-lg:rounded-[2.083vw] md:max-lg:flex-col-reverse md:max-lg:justify-end md:max-lg:gap-[2.083vw]
max-md:flex-shrink-0 max-md:w-[83.333vw] max-md:border max-md:p-[4.444vw] max-md:rounded-[4.444vw] max-md:flex-col-reverse max-md:justify-end max-md:gap-[4.444vw] max-md:min-h-[125vw] max-lg:snap-center"
max-md:flex-shrink-0 max-md:w-[83.333vw] max-md:border max-md:p-[4.444vw] max-md:rounded-[4.444vw] max-md:flex-col-reverse max-md:justify-end max-md:gap-[4.444vw] max-md:min-h-[125vw] max-lg:snap-center max-md:overflow-hidden"
>
<AccordeonGroup
isNewClient={isNewClient}
@@ -178,7 +178,7 @@ export default function WebDevelopmentTimeline() {
<div
className="w-full min-h-[35vw] flex flex-col justify-between border-[#37393B] px-[0.833vw] border-x gap-[4.167vw] relative
md:max-lg:flex-shrink-0 md:max-lg:w-[39.063vw] md:max-lg:border md:max-lg:p-[2.083vw] md:max-lg:rounded-[2.083vw] md:max-lg:flex-col-reverse md:max-lg:justify-end md:max-lg:gap-[2.083vw]
max-md:flex-shrink-0 max-md:w-[83.333vw] max-md:border max-md:p-[4.444vw] max-md:rounded-[4.444vw] max-md:flex-col-reverse max-md:justify-end max-md:gap-[4.444vw] max-md:min-h-[125vw] max-lg:snap-center"
max-md:flex-shrink-0 max-md:w-[83.333vw] max-md:border max-md:p-[4.444vw] max-md:rounded-[4.444vw] max-md:flex-col-reverse max-md:justify-end max-md:gap-[4.444vw] max-md:min-h-[125vw] max-lg:snap-center max-md:overflow-hidden"
>
<AccordeonGroup
isNewClient={isNewClient}
+15 -10
View File
@@ -11,7 +11,7 @@ export default function WebExperience() {
});
return (
<div>
<div className="max-md:overflow-x-hidden">
<h2
className="font-medium text-[4.444vw] leading-[95%] tracking-[-0.02em] text-start mb-[4.444vw] bg-gradient-to-r from-[#7A55FF] via-[#C932E8] to-[#FF79D2] bg-clip-text
md:max-lg:text-[5.208vw] md:max-lg:mb-[8.333vw]
@@ -26,7 +26,7 @@ export default function WebExperience() {
ref={gridRef}
className="grid grid-cols-[39.861vw_23.819vw_31.736vw] grid-rows-[35vw_24.167vw] gap-[0.833vw]
md:max-lg:grid-cols-[46.875vw_46.875vw] md:max-lg:grid-rows-[46.875vw_46.875vw_46.875vw] md:max-lg:gap-[1.823vw]
max-md:grid-cols-1 max-md:grid-rows-[77.778vw_100vw_100vw_77.778vw_72.222vw_100vw] max-md:gap-[3.889vw]"
max-md:grid-cols-1 max-md:grid-rows-[77.778vw_100vw_100vw_77.778vw_72.222vw_100vw] max-md:gap-[3.889vw] max-md:overflow-hidden"
>
<motion.div
animate={{
@@ -48,13 +48,14 @@ export default function WebExperience() {
/>
<div className="absolute inset-0 bg-gradient-to-b from-[#00000099]" />
</motion.div>
<motion.div
animate={{
opacity: +inView,
y: inView ? 0 : 100,
}}
transition={{ delay: 0.2, bounce: "none" }}
className="rounded-[1.111vw] overflow-hidden p-[2.222vw] flex flex-col justify-between backdrop-blur-[20px] bg-gradient-to-r from-[#FFFFFF14] to-[#FFFFFF05]
className="rounded-[1.111vw] overflow-hidden p-[2.222vw] flex flex-col justify-between backdrop-blur-[20px] bg-gradient-to-r from-[#FFFFFF14] to-[#FFFFFF05] relative
md:max-lg:p-[4.167vw] md:max-lg:text-[3.125vw]
max-md:rounded-[4.444vw] max-md:p-[8.889vw]"
>
@@ -62,9 +63,10 @@ export default function WebExperience() {
<img
src="/img/pages/web/experience/crm.png"
alt=""
className="flex-1 object-contain absolute lg:bottom-[2.222vw] md:max-lg:bottom-[3.125vw] bottom-[8.889vw] left-1/2 -translate-x-1/2"
className="flex-1 object-contain absolute lg:bottom-[2.222vw] md:max-lg:bottom-[3.125vw] bottom-[5.889vw] left-1/2 -translate-x-1/2"
/>
</motion.div>
<motion.div
animate={{
opacity: +inView,
@@ -72,7 +74,7 @@ export default function WebExperience() {
}}
transition={{ delay: 0.4, bounce: "none" }}
className="rounded-[1.111vw] overflow-hidden p-[2.222vw] flex flex-col justify-between backdrop-blur-[20px] bg-gradient-to-r from-[#FFFFFF14] to-[#FFFFFF05]
md:max-lg:p-[4.167vw] md:max-lg:text-[3.125vw]
md:max-lg:p-[4.167vw] md:max-lg:text-[3.125vw]
max-md:rounded-[4.444vw] max-md:p-[8.889vw]"
>
<span className="headline1">
@@ -81,28 +83,30 @@ export default function WebExperience() {
<img
src="/img/pages/web/experience/import.png"
alt=""
className="translate-y-[2.222vw] max-lg:scale-[120%] md:max-lg:translate-x-[2.222vw] md:max-lg:translate-y-[4.222vw] max-md:translate-x-[8.222vw] max-md:translate-y-[12.222vw]"
className="translate-y-[2.222vw] max-lg:scale-[1.2] md:max-lg:translate-x-[2.222vw] md:max-lg:translate-y-[4.222vw] max-md:translate-x-[0vw] max-md:translate-y-[12.222vw]"
/>
</motion.div>
<motion.div
animate={{
opacity: +inView,
y: inView ? 0 : 100,
}}
transition={{ delay: 0.4, bounce: "none" }}
className="rounded-[1.111vw] overflow-hidden p-[2.222vw] flex flex-col justify-between backdrop-blur-[20px] bg-gradient-to-r from-[#FFFFFF14] to-[#FFFFFF05]
className="rounded-[1.111vw] overflow-hidden p-[2.222vw] flex flex-col justify-between backdrop-blur-[20px] bg-gradient-to-r from-[#FFFFFF14] to-[#FFFFFF05] relative
md:max-lg:p-[4.167vw] md:max-lg:text-[3.125vw]
max-md:rounded-[4.444vw] max-md:p-[8.889vw]"
>
<span className="headline1">Ипотечный калькулятор</span>
<span className="headline1 relative z-10">Ипотечный калькулятор</span>
<img
src="/img/pages/web/experience/calculator.png"
alt=""
className="absolute w-[37.639vw] right-0 bottom-[2.222vw]
md:max-lg:left-[15.167vw] md:max-lg:right-0 md:max-lg:bottom-[8.771vw] md:max-lg:w-auto md:max-lg:scale-[150%]
max-md:left-[32vw] max-md:right-0 max-md:bottom-[5vw] max-md:w-auto max-md:scale-[150%]"
md:max-lg:left-[15.167vw] md:max-lg:bottom-[8.771vw] md:max-lg:w-auto md:max-lg:scale-[1.5]
max-md:left-[32vw] max-md:bottom-[5vw] max-md:w-auto max-md:scale-[1.5]"
/>
</motion.div>
<motion.div
animate={{
opacity: +inView,
@@ -121,6 +125,7 @@ export default function WebExperience() {
/>
<div className="absolute inset-0 bg-gradient-to-b from-[#00000099]" />
</motion.div>
<motion.div
animate={{
opacity: +inView,