mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-23 13:02:21 +08:00
chore: add animation for tour modal (#2365)
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user