-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feat] 차량 간단정보 구현 #56
Changes from all commits
ba57f0e
8a3a8f8
0eb9b88
001c8df
fb9f818
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"content": [ | ||
{ | ||
"src": "carimg1.png", | ||
"title": "독창적인 디자인", | ||
"desc": ["아이오닉 브랜드를 상징하는\n", "파라메트릭 픽셀 램프 디자인", "으로\n유니크한 이미지를 구현합니다."], | ||
"sub": "프로젝션 타입과 MFR 타입 중 선택 가능" | ||
}, | ||
{ | ||
"src": "carimg2.png", | ||
"title": "전용 전기차 플랫폼(E-GMP)", | ||
"desc": ["", "새로워진 전기차 플랫폼 E-GMP", "는\n알루미늄 압출재를 이용해\n구조적 안정성을 높였습니다."], | ||
"sub": "연출된 이미지로 실제 작동 사양과 다를 수 있음" | ||
}, | ||
{ | ||
"src": "carimg3.png", | ||
"title": "인터랙티브 픽셀 라이트", | ||
"desc": ["", "신규 디자인의 스티어링 휠", "로\n아이오닉5만의 차별화된\n주행 경험을 제공합니다."], | ||
"sub": "시동 / 충전 중 / 후진 중 표시 가능" | ||
}, | ||
{ | ||
"src": "carimg4.png", | ||
"title": "증강현실 내비게이션", | ||
"desc": ["주행에 필요한 각종 정보들을\n", "증강현실 기술", "을 통해\n직관적으로 제공합니다."], | ||
"sub": "인포테인먼트 시스템 화면 이미지는 업데이트에 따라 변동 가능" | ||
}, | ||
{ | ||
"src": "carimg5.png", | ||
"title": "디지털 사이드 미러", | ||
"desc": ["보다 슬림해진 ", "디지털 사이드 미러", "는\n카메라와 ", "OLED 모니터", "를 통해\n선명한 후방 시야를 제공합니다."], | ||
"sub": "모니터는 내부 운전석에 위치" | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { useEffect, useRef, useState } from "react"; | ||
import style from "./contentSection.module.css"; | ||
|
||
export default function ContentSection({ content }) { | ||
const contentRef = useRef(null); | ||
const [isVisible, setIsVisible] = useState(false); | ||
const [isHighlighted, setIsHighlighted] = useState(false); | ||
|
||
useEffect(() => { | ||
const contentDOM = contentRef.current; | ||
const observer = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => { | ||
if (entry.isIntersecting) { | ||
setIsVisible(true); | ||
observer.unobserve(entry.target); // 애니메이션 실행 후 옵저버 중지 | ||
} | ||
}); | ||
}, | ||
{ | ||
threshold: 0.8, // 80%가 보일 때 실행 | ||
}, | ||
); | ||
|
||
if (contentDOM) { | ||
observer.observe(contentDOM); | ||
} | ||
|
||
// 클린업 함수 | ||
return () => { | ||
if (contentDOM) { | ||
observer.unobserve(contentDOM); | ||
} | ||
}; | ||
}, []); | ||
|
||
const highlightDynamicStyle = { | ||
"--progress": isHighlighted ? "100%" : "0%", | ||
}; | ||
|
||
return ( | ||
<div | ||
ref={contentRef} | ||
onAnimationEnd={() => setIsHighlighted(true)} | ||
className={`${isVisible ? style.fadeIn : "opacity-0"} z-0 flex flex-col font-bold`} | ||
> | ||
<img src={content.src} className="w-full" /> | ||
|
||
<span className="pt-10 text-body-l text-neutral-800"> | ||
{content.title} | ||
</span> | ||
|
||
<div className="pt-3 flex justify-between items-end"> | ||
<div> | ||
{content.desc.map((str, index) => ( | ||
<span | ||
key={index} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 사실 key에 index를 쓰는 건 썩 좋은 습관은 아닙니다. 왜냐하면 index를 key로 쓴 상태에서 배열이 바뀌거나 하면 리액트에서 뭐가 제대로 삭제 안 되거나 하는 버그가 생길 수 있어요. 자세한 건 다음 링크를 참조하세요. |
||
style={highlightDynamicStyle} | ||
className={`${index % 2 ? style.highlightAnim : "text-neutral-800"} text-title-m whitespace-pre-wrap`} | ||
> | ||
{str} | ||
</span> | ||
))} | ||
</div> | ||
|
||
<span className="absoulte top-0 right-0 text-body-s text-neutral-300"> | ||
{content.sub} | ||
</span> | ||
</div> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
.fadeIn { | ||
animation: fade-in 0.4s ease-out forwards; | ||
} | ||
|
||
@keyframes fade-in { | ||
from { | ||
opacity: 0; | ||
} | ||
to { | ||
opacity: 1; | ||
} | ||
} | ||
|
||
.highlightAnim { | ||
display: inline-block; | ||
position: relative; | ||
} | ||
|
||
.highlightAnim::before { | ||
content: ""; | ||
position: absolute; | ||
display: block; | ||
width: 100%; | ||
height: 1.4em; | ||
background: linear-gradient(90deg, #3ED7BE, #069AF8); | ||
opacity: 0.3; | ||
z-index: -1; | ||
clip-path: polygon(0 0, 0 100%, var(--progress, 0) 100%, var(--progress, 0) 0); | ||
transition: clip-path 0.4s; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import JSONData from "./contentList.json"; | ||
import ContentSection from "./contentSection"; | ||
|
||
export default function SimpleInformation() { | ||
const contentList = JSONData.content; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good |
||
|
||
return ( | ||
<div className="h-[4700px] flex justify-center "> | ||
<div className="w-[1200px] flex flex-col gap-[160px]"> | ||
<div className="flex flex-col text-black font-bold pt-[240px]"> | ||
<span className="text-title-m">내가 선택한 단 하나의 전기차</span> | ||
|
||
<span className="text-head-l">The new IONIQ 5</span> | ||
</div> | ||
|
||
{contentList.map((content, index) => ( | ||
<ContentSection key={index} content={content} /> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
반복되는 부분을 별도의 변수로 떼신 것 아주 좋습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 안해주면 warning을 발생시킵니다. useRef로 선언한 개체는 current 속성을 통해 접근해야 하는데 여기서 current는 가변성이고 cleanup 함수가 실행되는 순간에는 화면이 업데이트 되고 난 뒤기 때문에 current의 값은 null로 세팅된다는 잠재적 문제가 있습니다.
https://velog.io/@cjhlsb/kencland