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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 175 additions and 37 deletions

View File

@ -1,29 +1,91 @@
import { style } from '@vanilla-extract/css';
import { keyframes, style } from '@vanilla-extract/css';
export const modalStyle = style({
width: '100%',
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
position: 'relative',
backgroundColor: 'var(--affine-background-secondary-color)',
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({
fontSize: 'var(--affine-font-h6)',
fontWeight: '600',
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({
paddingTop: '25px',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
});
export const videoContainerStyle = style({
paddingTop: '25px',
height: '300px',
width: 'calc(100% - 72px)',
display: 'flex',
@ -31,6 +93,7 @@ export const videoContainerStyle = style({
flexGrow: 1,
justifyContent: 'space-between',
position: 'relative',
overflow: 'hidden',
});
export const videoSlideStyle = style({
width: '100%',
@ -46,9 +109,19 @@ export const videoStyle = style({
border: '1px solid var(--affine-border-color)',
transition: 'opacity 0.5s ease-in-out',
});
const fadeIn = keyframes({
'0%': {
transform: 'translateX(300px)',
},
'100%': {
transform: 'translateX(0)',
},
});
export const videoActiveStyle = style({
animation: `${fadeIn} 0.5s ease-in-out forwards`,
opacity: 0,
});
export const arrowStyle = style({
wordBreak: 'break-all',
wordWrap: 'break-word',
@ -61,29 +134,52 @@ export const arrowStyle = style({
flexGrow: 0.2,
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({
marginTop: '15px',
width: '100%',
display: 'flex',
padding: '0 56px',
fontSize: 'var(--affine-font-sm)',
lineHeight: '18px',
marginBottom: '20px',
position: 'absolute',
});
export const tabStyle = style({
width: '40px',
height: '20px',
height: '40px',
content: '""',
borderBottom: '2px solid var(--affine-text-primary-color)',
opacity: 0.2,
margin: '0 10px 20px 0',
margin: '40px 10px 40px 0',
transition: 'all 0.15s ease-in-out',
position: 'relative',
cursor: 'pointer',
':hover': {
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({
opacity: 1,
'::after': {
opacity: 1,
},
});
export const tabContainerStyle = style({
width: '100%',

View File

@ -8,13 +8,18 @@ import { Modal, ModalCloseButton, ModalWrapper } from '../..';
import {
arrowStyle,
containerStyle,
descriptionContainerStyle,
descriptionStyle,
formSlideToLeftStyle,
formSlideToRightStyle,
modalStyle,
slideToLeftStyle,
slideToRightStyle,
tabActiveStyle,
tabContainerStyle,
tabStyle,
titleContainerStyle,
titleStyle,
videoActiveStyle,
videoContainerStyle,
videoSlideStyle,
videoStyle,
@ -27,9 +32,9 @@ type TourModalProps = {
export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
const t = useAFFiNEI18N();
const [step, setStep] = useState(0);
const [step, setStep] = useState(-1);
const handleClose = () => {
setStep(0);
setStep(-1);
onClose();
};
return (
@ -51,39 +56,59 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
data-testid="onboarding-modal-close-button"
/>
<div className={modalStyle}>
<div className={titleStyle}>
{step === 1
? t['com.affine.onboarding.title2']()
: t['com.affine.onboarding.title1']()}
<div className={titleContainerStyle}>
{step !== -1 && (
<div
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 className={containerStyle}>
<div
className={arrowStyle}
onClick={() => setStep(0)}
onClick={() => step === 1 && setStep(0)}
data-testid="onboarding-modal-pre-button"
>
<ArrowLeftSmallIcon />
</div>
<div className={videoContainerStyle}>
<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
autoPlay
muted
loop
className={clsx(videoStyle, {
[videoActiveStyle]: 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,
[slideToRightStyle]: step === 1,
[formSlideToLeftStyle]: step === 0,
})}
data-testid="onboarding-modal-switch-video"
>
@ -102,7 +127,9 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
</div>
<ul className={tabContainerStyle}>
<li
className={clsx(tabStyle, { [tabActiveStyle]: step === 0 })}
className={clsx(tabStyle, {
[tabActiveStyle]: step !== 1,
})}
onClick={() => setStep(0)}
></li>
<li
@ -110,10 +137,25 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
onClick={() => setStep(1)}
></li>
</ul>
<div className={descriptionStyle}>
{step === 1
? t['com.affine.onboarding.videoDescription2']()
: t['com.affine.onboarding.videoDescription1']()}
<div className={descriptionContainerStyle}>
{step !== -1 && (
<div
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>
</ModalWrapper>