chore: add animation for tour modal (#2365)

This commit is contained in:
JimmFly
2023-05-15 16:48:52 +08:00
committed by GitHub
parent 2c4db4fa16
commit 0bfcab4067
2 changed files with 175 additions and 37 deletions
@@ -1,29 +1,91 @@
import { style } from '@vanilla-extract/css'; import { keyframes, style } from '@vanilla-extract/css';
export const modalStyle = style({ export const modalStyle = style({
width: '100%', width: '100%',
height: '100%',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
position: 'relative', position: 'relative',
backgroundColor: 'var(--affine-background-secondary-color)', backgroundColor: 'var(--affine-background-secondary-color)',
borderRadius: '16px', borderRadius: '16px',
overflow: 'hidden',
});
export const titleContainerStyle = style({
width: 'calc(100% - 72px)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
height: '60px',
overflow: 'hidden',
}); });
export const titleStyle = style({ export const titleStyle = style({
fontSize: 'var(--affine-font-h6)', fontSize: 'var(--affine-font-h6)',
fontWeight: '600', fontWeight: '600',
marginTop: '12px', marginTop: '12px',
position: 'absolute',
marginBottom: '12px',
}); });
const slideToLeft = keyframes({
'0%': {
transform: 'translateX(0)',
opacity: 1,
},
'100%': {
transform: 'translateX(-300px)',
opacity: 0,
},
});
const slideToRight = keyframes({
'0%': {
transform: 'translateX(0)',
opacity: 1,
},
'100%': {
transform: 'translateX(300px)',
opacity: 0,
},
});
const slideFormLeft = keyframes({
'0%': {
transform: 'translateX(300px)',
opacity: 0,
},
'100%': {
transform: 'translateX(0)',
opacity: 1,
},
});
const slideFormRight = keyframes({
'0%': {
transform: 'translateX(-300px)',
opacity: 0,
},
'100%': {
transform: 'translateX(0)',
opacity: 1,
},
});
export const formSlideToLeftStyle = style({
animation: `${slideFormLeft} 0.3s ease-in-out forwards`,
});
export const formSlideToRightStyle = style({
animation: `${slideFormRight} 0.3s ease-in-out forwards`,
});
export const slideToLeftStyle = style({
animation: `${slideToLeft} 0.3s ease-in-out forwards`,
});
export const slideToRightStyle = style({
animation: `${slideToRight} 0.3s ease-in-out forwards`,
});
export const containerStyle = style({ export const containerStyle = style({
paddingTop: '25px',
width: '100%', width: '100%',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
}); });
export const videoContainerStyle = style({ export const videoContainerStyle = style({
paddingTop: '25px',
height: '300px', height: '300px',
width: 'calc(100% - 72px)', width: 'calc(100% - 72px)',
display: 'flex', display: 'flex',
@@ -31,6 +93,7 @@ export const videoContainerStyle = style({
flexGrow: 1, flexGrow: 1,
justifyContent: 'space-between', justifyContent: 'space-between',
position: 'relative', position: 'relative',
overflow: 'hidden',
}); });
export const videoSlideStyle = style({ export const videoSlideStyle = style({
width: '100%', width: '100%',
@@ -46,9 +109,19 @@ export const videoStyle = style({
border: '1px solid var(--affine-border-color)', border: '1px solid var(--affine-border-color)',
transition: 'opacity 0.5s ease-in-out', transition: 'opacity 0.5s ease-in-out',
}); });
const fadeIn = keyframes({
'0%': {
transform: 'translateX(300px)',
},
'100%': {
transform: 'translateX(0)',
},
});
export const videoActiveStyle = style({ export const videoActiveStyle = style({
animation: `${fadeIn} 0.5s ease-in-out forwards`,
opacity: 0, opacity: 0,
}); });
export const arrowStyle = style({ export const arrowStyle = style({
wordBreak: 'break-all', wordBreak: 'break-all',
wordWrap: 'break-word', wordWrap: 'break-word',
@@ -61,29 +134,52 @@ export const arrowStyle = style({
flexGrow: 0.2, flexGrow: 0.2,
cursor: 'pointer', cursor: 'pointer',
}); });
export const descriptionContainerStyle = style({
width: 'calc(100% - 112px)',
height: '100px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
position: 'relative',
overflow: 'hidden',
});
export const descriptionStyle = style({ export const descriptionStyle = style({
marginTop: '15px', marginTop: '15px',
width: '100%', width: '100%',
display: 'flex', display: 'flex',
padding: '0 56px',
fontSize: 'var(--affine-font-sm)', fontSize: 'var(--affine-font-sm)',
lineHeight: '18px', lineHeight: '18px',
marginBottom: '20px', position: 'absolute',
}); });
export const tabStyle = style({ export const tabStyle = style({
width: '40px', width: '40px',
height: '20px', height: '40px',
content: '""', content: '""',
borderBottom: '2px solid var(--affine-text-primary-color)', margin: '40px 10px 40px 0',
opacity: 0.2,
margin: '0 10px 20px 0',
transition: 'all 0.15s ease-in-out', transition: 'all 0.15s ease-in-out',
position: 'relative',
cursor: 'pointer',
':hover': { ':hover': {
opacity: 1, opacity: 1,
}, },
'::after': {
content: '""',
position: 'absolute',
bottom: '20px',
left: '0',
width: '100%',
height: '2px',
background: 'var(--affine-text-primary-color)',
transition: 'all 0.15s ease-in-out',
opacity: 0.2,
cursor: 'pointer',
},
}); });
export const tabActiveStyle = style({ export const tabActiveStyle = style({
opacity: 1, '::after': {
opacity: 1,
},
}); });
export const tabContainerStyle = style({ export const tabContainerStyle = style({
width: '100%', width: '100%',
@@ -8,13 +8,18 @@ import { Modal, ModalCloseButton, ModalWrapper } from '../..';
import { import {
arrowStyle, arrowStyle,
containerStyle, containerStyle,
descriptionContainerStyle,
descriptionStyle, descriptionStyle,
formSlideToLeftStyle,
formSlideToRightStyle,
modalStyle, modalStyle,
slideToLeftStyle,
slideToRightStyle,
tabActiveStyle, tabActiveStyle,
tabContainerStyle, tabContainerStyle,
tabStyle, tabStyle,
titleContainerStyle,
titleStyle, titleStyle,
videoActiveStyle,
videoContainerStyle, videoContainerStyle,
videoSlideStyle, videoSlideStyle,
videoStyle, videoStyle,
@@ -27,9 +32,9 @@ type TourModalProps = {
export const TourModal: FC<TourModalProps> = ({ open, onClose }) => { export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const [step, setStep] = useState(0); const [step, setStep] = useState(-1);
const handleClose = () => { const handleClose = () => {
setStep(0); setStep(-1);
onClose(); onClose();
}; };
return ( return (
@@ -51,39 +56,59 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
data-testid="onboarding-modal-close-button" data-testid="onboarding-modal-close-button"
/> />
<div className={modalStyle}> <div className={modalStyle}>
<div className={titleStyle}> <div className={titleContainerStyle}>
{step === 1 {step !== -1 && (
? t['com.affine.onboarding.title2']() <div
: t['com.affine.onboarding.title1']()} className={clsx(titleStyle, {
[slideToLeftStyle]: step === 0,
[formSlideToRightStyle]: step === 1,
})}
>
{t['com.affine.onboarding.title2']()}
</div>
)}
<div
className={clsx(titleStyle, {
[slideToRightStyle]: step === 1,
[formSlideToLeftStyle]: step === 0,
})}
>
{t['com.affine.onboarding.title1']()}
</div>
</div> </div>
<div className={containerStyle}> <div className={containerStyle}>
<div <div
className={arrowStyle} className={arrowStyle}
onClick={() => setStep(0)} onClick={() => step === 1 && setStep(0)}
data-testid="onboarding-modal-pre-button" data-testid="onboarding-modal-pre-button"
> >
<ArrowLeftSmallIcon /> <ArrowLeftSmallIcon />
</div> </div>
<div className={videoContainerStyle}> <div className={videoContainerStyle}>
<div className={videoSlideStyle}> <div className={videoSlideStyle}>
{step !== -1 && (
<video
autoPlay
muted
loop
className={clsx(videoStyle, {
[slideToLeftStyle]: step === 0,
[formSlideToRightStyle]: step === 1,
})}
data-testid="onboarding-modal-editing-video"
>
<source src="/editingVideo.mp4" type="video/mp4" />
<source src="/editingVideo.webm" type="video/webm" />
</video>
)}
<video <video
autoPlay autoPlay
muted muted
loop loop
className={clsx(videoStyle, { className={clsx(videoStyle, {
[videoActiveStyle]: step === 0, [slideToRightStyle]: step === 1,
})} [formSlideToLeftStyle]: step === 0,
data-testid="onboarding-modal-editing-video"
>
<source src="/editingVideo.mp4" type="video/mp4" />
<source src="/editingVideo.webm" type="video/webm" />
</video>
<video
autoPlay
muted
loop
className={clsx(videoStyle, {
[videoActiveStyle]: step === 1,
})} })}
data-testid="onboarding-modal-switch-video" data-testid="onboarding-modal-switch-video"
> >
@@ -102,7 +127,9 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
</div> </div>
<ul className={tabContainerStyle}> <ul className={tabContainerStyle}>
<li <li
className={clsx(tabStyle, { [tabActiveStyle]: step === 0 })} className={clsx(tabStyle, {
[tabActiveStyle]: step !== 1,
})}
onClick={() => setStep(0)} onClick={() => setStep(0)}
></li> ></li>
<li <li
@@ -110,10 +137,25 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
onClick={() => setStep(1)} onClick={() => setStep(1)}
></li> ></li>
</ul> </ul>
<div className={descriptionStyle}> <div className={descriptionContainerStyle}>
{step === 1 {step !== -1 && (
? t['com.affine.onboarding.videoDescription2']() <div
: t['com.affine.onboarding.videoDescription1']()} className={clsx(descriptionStyle, {
[slideToLeftStyle]: step === 0,
[formSlideToRightStyle]: step === 1,
})}
>
{t['com.affine.onboarding.videoDescription2']()}
</div>
)}
<div
className={clsx(descriptionStyle, {
[slideToRightStyle]: step === 1,
[formSlideToLeftStyle]: step === 0,
})}
>
{t['com.affine.onboarding.videoDescription1']()}
</div>
</div> </div>
</div> </div>
</ModalWrapper> </ModalWrapper>