Skip to content

Commit

Permalink
Merge pull request #80 from KNU-Design-Web/feature#79
Browse files Browse the repository at this point in the history
Feature#79: 프로젝트 이미지 및 비디오 LazyLoad 적용
  • Loading branch information
toothlessdev authored Jan 9, 2025
2 parents 5e2bdae + bc1ce48 commit bfd7683
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 4 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-ga4": "^2.1.0",
"react-intersection-observer": "^9.14.1",
"react-lazyload": "^3.2.1",
"react-router-dom": "^6.26.2",
"react-transition-group": "^4.4.5",
Expand Down
18 changes: 18 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion src/components/ProjectPage/ProjectCard/ProjectCard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { useCallback } from "react";
import { useNavigate } from "react-router-dom";

import { preloadImages } from "@/utils/preload";

import * as CardStyles from "./ProjectCard.style";
import { data } from "@/apps/data";
import { Text } from "@/common/components/Text/Text";
import { useProjectEvent } from "@/events/projectEvents";

Expand All @@ -16,9 +20,17 @@ export const ProjectCard = (props: ProjectCardProps) => {

const { dispatchMouseOverEvent, dispatchMouseClickEvent } = useProjectEvent(props.id);

const handleMouseOver = useCallback(() => {
const headerImage = data[props.id - 1].project.contents[0];
const firstProjectImage = data[props.id - 1].project.contents[1];

preloadImages([headerImage, firstProjectImage]);
dispatchMouseOverEvent();
}, [dispatchMouseOverEvent, props.id]);

return (
<CardStyles.Wrapper
onMouseOver={dispatchMouseOverEvent}
onMouseOver={handleMouseOver}
onClick={() => {
navigate(`?section=project&id=${props.id - 1}`);
dispatchMouseClickEvent();
Expand Down
11 changes: 11 additions & 0 deletions src/components/ProjectPage/ProjectImage/ProjectImage.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import styled from "@emotion/styled";

export const ImageWrapper = styled.div`
width: 100%;
`;

export const Image = styled.img`
display: block;
width: 100%;
margin: 0px auto;
`;
20 changes: 20 additions & 0 deletions src/components/ProjectPage/ProjectImage/ProjectImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useInView } from "react-intersection-observer";

import * as ProjectImageStyles from "./ProjectImage.style";

export interface ProjectImageProps extends React.ComponentProps<"img"> {
offset: number;
}

export const ProjectImage = ({ src, offset, ...rest }: ProjectImageProps) => {
const { ref, inView } = useInView({
triggerOnce: true,
rootMargin: `${offset}px`,
});

return (
<ProjectImageStyles.ImageWrapper ref={ref} style={{ minHeight: `${offset}px` }}>
{inView && <ProjectImageStyles.Image src={src} {...rest} />}
</ProjectImageStyles.ImageWrapper>
);
};
13 changes: 13 additions & 0 deletions src/components/ProjectPage/ProjectVideo/ProjectVideo.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from "@emotion/styled";

export const VideoWrapper = styled.div`
width: 100%;
`;

export const Video = styled.video`
display: block;
width: 100%;
margin: 0px auto;
border: 0;
padding: 0;
`;
20 changes: 20 additions & 0 deletions src/components/ProjectPage/ProjectVideo/ProjectVideo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useInView } from "react-intersection-observer";

import * as ProjectVideoStyles from "./ProjectVideo.style";

export interface ProjectVideoProps extends React.ComponentProps<"video"> {
offset: number;
}

export const ProjectVideo = ({ src, offset, ...rest }: ProjectVideoProps) => {
const { ref, inView } = useInView({
triggerOnce: true,
rootMargin: `${offset}px`,
});

return (
<ProjectVideoStyles.VideoWrapper ref={ref} style={{ minHeight: `${offset}px` }}>
{inView && <ProjectVideoStyles.Video src={src} autoPlay loop muted playsInline {...rest} />}
</ProjectVideoStyles.VideoWrapper>
);
};
1 change: 1 addition & 0 deletions src/pages/ProjectPage/ProjectDetailPage.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const Body = styled.div`
display: block;
width: 100%;
max-width: 980px;
min-height: 100vh;
margin: 0px auto;
margin-top: 60px;
Expand Down
8 changes: 5 additions & 3 deletions src/pages/ProjectPage/ProjectDetailPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { memo, useLayoutEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";

import { ProjectImage } from "@/components/ProjectPage/ProjectImage/ProjectImage";
import { ProjectVideo } from "@/components/ProjectPage/ProjectVideo/ProjectVideo";

import { useProjectId } from "@/hooks/ProjectPage/useProjectId";

import { LeftArrow } from "@/assets/icons/LeftArrow";
Expand Down Expand Up @@ -91,9 +94,8 @@ export default memo(function ProjectDetailPage() {
</ProjectDetailStyles.ContentWrapper>

{data[projectId].project.contents.map((src) => {
if (src.match(/\.(webp|gif)$/)) return <ProjectDetailStyles.Image src={src} />;
else if (src.match(/\.(webm|mp4)$/))
return <ProjectDetailStyles.Video src={src} autoPlay loop muted playsInline />;
if (src.match(/\.(webp|gif)$/)) return <ProjectImage src={src} offset={400} />;
else if (src.match(/\.(webm|mp4)$/)) return <ProjectVideo src={src} offset={800} />;
})}
</ProjectDetailStyles.Body>
</ProjectDetailStyles.PageWrapper>
Expand Down

0 comments on commit bfd7683

Please sign in to comment.