나도 이제 GIF 파일을 올리는 방법을 알았다 ㅎㅎ 그건 그렇고 오늘은 나의 댓글 input 부분에 애니메이션을 추가하였다. 정말정말 간단해 보이지만 실제로 작업하는 데 30분 이상 걸렸다...
<div className={styles.Comment_input_box}>
{
isFocus === false ?
<div className={styles.Comment_line}></div>
: <div className={styles.Comment_line_inAni}></div>
}
<input
onFocus={() => {
setIsFocus(true)
setFirstFocus(true)
}}
onBlur={() => setIsFocus(false)}
placeholder='댓글 추가...'
/>
<div>
{
firstFocus ?
<>
<div id={styles.Comment_btn1} className={styles.Comment_buttons}>취소</div>
<div id={styles.Comment_btn2} className={styles.Comment_buttons}>댓글</div>
</> :
<div className={styles.Comment_buttons}></div>
}
</div>
</div>
.Comment_input_box {
width: 100%;
}
.Comment_input_box div {
float: right;
display: flex;
flex-direction: row;
}
.Comment_input_box input {
width: 100%;
height: 100%;
background-color: transparent;
border: none;
color: #666;
vertical-align: middle;
box-sizing: border-box;
}
.Comment_input_box input:focus {
outline: none;
}
@keyframes inputAnimation {
from {
box-sizing: inherit;
border-bottom: 1px solid #000;
transform: scale3d(0, 1, 0);
}
to {
box-sizing: inherit;
border-bottom: 2px solid #000;
transform: scale3d(1, 1, 1);
}
}
.Comment_line {
position: absolute;
top: 116.5%;
height: 1px;
width: 62%;
background-color: lightgrey;
}
.Comment_line_inAni {
position: absolute;
top: 116.5%;
height: 2px;
width: 62%;
background-color: black;
animation-name: inputAnimation;
animation-duration: 0.3s;
}
.Comment_buttons {
width: 72px;
height: 37px;
line-height: 37px;
margin: 7px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
color: #909090;
font-size: 13px;
}
#Comment_btn1 {
background-color: rgb(246, 246, 246);
color: #787878;
}
#Comment_btn2 {
background-color: rgb(225, 225, 225);
}
keyframes로 애니메이션을 만들어서 적용시켜주는 방법을 사용하였다.
오늘은 어제 했던 부분에 이어서 댓글 작성 부분에 글을 입력하면 버튼의 색이 바뀌는 것과 취소 버튼을 눌렸을 때 버튼들이 사라지는 것을 만들었다. 그리고 댓글부분도 만들었다. 저 댓글들은 임의로 데이터를 만들어서 map함수로 컴포넌트를 리턴하는 방식으로 만들었다. 그리고 VideoPart.js부분에 모든 코드가 있는 것이 더러워 보여서 CommentPart.js로 컴포넌트를 나눠주기도 하였다.
import React, { useState } from 'react'
import styles from "../styles/CommentPart.module.css"
import { MdSort } from "react-icons/md"
import Comment from './Comment';
const CommentPart = () => {
const [firstFocus, setFirstFocus] = useState(false);
const [isFocus, setIsFocus] = useState(false);
const [isInput, setIsInput] = useState(false);
const commentsSample = [
{
imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu_D4JwoqK3mF9YcK1b6_sQ8lvdQ-luwDuqt3w=s88-c-k-c0x00ffffff-no-rj",
userName: "taehyun",
comment: "좋아요!!",
good: 5,
date: "2022/05/05"
},
{
imgSrc: "https://lh3.googleusercontent.com/ogw/AOh-ky1uAXwkV8yiFs8KaB07AxluQj2nYO0d-RgdXzqr=s64-c-mo",
userName: "태현",
comment: "정말 좋아요!!",
good: 10,
date: "2022/05/10"
},
{
imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu9kCKCBTyW3NUg0qiakoo_NPijLpMRjHyWyeUhO1-FAXAjbtINI76YzJSfQZ5JZCGnqUxU=s88-c-k-c0x00ffffff-no-rj",
userName: "구크르",
comment: "정말 재밌어요!!",
good: 2,
date: "2022/05/25"
},
]
return (
<div>
<div className={styles.Comment_box}>
<div className={styles.Comment_sort_box}>
<span className={styles.Comment_number}>댓글 1개</span>
<div>
<MdSort size={30}/>
<span className={styles.Comment_sort}>정렬 기준</span>
</div>
</div>
<div className={styles.Comment_input_part}>
<img src='https://yt3.ggpht.com/ytc/AMLnZu9Jzp859A5IesAX3WqVFY0ocYhG3_oFkYuLlNlH1KPJhA=s88-c-k-c0x00ffffff-no-rj-mo'/>
<div className={styles.Comment_input_box}>
{
isFocus === false ?
<div className={styles.Comment_line}></div>
: <div className={styles.Comment_line_inAni}></div>
}
<input
onFocus={() => {
setIsFocus(true)
setFirstFocus(true)
}}
onChange={(e) => {
if(e.target.value)setIsInput(true)
else setIsInput(false)
}}
onBlur={() => setIsFocus(false)}
placeholder='댓글 추가...'
/>
<div>
{
firstFocus ?
<>
<div
onClick={() => setFirstFocus(false)}
id={styles.Comment_btn1}
className={styles.Comment_buttons}
>
취소
</div>
{
!isInput ?
<div id={styles.Comment_btn2} className={styles.Comment_buttons}>댓글</div>
: <div id={styles.Comment_btn2_active} className={styles.Comment_buttons}>댓글</div>
}
</> :
<div className={styles.Comment_buttons}></div>
}
</div>
</div>
</div>
<div className='Comments_list'>
{
commentsSample.map((comment, idx) => {
return <Comment comment={comment} key={idx} />
})
}
</div>
</div>
</div>
)
}
export default CommentPart
import React from 'react'
import { FiThumbsDown, FiThumbsUp } from 'react-icons/fi'
import styles from "../styles/Comment.module.css"
const Comment = (props) => {
return (
<div className={styles.Comment_box}>
<img src={props.comment.imgSrc} />
<div>
<div className={styles.Comment_info}>
<p>{props.comment.userName}</p>
<span>{props.comment.date}</span>
</div>
<p>{props.comment.comment}</p>
<div className={styles.Comment_good_bad}>
<FiThumbsUp size={15} />
<span>{props.comment.good}</span>
<FiThumbsDown size={15} />
</div>
</div>
</div>
)
}
export default Comment
.Comment_box {
width: 100%;
}
.Comment_sort_box {
width: 200px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.Comment_sort_box div {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.Comment_sort {
color: black !important;
font-size: 14px !important;
}
.Comment_number {
font-size: 16px !important;
color: black !important;
}
.Comment_input_part {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
margin-top: 20px;
/* background-color: aquamarine; */
}
.Comment_input_part img {
width: 40px;
height: 40px;
border-radius: 100px;
margin-bottom: 20px;
margin-right: 10px;
}
.Comment_input_box {
width: 100%;
/* background-color: #787878; */
}
.Comment_input_box div {
float: right;
display: flex;
flex-direction: row;
}
.Comment_input_box input {
width: 100%;
height: 100%;
background-color: transparent;
border: none;
color: #666;
vertical-align: middle;
box-sizing: border-box;
}
.Comment_input_box input:focus {
outline: none;
}
@keyframes inputAnimation {
from {
box-sizing: inherit;
border-bottom: 1px solid #000;
transform: scale3d(0, 1, 0);
}
to {
box-sizing: inherit;
border-bottom: 2px solid #000;
transform: scale3d(1, 1, 1);
}
}
.Comment_line {
position: absolute;
top: 118.5%;
height: 1px;
width: 62%;
background-color: lightgrey;
transition: 0.3s;
}
.Comment_line_inAni {
position: absolute;
top: 118.5%;
height: 2px;
width: 62%;
background-color: black;
animation-name: inputAnimation;
animation-duration: 0.3s;
}
.Comment_buttons {
width: 72px;
height: 37px;
line-height: 37px;
margin: 7px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
color: #909090;
font-size: 13px;
}
#Comment_btn1 {
background-color: rgb(246, 246, 246);
color: #787878;
}
#Comment_btn2 {
background-color: rgb(225, 225, 225);
}
#Comment_btn2_active {
background-color: rgb(19, 51, 255);
color: white;
cursor: pointer;
}
.Comments_list {
width: 100%;
height: auto;
background-color: #787878;
}
.Comment_box {
width: 100%;
height: auto;
/* background-color: azure; */
margin: 30px 0;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}
.Comment_box:nth-child(1) {
margin-top: 10px;
}
.Comment_box img {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 20px;
}
.Comment_box > div > p {
font-size: 13px;
}
.Comment_info {
display: flex;
flex-direction: row;
align-items: center;
}
.Comment_info span {
font-size: 10px;
color: grey;
}
.Comment_info p {
font-size: 13px;
margin-right: 5px;
}
.Comment_good_bad {
display: flex;
align-items: center;
}
.Comment_good_bad span {
margin: 0 5px;
}
오늘은 이것을 만들었다. 댓글을 추가할 수 있게 하였고, 좋아요를 누를 수도 있게 만들었다. 좋아요와 싫어요 사이, '취소' 버튼과 '댓글' 버튼 사이의 디테일적인 기능이 많다. 내가 아무렇지도 않게 사용하던 기능이지만 이렇게 만들어보니 작은 기능도 코드는 길 수 있다는 것을 알게되었다.
import React, { useState } from 'react'
import styles from "../styles/CommentPart.module.css"
import { MdSort } from "react-icons/md"
import Comment from './Comment';
const CommentPart = () => {
const [firstFocus, setFirstFocus] = useState(false);
const [isFocus, setIsFocus] = useState(false);
const [isInput, setIsInput] = useState(false);
const [inputComment, setInputComment] = useState("");
const Me = {
imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu9Jzp859A5IesAX3WqVFY0ocYhG3_oFkYuLlNlH1KPJhA=s88-c-k-c0x00ffffff-no-rj-mo",
userName: "이태현",
comment: "",
good: 0,
date: "2022/09/21"
}
const [commentsSample, setCommentsSample] = useState([
{
imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu_D4JwoqK3mF9YcK1b6_sQ8lvdQ-luwDuqt3w=s88-c-k-c0x00ffffff-no-rj",
userName: "TaeHi",
comment: "좋아요!!",
good: 5,
date: "2022/05/05",
},
{
imgSrc: "https://lh3.googleusercontent.com/ogw/AOh-ky1uAXwkV8yiFs8KaB07AxluQj2nYO0d-RgdXzqr=s64-c-mo",
userName: "DoHi",
comment: "정말 좋아요!!",
good: 10,
date: "2022/05/10"
},
{
imgSrc: "https://yt3.ggpht.com/ytc/AMLnZu9kCKCBTyW3NUg0qiakoo_NPijLpMRjHyWyeUhO1-FAXAjbtINI76YzJSfQZ5JZCGnqUxU=s88-c-k-c0x00ffffff-no-rj",
userName: "구크르",
comment: "정말 재밌어요!!",
good: 2,
date: "2022/05/25"
},
])
return (
<div>
<div className={styles.Comment_box}>
<div className={styles.Comment_sort_box}>
<span className={styles.Comment_number}>댓글 {commentsSample.length}개</span>
<div>
<MdSort size={30}/>
<span className={styles.Comment_sort}>정렬 기준</span>
</div>
</div>
<div className={styles.Comment_input_part}>
<img src='https://yt3.ggpht.com/ytc/AMLnZu9Jzp859A5IesAX3WqVFY0ocYhG3_oFkYuLlNlH1KPJhA=s88-c-k-c0x00ffffff-no-rj-mo'/>
<div className={styles.Comment_input_box}>
{
isFocus === false ?
<div className={styles.Comment_line}></div>
: <div className={styles.Comment_line_inAni}></div>
}
<input
onFocus={() => {
setIsFocus(true)
setFirstFocus(true)
}}
onChange={(e) => {
if(e.target.value)setIsInput(true)
else setIsInput(false)
setInputComment(e.target.value)
}}
onBlur={() => setIsFocus(false)}
placeholder='댓글 추가...'
value={inputComment}
/>
<div>
{
firstFocus ?
<>
<div
onClick={() => {
setFirstFocus(false)
setInputComment("")
}}
id={styles.Comment_btn1}
className={styles.Comment_buttons}
>
취소
</div>
{
!isInput ?
<div id={styles.Comment_btn2} className={styles.Comment_buttons}>댓글</div>
: <div
id={styles.Comment_btn2_active}
className={styles.Comment_buttons}
onClick={() => {
Me.comment = inputComment
setCommentsSample([Me, ...commentsSample])
setInputComment("")
}}
>
댓글
</div>
}
</> :
<div className={styles.Comment_buttons}></div>
}
</div>
</div>
</div>
<div className='Comments_list'>
{
commentsSample.map((comment, idx) => {
return <Comment comment={comment} key={idx} />
})
}
</div>
</div>
</div>
)
}
export default CommentPart
import React, { useState } from 'react'
import { IoMdThumbsDown, IoMdThumbsUp } from 'react-icons/io'
import { BsThreeDotsVertical } from 'react-icons/bs'
import styles from "../styles/Comment.module.css"
const Comment = (props) => {
// const [list, setList] = useState(props.commentsSample)
const [isGood, setIsGood] = useState(false)
const [isBad, setIsBad] = useState(false)
const [menuOn, setMenuOn] = useState(false);
return (
<div onMouseOver={() => setMenuOn(true)} onMouseLeave={() => setMenuOn(false)} className={styles.Comment_box}>
{menuOn && <BsThreeDotsVertical className={styles.Comment_menu} />}
<img src={props.comment.imgSrc} />
<div>
<div className={styles.Comment_info}>
<p>{props.comment.userName}</p>
<span>{props.comment.date}</span>
</div>
<p>{props.comment.comment}</p>
<div className={styles.Comment_good_bad}>
<IoMdThumbsUp
onClick={() => {
setIsGood(!isGood)
setIsBad(false)
if(!isGood)props.comment.good++
else props.comment.good--
}}
color={isGood ? "rgb(0, 102, 255)" : "black"}
size={15}
/>
<span style={{width: "10px", textAlign: "center"}}>{props.comment.good}</span>
<IoMdThumbsDown
onClick={() => {
setIsBad(!isBad)
if(isGood) {
props.comment.good--
setIsGood(false)
}
}}
color={isBad ? "rgb(0, 102, 255)" : "black"}
size={15}
/>
</div>
</div>
</div>
)
}
export default Comment
.Comment_box {
width: 100%;
}
.Comment_sort_box {
width: 200px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.Comment_sort_box div {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.Comment_sort {
color: black !important;
font-size: 14px !important;
}
.Comment_number {
font-size: 16px !important;
color: black !important;
}
.Comment_input_part {
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
margin-top: 20px;
/* background-color: aquamarine; */
}
.Comment_input_part img {
width: 40px;
height: 40px;
border-radius: 100px;
margin-bottom: 20px;
margin-right: 10px;
}
.Comment_input_box {
width: 100%;
/* background-color: #787878; */
}
.Comment_input_box div {
float: right;
display: flex;
flex-direction: row;
}
.Comment_input_box input {
width: 100%;
height: 100%;
background-color: transparent;
border: none;
color: #666;
vertical-align: middle;
box-sizing: border-box;
}
.Comment_input_box input:focus {
outline: none;
}
@keyframes inputAnimation {
from {
box-sizing: inherit;
border-bottom: 1px solid #000;
transform: scale3d(0, 1, 0);
}
to {
box-sizing: inherit;
border-bottom: 2px solid #000;
transform: scale3d(1, 1, 1);
}
}
.Comment_line {
position: absolute;
top: 138%;
height: 1px;
width: 63%;
background-color: lightgrey;
transition: 0.3s;
}
.Comment_line_inAni {
position: absolute;
top: 138%;
height: 2px;
width: 63%;
background-color: black;
animation-name: inputAnimation;
animation-duration: 0.3s;
}
.Comment_buttons {
width: 72px;
height: 37px;
line-height: 37px;
margin: 7px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
color: #909090;
font-size: 13px;
}
#Comment_btn1 {
background-color: rgb(246, 246, 246);
color: #787878;
}
#Comment_btn2 {
background-color: rgb(225, 225, 225);
}
#Comment_btn2_active {
background-color: rgb(19, 51, 255);
color: white;
cursor: pointer;
}
.Comments_list {
width: 100%;
height: auto;
background-color: #787878;
}
.Comment_box {
width: 100%;
height: auto;
/* background-color: azure; */
margin-bottom: 10px;
margin-top: 0;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}
.Comment_menu {
position: absolute;;
right: 480px;
}
.Comment_box:nth-child(1) {
margin-top: 10px;
}
.Comment_box img {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 20px;
}
.Comment_box > div > p {
font-size: 13px;
margin-top: 5px;
}
.Comment_info {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 25px;
}
.Comment_info span {
font-size: 10px;
color: grey;
}
.Comment_info p {
font-size: 13px;
margin-right: 5px;
}
.Comment_good_bad {
margin-top: 10px;
display: flex;
align-items: center;
}
.Comment_good_bad span {
margin: 0 10px;
color: black;
}