styled-components 설치하기
yarn add styled-components
App.js
import React from 'react';
import BucketList from './BucketList';
import styled from 'styled-components';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
list: ['영화관 가기', '매일 책읽기', '수영 배우기'],
};
}
componentDidMount() {
}
render() {
return (
<div className="App">
<Container>
<Title>내 버킷리스트</Title>
<Line/>
<BucketList list = {this.state.list} />
</Container>
</div>
);
}
}
const Container = styled.div`
max-width: 350px;
min-height: 80vh;
background-color: #fff;
padding: 16px;
margin: 20px auto;
border-radius: 5px;
border: 1px solid #ddd;
`;
const Title = styled.h1`
color: slateblue;
text-align: center;
`;
const Line = styled.hr`
margin: 16px 0px;
border: 1px dotted #ddd;
`;
export default App;
BucketList.js
import React from 'react';
import styled from 'styled-components';
const BucketList = (props) => {
const my_lists = props.list;
return (
<ListStyle>
{my_lists.map((list, index) => {
return (
<ItemStyle key={index}>
{list}
</ItemStyle>
)
})}
</ListStyle>
);
};
const ListStyle = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
`;
const ItemStyle = styled.div`
padding: 16px;
margin: 8px;
background-color: aliceblue;
`;
export default BucketList;
App.js
import React from 'react';
import './App.css';
import Nemo from './Nemo';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return(
<div className="App">
<Nemo/>
</div>
);
}
}
export default App;
Nemo.js
import React from 'react';
const Nemo = () => {
const [count, setCount] = React.useState(3);
const addNemo = () => {
setCount(count + 1);
}
const removeNemo = () => {
setCount(count>0 ? count - 1 : 0);
}
const nemo_count = Array.from({length: count}, (v, i) => i);
return (
<div>
{nemo_count.map((num, idx) => {
return (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
width: "150px",
height: "150px",
background: "#eee",
margin: "10px",
}}
>nemo</div>
);
})}
<button onClick={addNemo}>하나 추가</button>
<button onClick={removeNemo}>하나 빼기</button>
</div>
);
}
export default Nemo;
App.js
import React from 'react';
import BucketList from './BucketList';
import styled from 'styled-components';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
list: ['영화관 가기', '매일 책읽기', '수영 배우기'],
};
this.text = React.createRef();
}
addBucketList = () => {
let list = this.state.list;
const new_item = this.text.current.value;
this.setState({list: [...list, new_item]});
}
componentDidMount() {
console.log(this.text);
console.log(this.text.current);
}
render() {
return (
<div className="App">
<Container>
<Title>내 버킷리스트</Title>
<Line/>
<BucketList list = {this.state.list} />
</Container>
<Input>
<input type="text" ref={this.text}/>
<button onClick={this.addBucketList}>추가하기</button>
</Input>
</div>
);
}
}
const Input = styled.div`
max-width: 350px;
min-height: 10vh;
background-color: #fff;
padding: 16px;
margin: 20px auto;
border-radius: 5px;
border: 1px solid #ddd;
`;
const Container = styled.div`
max-width: 350px;
min-height: 80vh;
background-color: #fff;
padding: 16px;
margin: 20px auto;
border-radius: 5px;
border: 1px solid #ddd;
`;
const Title = styled.h1`
color: slateblue;
text-align: center;
`;
const Line = styled.hr`
margin: 16px 0px;
border: 1px dotted #ddd;
`;
export default App;
BucketList.js
import React from 'react';
import styled from 'styled-components';
const BucketList = (props) => {
const my_lists = props.list;
return (
<ListStyle>
{my_lists.map((list, index) => {
return (
<ItemStyle key={index}>
{list}
</ItemStyle>
)
})}
</ListStyle>
);
};
const ListStyle = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
`;
const ItemStyle = styled.div`
padding: 16px;
margin: 8px;
background-color: aliceblue;
`;
export default BucketList;
스와이프 이벤트를 미리 구현해준 패키기 다운로드
yarn add react-tinder-card
App.js
import React from "react";
import Start from "./Start";
import Quiz from './Quiz';
import Score from './Score';
class App extends React.Component {
constructor(props){
super(props);
this.state = {
name: "르탄이",
page: "quiz",
list: [
{question: "르탄이는 1살이다", answer: "O"},
{question: "르탄이는 2살이다", answer: "O"},
{question: "르탄이는 3살이다", answer: "O"},
{question: "르탄이는 4살이다", answer: "O"},
],
scoreMsg: "이 정도면 아주 친한 친구 사이! 앞으로도 더 친하게 지내요!:)"
};
}
render () {
return (
<div className="App">
{this.state.page === "quiz" && <Quiz list={this.state.list}/>}
{this.state.page === "start" && <Start name={this.state.name}/>}
{this.state.page === "score" && <Score name={this.state.name} scoreMsg={this.state.scoreMsg}/>}
{/* <Start name={this.state.name}/> */}
{/* <Quiz/> */}
</div>
);
}
}
export default App;
Quiz.js
import React from "react";
import styled from "styled-components";
import img from "./scc_img01.png";
import TinderCard from "react-tinder-card";
import SwipeItem from "./SwipeItem";
const Quiz = (props) => {
console.log(props);
const [num, setNum] = React.useState(0);
const onSwipe = (direction) => {
console.log("You swiped: " + direction);
setNum(num + 1);
};
if (num > 10) {
return <div>퀴즈 끝!</div>;
}
return (
<QuizContainer>
<p>
<span>{num + 1}번 문제</span>
</p>
{props.list.map((l, idx) => {
if (num === idx) {
return <Question key={idx}>{l.question}</Question>;
}
})}
<AnswerZone>
<Answer>{"O "}</Answer>
<Answer>{" X"}</Answer>
</AnswerZone>
{props.list.map((l, idx) => {
if (idx === num) {
return <SwipeItem key={idx} onSwipe={onSwipe} />;
}
})}
</QuizContainer>
);
};
const QuizContainer = styled.div`
text-align: center;
& > p > span {
padding: 8px 16px;
background-color: #fef5d4;
// border-bottom: 3px solid #ffd6aa;
border-radius: 30px;
}
`;
const Question = styled.h1`
font-size: 1.5em;
`;
const AnswerZone = styled.div`
width: 100%;
display: flex;
flex-direction: row;
min-height: 70vh;
`;
const Answer = styled.div`
width: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 100px;
font-weight: 600;
color: #dadafc77;
`;
const DragItem = styled.div`
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
& > div {
border-radius: 500px;
background-color: #ffd6aa;
}
& img {
max-width: 150px;
}
`;
export default Quiz;
Score.js
import React from 'react';
import styled from 'styled-components';
const Score = (props) => {
return (
<ScoreContainer>
<Text> <span>{props.name}</span> 퀴즈에 <br/>
대한 내 점수는?
</Text>
<MyScore>
<span>100</span>점
<p>{props.scoreMsg}</p>
</MyScore>
<Button>랭킹보기</Button>
</ScoreContainer>
);
};
const ScoreContainer = styled.div`
display: flex;
width: 100vw;
height: 100vh;
overflow: hidden;
padding: 16px;
box-sizing: border-box;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const Text = styled.h1`
font-size: 1.5em;
margin: 0px;
line-height: 1.4;
& span {
background-color: #fef5d4;
padding: 5px 10px;
border-radius: 30px;
}
`;
const MyScore = styled.div`
text-align: center;
& span {
background-color: #fef5d4;
padding: 5px 10px;
border-radius: 30px;
}
font-weight: 600;
font-size: 2em;
margin: 24px;
& > p {
margin: 24px 0;
font-size: 16px;
font-weight: 400;
}
`;
const Button = styled.button`
padding: 8px 24px;
background-color: #dadafc;
border-radius: 80px;
margin: 8px;
border: 1px solid #dadafc;
width: 80vw;
`;
export default Score;
Start.js
import React from "react";
import img from "./scc_img01.png";
const Start = (props) => {
return (
<div
style={{
display: "flex",
height: "100vh",
width: "100vw",
overflow: "hidden",
padding: "16px",
boxSizing: "border-box",
}}
>
<div
className="outter"
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexDirection: "column",
height: "100vh",
width: "100vw",
overflow: "hidden",
padding: "0px 10vw",
boxSizing: "border-box",
maxWidth: "400px",
}}
>
<img src={img} style={{ width: "80%", margin: "16px" }} />
<h1 style={{ fontSize: "1.5em", margin: "0px", lineHeight: "1.4" }}>
나는{" "}
<span
style={{
backgroundColor: "#fef5d4",
padding: "5px 10px",
borderRadius: "30px",
}}
>
{props.name}
</span>
에 대해 얼마나 알고 있을까?
</h1>
<input
type="text"
style={{
padding: "10px",
margin: "24px 0px",
border: "1px solid #dadafc",
borderRadius: "30px",
width: "100%",
// backgroundColor: "#dadafc55",
}}
placeholder="내 이름"
/>
<button
style={{
padding: "8px 24px",
backgroundColor: "#dadafc",
borderRadius: "30px",
border: "#dadafc",
}}
>
시작하기
</button>
</div>
</div>
);
};
export default Start;
SwipeItem.js
import React from "react";
import styled from "styled-components";
import img from "./scc_img01.png";
const SwipeItem = React.memo(({ onSwipe }) => {
const swipe_div = React.useRef(null);
let swipe_status = "ready";
let target_classname = "";
let coordinate = {
start_x: 0,
start_y: 0,
end_x: 0,
end_y: 0,
};
React.useEffect(() => {
const reset = () => {
swipe_status = "ready";
coordinate = {
start_x: 0,
start_y: 0,
end_x: 0,
end_y: 0,
};
swipe_div.current.className = target_classname;
swipe_div.current.style.left = 0 + "px";
swipe_div.current.style.top = 0 + "px";
};
const touchStart = (e) => {
swipe_status = "touchstart";
target_classname = swipe_div.current.className;
coordinate = {
...coordinate,
start_x: e.touches[0].clientX,
start_y: e.touches[0].clientY,
};
};
const touchEnd = (e) => {
swipe_status = "touchend";
coordinate = {
...coordinate,
end_x: e.changedTouches[0].clientX,
end_y: e.changedTouches[0].clientY,
};
let diff_x = coordinate.end_x - coordinate.start_x;
let direct = "left";
if (Math.abs(diff_x) > 50) {
swipe_div.current.className = target_classname + " swipe";
if (diff_x > 0) {
direct = "right";
swipe_div.current.style.left = diff_x + 150 + "px";
swipe_div.current.style.opacity = 0;
} else {
direct = "left";
swipe_div.current.style.left = diff_x - 150 + "px";
swipe_div.current.style.opacity = 0;
}
window.setTimeout(() => {
reset();
onSwipe(direct);
}, 300);
return;
}
reset();
};
const touchMove = (e) => {
e.preventDefault();
let current_coordinate = {
x: e.touches[0].clientX,
y: e.touches[0].clientY,
};
swipe_div.current.style.left =
current_coordinate.x - coordinate.start_x + "px";
swipe_div.current.style.top =
current_coordinate.y - coordinate.start_y + "px";
};
const touchCancel = (e) => {
swipe_status = "cancel";
reset();
};
swipe_div.current.addEventListener("touchstart", touchStart);
swipe_div.current.addEventListener("touchend", touchEnd);
swipe_div.current.addEventListener("touchmove", touchMove);
swipe_div.current.addEventListener("touchcancel", touchCancel);
return () => {
if (!swipe_div.current) {
return;
}
swipe_div.current.removeEventListener("touchstart", touchStart);
swipe_div.current.removeEventListener("touchend", touchEnd);
swipe_div.current.removeEventListener("touchmove", touchMove);
swipe_div.current.removeEventListener("touchcancel", touchCancel);
};
}, []);
return (
<DragItem ref={swipe_div}>
<img src={img} />
</DragItem>
);
});
const DragItem = styled.div`
display: flex;
align-items: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
&.swipe {
transition: 300ms;
}
& > div {
border-radius: 500px;
background-color: #ffd6aa;
}
& img {
max-width: 150px;
}
`;
SwipeItem.defaultProps = {
onSwipe: (direction) => {},
};
export default SwipeItem;