Skip to content

Commit

Permalink
Merge pull request #3960 from CAFECA-IO/feature/landing_page_animation
Browse files Browse the repository at this point in the history
Feature/landing page animation
  • Loading branch information
Luphia authored Jan 8, 2025
2 parents e4c5e46 + 9be53a7 commit 7cbd73d
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 109 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "iSunFA",
"version": "0.8.5+274",
"version": "0.8.5+275",
"private": false,
"scripts": {
"dev": "next dev",
Expand Down
2 changes: 1 addition & 1 deletion src/components/date_picker/date_picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ const PopulateDates = ({
key={el?.date || `${Date.now()}-${index}`}
type="button"
disabled={el?.disable ?? true} // Info: (20241108 - Julian) 禁用範圍外和空白日期
className={`relative z-10 flex h-35px items-center justify-center whitespace-nowrap px-1 text-base transition-all duration-150 ease-in-out disabled:text-date-picker-text-disable md:h-35px ${isSelectedDateStyle} ${isSelectedPeriodStyle} ${!el?.disable ? 'hover:bg-date-picker-surface-date-period' : ''} hover:rounded-full`}
className={`relative z-10 flex h-42px items-center justify-center whitespace-nowrap px-1 text-base transition-all duration-150 ease-in-out disabled:text-date-picker-text-disable ${isSelectedDateStyle} ${isSelectedPeriodStyle} ${!el?.disable ? 'hover:bg-date-picker-surface-date-period' : ''} hover:rounded-full`}
onClick={dateClickHandler}
>
{el?.date ?? ' '}
Expand Down
43 changes: 37 additions & 6 deletions src/components/landing_page_v2/cta.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef, useState, useEffect } from 'react';
import Image from 'next/image';
import Link from 'next/link';
import { useTranslation } from 'next-i18next';
Expand Down Expand Up @@ -70,6 +70,18 @@ const CTAIntroCard: React.FC<{
const CTA: React.FC = () => {
const { t } = useTranslation('common');

const ctaRef = useRef<HTMLDivElement>(null);
const [isCtaRefVisible, setIsCtaRefVisible] = useState(false);

useEffect(() => {
const waitForCTA = setTimeout(() => {
setIsCtaRefVisible(true);
}, 500);
return () => {
clearTimeout(waitForCTA);
};
}, []);

// Info: (20241218 - Julian) 卡片內容
const introCards = [
{
Expand Down Expand Up @@ -103,22 +115,37 @@ const CTA: React.FC = () => {
));

return (
<div className="flex flex-col items-center py-14px md:px-50px md:py-20px lg:py-70px">
<div
ref={ctaRef}
className="flex flex-col items-center py-14px md:px-50px md:py-20px lg:py-70px"
>
{/* Info: (20241211 - Julian) CTA Main */}
<div className="flex flex-col items-center">
{/* Info: (20241205 - Julian) CTA Main */}
<div className="flex flex-col items-center gap-12px lg:gap-24px">
<LinearGradientText size={LinearTextSize.XL} align={TextAlign.CENTER}>
<LinearGradientText
size={LinearTextSize.XL}
align={TextAlign.CENTER}
className={`${
isCtaRefVisible ? 'translate-y-0 opacity-100' : '-translate-y-200px opacity-0'
} transition-all duration-500`}
>
{t('landing_page_v2:CTA.MAIN_TITLE')}
</LinearGradientText>
<p className="text-center text-xs font-medium md:text-lg lg:text-xl">
<p
className={` ${isCtaRefVisible ? 'translate-y-0 opacity-100' : 'translate-y-full opacity-0'} text-center text-xs font-medium transition-all duration-500 md:text-lg lg:text-xl`}
>
{t('landing_page_v2:CTA.MAIN_DESCRIPTION')}
</p>
</div>

<div className="mt-20px md:mt-30px lg:mt-60px">
<Link href={ISUNFA_ROUTE.DASHBOARD}>
<LandingButton type="button" variant="primary">
<LandingButton
type="button"
variant="primary"
className={` ${isCtaRefVisible ? 'opacity-100' : 'opacity-0'} transition-all duration-500`}
>
<BsFillRocketTakeoffFill size={20} />
<p className="text-base font-bold">{t('landing_page_v2:CTA.FREE_TRIAL_BTN')}</p>
</LandingButton>
Expand All @@ -127,7 +154,11 @@ const CTA: React.FC = () => {
</div>

{/* Info: (20241211 - Julian) CTA Intro Card */}
<div className="mt-140px grid grid-cols-1 gap-36px md:mt-80px lg:mt-120px lg:grid-cols-3">
<div
className={` ${
isCtaRefVisible ? 'translate-y-0 opacity-100' : 'translate-y-200px opacity-0'
} mt-140px grid grid-cols-1 gap-36px transition-all duration-500 md:mt-80px lg:mt-120px lg:grid-cols-3`}
>
{displayIntroCards}
</div>
</div>
Expand Down
37 changes: 33 additions & 4 deletions src/components/landing_page_v2/easy_to_use.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useRef, useEffect } from 'react';
import { useTranslation } from 'next-i18next';
import Image from 'next/image';
import {
Expand Down Expand Up @@ -110,8 +110,29 @@ const EasyToUse: React.FC = () => {
},
];

const easyRef = useRef<HTMLDivElement>(null);

// Info: (20241218 - Julian) 卡片順序
const [currentOrder, setCurrentOrder] = useState<number[]>([0, 1, 2]);
// Info: (20250108 - Julian) 播放動畫
const [isEasyRefVisible, setIsEasyRefVisible] = useState(false);

const scrollHandler = () => {
if (easyRef.current) {
const rect = (easyRef.current as HTMLElement).getBoundingClientRect();
const rectTop = rect.top;
const windowHeight = window.innerHeight;

setIsEasyRefVisible(rectTop < windowHeight);
}
};

useEffect(() => {
window.addEventListener('scroll', scrollHandler, { passive: true });
return () => {
window.removeEventListener('scroll', scrollHandler);
};
}, []);

const toLeft = () => {
// Info: (20241218 - Julian) 左移:將最後一個元素移到第一個
Expand Down Expand Up @@ -147,14 +168,22 @@ const EasyToUse: React.FC = () => {
});

return (
<div className="flex flex-col py-120px">
<div ref={easyRef} className="flex flex-col py-120px">
{/* Info: (20241218 - Julian) Title */}
<LinearGradientText size={LinearTextSize.LG} align={TextAlign.CENTER}>
<LinearGradientText
size={LinearTextSize.LG}
align={TextAlign.CENTER}
className={`${
isEasyRefVisible ? 'translate-y-0 opacity-100' : '-translate-y-200px opacity-0'
} transition-all duration-500`}
>
{t('landing_page_v2:EASY_TO_USE.MAIN_TITLE')}
</LinearGradientText>

{/* Info: (20241218 - Julian) Carousel */}
<div className="relative mx-auto mt-80px h-300px w-full overflow-hidden bg-digital bg-cover bg-bottom bg-no-repeat md:h-550px lg:px-120px">
<div
className={`${isEasyRefVisible ? 'translate-y-0 opacity-100' : 'translate-y-full opacity-0'} relative mx-auto mt-80px h-300px w-full overflow-hidden bg-digital bg-cover bg-bottom bg-no-repeat transition-all duration-500 md:h-550px lg:px-120px`}
>
<div className="relative flex transform-gpu items-center justify-center">
{displayedCards}
</div>
Expand Down
47 changes: 42 additions & 5 deletions src/components/landing_page_v2/financial_report.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef, useState, useEffect } from 'react';
import Image from 'next/image';
import { useTranslation } from 'next-i18next';
import {
Expand All @@ -10,16 +10,49 @@ import {
const FinancialReport: React.FC = () => {
const { t } = useTranslation('common');

const reportRef = useRef<HTMLDivElement>(null);
const [isReportRefVisible, setIsReportRefVisible] = useState(false);

const scrollHandler = () => {
if (reportRef.current) {
const rect = (reportRef.current as HTMLElement).getBoundingClientRect();
const rectTop = rect.top;
const windowHeight = window.innerHeight;

setIsReportRefVisible(rectTop < windowHeight);
}
};

useEffect(() => {
window.addEventListener('scroll', scrollHandler, { passive: true });
return () => {
window.removeEventListener('scroll', scrollHandler);
};
}, []);

return (
<div className="flex flex-col items-start justify-between bg-landing-page-black3 px-16px py-120px md:px-80px lg:flex-row lg:items-center lg:px-112px">
<div
ref={reportRef}
className="flex flex-col items-start justify-between bg-landing-page-black3 px-16px py-120px md:px-80px lg:flex-row lg:items-center lg:px-112px"
>
<div className="order-2 flex flex-col gap-16px lg:order-1 lg:w-1/2">
{/* Info: (20241218 - Julian) Title */}
<LinearGradientText size={LinearTextSize.LG} align={TextAlign.LEFT}>
<LinearGradientText
size={LinearTextSize.LG}
align={TextAlign.LEFT}
className={`${
isReportRefVisible ? 'translate-y-0 opacity-100' : '-translate-y-200px opacity-0'
} transition-all duration-500`}
>
{t('landing_page_v2:REAL_TIME_REPORT.MAIN_TITLE')}
</LinearGradientText>

{/* Info: (20241218 - Julian) Reports */}
<ul className="list-none text-xs md:text-lg lg:text-xl">
<ul
className={`${
isReportRefVisible ? 'translate-x-0 opacity-100' : '-translate-x-full opacity-0'
} list-none text-xs transition-all duration-500 md:text-lg lg:text-xl`}
>
<li>{t('landing_page_v2:REAL_TIME_REPORT.BS')}</li>
<li>{t('landing_page_v2:REAL_TIME_REPORT.CFS')}</li>
<li>{t('landing_page_v2:REAL_TIME_REPORT.CIS')}</li>
Expand All @@ -28,7 +61,11 @@ const FinancialReport: React.FC = () => {
</div>

{/* Info: (20241218 - Julian) Image */}
<div className="order-1 mx-auto max-w-400px lg:order-2 lg:w-1/2">
<div
className={`${
isReportRefVisible ? 'translate-x-0 opacity-100' : 'translate-x-full opacity-0'
} order-1 mx-auto max-w-400px transition-all duration-500 lg:order-2 lg:w-1/2`}
>
<Image
src="/elements/real_time_report.png"
width={544}
Expand Down
53 changes: 49 additions & 4 deletions src/components/landing_page_v2/flexible_feature_selection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef, useState, useEffect } from 'react';
import { useTranslation } from 'next-i18next';
import Image from 'next/image';
import Link from 'next/link';
Expand Down Expand Up @@ -41,6 +41,37 @@ const FlexibleFeatureIcon: React.FC<{ feature: string; size?: number; halo?: boo
const FlexibleFeatureSelection: React.FC = () => {
const { t } = useTranslation('common');

const featureHeadRef = useRef<HTMLDivElement>(null);
const featureFirstRef = useRef<HTMLDivElement>(null);

const [isFeatureHeadRefVisible, setIsFeatureHeadRefVisible] = useState(false);
const [isFeatureFirstRefVisible, setIsFeatureFirstRefVisible] = useState(false);

const scrollHandler = () => {
if (featureHeadRef.current) {
const rect = (featureHeadRef.current as HTMLElement).getBoundingClientRect();
const rectTop = rect.top;
const windowHeight = window.innerHeight;

setIsFeatureHeadRefVisible(rectTop < windowHeight);
}

if (featureFirstRef.current) {
const rect = (featureFirstRef.current as HTMLElement).getBoundingClientRect();
const rectTop = rect.top;
const windowHeight = window.innerHeight;

setIsFeatureFirstRefVisible(rectTop < windowHeight);
}
};

useEffect(() => {
window.addEventListener('scroll', scrollHandler, { passive: true });
return () => {
window.removeEventListener('scroll', scrollHandler);
};
}, []);

// Info: (20241219 - Julian) 第一分類:主要功能
const featuresOfFirstPart = [
'Dashboard',
Expand Down Expand Up @@ -81,14 +112,28 @@ const FlexibleFeatureSelection: React.FC = () => {
// const featuresOfFifthPart = ['Manufacturing Management', 'Supply Chain Management'];

return (
<div className="flex flex-col gap-120px px-16px py-120px md:px-80px lg:px-112px">
<div
ref={featureHeadRef}
className="flex flex-col gap-120px px-16px py-120px md:px-80px lg:px-112px"
>
{/* Info: (20241219 - Julian) Title */}
<LinearGradientText size={LinearTextSize.LG} align={TextAlign.CENTER}>
<LinearGradientText
size={LinearTextSize.LG}
align={TextAlign.CENTER}
className={`${
isFeatureHeadRefVisible ? 'translate-y-0 opacity-100' : '-translate-y-200px opacity-0'
} transition-all duration-500`}
>
{t('landing_page_v2:FLEXIBLE_FEATURE_SELECTION.MAIN_TITLE')}
</LinearGradientText>

{/* Info: (20241219 - Julian) Features of First Part */}
<div className="grid grid-cols-2 gap-34px md:grid-cols-3 lg:grid-cols-5">
<div
ref={featureFirstRef}
className={`${
isFeatureFirstRefVisible ? 'translate-x-0 opacity-100' : '-translate-x-full opacity-0'
} grid grid-cols-2 gap-34px transition-all duration-500 md:grid-cols-3 lg:grid-cols-5`}
>
{featuresOfFirstPart.map((feature) => (
<FlexibleFeatureIcon key={feature} feature={feature} />
))}
Expand Down
44 changes: 39 additions & 5 deletions src/components/landing_page_v2/global_map.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef, useState, useEffect } from 'react';
import Image from 'next/image';
import { useTranslation } from 'next-i18next';
import {
Expand All @@ -10,18 +10,52 @@ import {
const GlobalMap: React.FC = () => {
const { t } = useTranslation('common');

const mapRef = useRef<HTMLDivElement>(null);
const [isMapRefVisible, setIsMapRefVisible] = useState(false);

const scrollHandler = () => {
if (mapRef.current) {
const rect = (mapRef.current as HTMLElement).getBoundingClientRect();
const rectTop = rect.top;
const windowHeight = window.innerHeight;

setIsMapRefVisible(rectTop < windowHeight);
}
};

useEffect(() => {
window.addEventListener('scroll', scrollHandler, { passive: true });
return () => {
window.removeEventListener('scroll', scrollHandler);
};
}, []);

return (
<div className="flex flex-col px-16px py-120px md:px-80px lg:px-100px">
<div ref={mapRef} className="flex flex-col px-16px py-120px md:px-80px lg:px-100px">
<div className="flex flex-col gap-16px">
<LinearGradientText size={LinearTextSize.LG} align={TextAlign.LEFT}>
<LinearGradientText
size={LinearTextSize.LG}
align={TextAlign.LEFT}
className={`${
isMapRefVisible ? 'translate-y-0 opacity-100' : '-translate-y-200px opacity-0'
} transition-all duration-500`}
>
{t('landing_page_v2:GLOBAL_MAP.MAIN_TITLE')}
</LinearGradientText>
<p className="text-xs font-medium md:text-lg lg:text-xl">
<p
className={`${
isMapRefVisible ? 'translate-x-0 opacity-100' : '-translate-x-200px opacity-0'
} text-xs font-medium transition-all duration-500 md:text-lg lg:text-xl`}
>
{t('landing_page_v2:GLOBAL_MAP.MAIN_DESCRIPTION')}
</p>
</div>

<div className="relative flex h-300px w-full items-center lg:h-700px">
<div
className={`${
isMapRefVisible ? 'translate-y-0 opacity-100' : 'translate-y-200px opacity-0'
} relative flex h-300px w-full items-center transition-all duration-500 lg:h-700px`}
>
<Image src="/elements/map.svg" fill objectFit="contain" alt="map" />
<p className="absolute right-1/5 -mt-10px text-xxs md:right-1/8 md:-mt-30px md:text-base">
{t('landing_page_v2:GLOBAL_MAP.COMING_SOON')}
Expand Down
Loading

0 comments on commit 7cbd73d

Please sign in to comment.