Skip to content

Commit

Permalink
feat: preload images when near view
Browse files Browse the repository at this point in the history
Fixes #8
  • Loading branch information
sehnryr committed Nov 10, 2024
1 parent bcb51e9 commit 5598eac
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 15 deletions.
1 change: 0 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
"react": "npm:[email protected]",
"react-dom": "npm:[email protected]",
"react-icons": "npm:[email protected]",
"react-intersection-observer": "npm:[email protected]",
"react-router-dom": "npm:[email protected]",
"tailwindcss": "npm:[email protected]",
"vite": "npm:[email protected]",
Expand Down
10 changes: 0 additions & 10 deletions deno.lock

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

11 changes: 7 additions & 4 deletions src/pages/Browse/ExtensionBrowse.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { forwardRef, useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { useInView } from "react-intersection-observer";

import { Extension } from "../../types/extension.ts";
import { Manga } from "../../types/manga.ts";
import { getMangaList } from "../../services/extensions.service.ts";
import { useStore } from "../../services/store.service.ts";
import { downloadImage } from "../../services/tauri.service.ts";
import useInfiniteScroll from "../../utils/infinite-scroll-hook.ts";
import useLazyImage from "../../utils/lazy-image-hook.ts";

export default function ExtensionBrowse() {
const { extensionId } = useParams();
Expand Down Expand Up @@ -116,10 +116,11 @@ const MangaItem = (
const [src, setSrc] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(true);

const { ref, inView } = useInView({
const { containerRef, inView } = useLazyImage({
onChange: (inView) => {
if (inView) setLoading(true);
},
offset: "200vh",
});

useEffect(() => {
Expand All @@ -133,11 +134,13 @@ const MangaItem = (
return (
<>
<Link
ref={ref}
to={{ pathname: `/browse/${extensionId}/${manga.id}` }}
state={manga}
>
<div className="w-full aspect-[2/3] skeleton rounded-md">
<div
ref={containerRef}
className="w-full aspect-[2/3] skeleton rounded-md relative"
>
{inView && (
<img
src={src}
Expand Down
53 changes: 53 additions & 0 deletions src/utils/lazy-image-hook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { LegacyRef, useEffect, useRef, useState } from "react";

export default function useLazyImage({
onChange = (_inView: boolean) => {},
offset = "0px",
}: {
onChange?: (inView: boolean) => void;
offset?: string;
}) {
const [inView, setInView] = useState(false);
const observerRef = useRef<IntersectionObserver>();
const targetRef = useRef((() => {
const target = document.createElement("div");
target.setAttribute("image-lazy-loading-detector", "");
target.style.position = "absolute";
target.style.bottom = "0px";
target.style.width = "0px";
target.style.height = `calc(100% + ${offset} * 2)`;
target.style.top = `calc(${offset} * -1)`;
return target;
})());

const containerRef: LegacyRef<HTMLElement> = (container: HTMLElement) => {
if (container) {
container.append(targetRef.current);
container.style.position = "relative";
}
};

useEffect(() => {
onChange(inView);
}, [inView]);

useEffect(() => {
const observer = observerRef.current;

if (observer) {
observer.disconnect();
}

observerRef.current = new IntersectionObserver(([entry]) => {
setInView(entry.isIntersecting);
});

observerRef.current.observe(targetRef.current);

return () => {
observerRef.current?.disconnect();
};
}, []);

return { containerRef, inView };
}

0 comments on commit 5598eac

Please sign in to comment.