Skip to content

Commit

Permalink
Merge pull request #264 from boostcampwm-2022/242-feat-튜토리얼-업데이트
Browse files Browse the repository at this point in the history
FEAT: 튜토리얼 업데이트
  • Loading branch information
daeseong9388 authored Dec 18, 2022
2 parents 72f9bd1 + 56e8950 commit d618005
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 934 deletions.
5 changes: 2 additions & 3 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ import Menubar from '@container/Menubar';
import Main from '@page/Main';
import Todos from '@page/Todos';
import DiagramPage from '@page/DiagramPage';
import OverLay from '@components/OverLay';
import TodoController from '@container/TodoController';

import { TutorialImage } from '@components/tutorial/TutorialImage';
import { TutorialVideo } from '@components/tutorial/TutorialVideo';
import { isTutorialAtom } from '@util/GlobalState';
import { PRIMARY_COLORS } from '@util/Constants';

Expand Down Expand Up @@ -82,7 +81,7 @@ const App = (): ReactElement => {
</Routes>
</Wrapper>
<TodoController />
{isShow && <TutorialImage isTutorial={isTutorial} setIsOver={setIsOver} />}
{isShow && <TutorialVideo setIsOver={setIsOver} />}
{isTutorial && (
<TutorialRadialOverlay>
<span>
Expand Down
78 changes: 66 additions & 12 deletions client/src/components/tutorial/Dots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,88 @@ import { ReactElement } from 'react';
import styled from 'styled-components';
import { PRIMARY_COLORS } from '@util/Constants';

const Dot = styled.span<{ active: boolean }>`
padding: 5px;
margin-right: 10px;
cursor: none;
const Dot = styled.li<{ active: boolean }>`
position: relative;
width: 40px;
height: 40px;
border-radius: 50%;
background: ${(props) => (props.active ? `${PRIMARY_COLORS.lightGray}` : `${PRIMARY_COLORS.darkGray}`)}};
margin: 0 8px;
cursor: pointer;
text-align: -webkit-match-parent;
&::before {
content: '';
width: 20px;
height: 20px;
background-color: ${(props) => (props.active ? `${PRIMARY_COLORS.red}` : `${PRIMARY_COLORS.blue}`)};
border-radius: 50%;
display: flex;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
`;

const Svg = styled.svg`
width: 40px;
height: 40px;
fill: none;
`;

const Wrapper = styled.div`
position: absolute;
const Circle = styled.circle<{ duration: number }>`
opacity: 1;
stroke-linecap: round;
transform: rotate(-90deg);
transform-origin: 50% 50%;
stroke-width: 3px;
stroke: ${PRIMARY_COLORS.red};
stroke-dasharray: 330;
stroke-dashoffset: 60;
animation: progress ${(props) => `${props.duration}s`} linear;
@keyframes progress {
0% {
stroke-dashoffset: 330;
}
100% {
stroke-dashoffset: 60;
}
}
`;

const Wrapper = styled.ul`
display: flex;
justify-content: center;
align-items: center;
bottom: 25px;
width: 100%;
padding: 0;
margin: 15px 0 0;
list-style: none;
`;

interface DotsProps {
slides: string[];
currentIndex: number;
setCurrentIndex: React.Dispatch<React.SetStateAction<number>>;
duration: number;
}

export const Dots = ({ slides, currentIndex }: DotsProps): ReactElement => {
export const Dots = ({ slides, currentIndex, setCurrentIndex, duration }: DotsProps): ReactElement => {
return (
<Wrapper>
{slides.map((slide, i) => (
<Dot key={slide} active={currentIndex === i} />
))}
{slides.map((slide, i) => {
if (currentIndex === i) {
return (
<Dot key={slide} active={true} onClick={() => setCurrentIndex(i)}>
<Svg>
<Circle cx="20" cy="20" r="15" duration={2.9 * duration}></Circle>
</Svg>
</Dot>
);
}
return <Dot key={slide} active={false} onClick={() => setCurrentIndex(i)} />;
})}
</Wrapper>
);
};
95 changes: 0 additions & 95 deletions client/src/components/tutorial/TutorialImage.tsx

This file was deleted.

86 changes: 86 additions & 0 deletions client/src/components/tutorial/TutorialVideo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { ReactElement, useState, useRef } from 'react';
import styled from 'styled-components';

import { CancelIcon } from './Icons';
import { PRIMARY_COLORS } from '@util/Constants';
import Button from '@components/Button';
import { Dots } from './Dots';

import Main from '@images/tutorial/main.mp4';
import Table from '@images/tutorial/table.mp4';
import Diagram from '@images/tutorial/diagram.mp4';

const videoSrcArray: string[] = [Main, Table, Diagram];
const titleArray: string[] = ['메인 페이지', '테이블 뷰', '다이어그램 뷰'];

const Title = styled.h1`
font-family: 'Roboto';
color: ${PRIMARY_COLORS.red};
`;

const StyledOverlay = styled.div`
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
background-color: ${PRIMARY_COLORS.white};
width: 100vw;
height: 100vh;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 10;
& > video {
width: 80%;
height: 80%;
}
`;

const StyledCancelButton = styled(Button)`
position: absolute;
background-color: transparent;
top: 0px;
right: 0px;
`;

export const TutorialVideo = ({
setIsOver,
}: {
setIsOver: React.Dispatch<React.SetStateAction<boolean>>;
}): ReactElement => {
const ref = useRef<HTMLVideoElement>(null);
const [currentIndex, setCurrentIndex] = useState(0);
const [duration, setDuration] = useState(0);

const handleCancel = (): void => {
setIsOver(true);
};

const onEnded = (): void => {
setCurrentIndex(currentIndex === videoSrcArray.length - 1 ? 0 : currentIndex + 1);
};

const onLoadedMetadata = (): void => {
if (ref.current === null) return;
setDuration(ref.current.duration);
};

return (
<StyledOverlay>
<StyledCancelButton context={<CancelIcon fill={PRIMARY_COLORS.blue} />} onClick={handleCancel} />
<Title>{titleArray[currentIndex]}</Title>
<video
src={videoSrcArray[currentIndex]}
ref={ref}
onEnded={onEnded}
onLoadedMetadata={onLoadedMetadata}
autoPlay
/>
<Dots slides={videoSrcArray} currentIndex={currentIndex} setCurrentIndex={setCurrentIndex} duration={duration} />
</StyledOverlay>
);
};
3 changes: 2 additions & 1 deletion client/src/container/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const Wrapper = styled.div`

const Header = (): ReactElement => {
const [isTutorial, setIsTutorial] = useAtom(isTutorialAtom);
const url: string = isTutorial ? '/tutorials' : '/';
const [, changeIndexedDBtoMemory] = useAtom(changeIndexedDBtoMemoryAtom);
const [, changeMemorytoIndexedDB] = useAtom(changeMemorytoIndexedDBAtom);
const startTutorial = (): void => {
Expand All @@ -36,7 +37,7 @@ const Header = (): ReactElement => {
};
return (
<Wrapper>
<Link to="/">
<Link to={`${url}`}>
<Image src={LongLogo} flexGrow={3} />
</Link>
{isTutorial ? (
Expand Down
Binary file added client/src/images/tutorial/diagram.mp4
Binary file not shown.
Binary file added client/src/images/tutorial/main.mp4
Binary file not shown.
Binary file added client/src/images/tutorial/table.mp4
Binary file not shown.
Loading

0 comments on commit d618005

Please sign in to comment.