Layar Muat

Starter pack

Dalam pengalaman 3D, umumnya terdapat model kompleks dan tekstur besar. Hal ini bisa menyebabkan waktu muat yang lama. Untuk menghindari layar kosong dan kesan pertama yang buruk, kita bisa menambahkan layar muat.

Saya pribadi berpendapat bahwa jika memungkinkan, layar muat sebaiknya dihindari (Kecuali jika itu merupakan pilihan desain). Optimisasi model dan tekstur harus dipertimbangkan terlebih dahulu.

Tetapi terkadang Anda tidak punya pilihan, dan bahkan model yang sudah dioptimalkan dapat tetap besar atau Anda memiliki banyak di antaranya. Dalam hal ini, layar muat bisa menjadi solusi yang baik.

Suspense

Suspense adalah komponen pembungkus React yang memungkinkan Anda untuk merender komponen cadangan sambil menunggu data dimuat.

Data tersebut dalam aplikasi tradisional biasanya mengacu pada pengambilan data atau pemisahan kode. Tetapi dalam React Three Fiber, kita bisa menggunakannya untuk menunggu model dan tekstur dimuat.

Hook useLoader menggunakan janji (promises) untuk memuat model dan tekstur. Jadi kita bisa menggunakan Suspense untuk menunggu mereka selesai.

Dalam starter pack saya telah menambahkan 4 model dalam public/models/ masing-masing +3MB. (Ini banyak untuk sebuah aplikasi web karena semuanya mengandung animasi yang sama dan bisa/seharusnya dioptimalkan). Itu akan memungkinkan kita melihat cara kerja Suspense.

Ketika saya memuat ulang, kita memiliki layar kosong yang lama sebelum model dimuat. Ini adalah pengalaman pengguna yang tidak baik, pengguna bisa berpikir bahwa aplikasi rusak dan meninggalkannya.

Jika Anda bekerja secara lokal atau memiliki model yang di-cache di peramban Anda, Anda mungkin tidak melihat layar kosong.

Untuk memaksa memuat ulang sumber daya, Anda bisa menonaktifkan cache di tab Network pada alat pengembang peramban Anda.

Anda juga bisa memperlambat jaringan dengan opsi Throttling.

Nonaktifkan cache dan Jaringan Throttling

Sekarang kita semua harus melihat layar kosong, mari kita tambahkan komponen Suspense dengan properti fallback:

import { Canvas } from "@react-three/fiber";
import { Experience } from "./components/Experience";
import { Suspense } from "react";

const CubeLoader = () => {
  return (
    <mesh>
      <boxGeometry />
      <meshNormalMaterial />
    </mesh>
  );
};

function App() {
  return (
    <>
      <Canvas camera={{ position: [-4, 4, 12], fov: 30 }}>
        <Suspense fallback={<CubeLoader />}>
          <group position-y={-1}>
            <Experience />
          </group>
        </Suspense>
      </Canvas>
    </>
  );
}

export default App;

Komponen fallback kami adalah kubus sederhana, bukan layar muat yang paling menarik, tetapi sudah cukup untuk memahami cara kerja Suspense.

Ketika kita memuat ulang, kita bisa melihat bahwa kubus ditampilkan sementara model sedang dimuat. Ketika mereka sudah dimuat, kubus akan diganti dengan komponen Experience.

Preload

Masalah umum yang mungkin Anda hadapi adalah sumber daya Anda tidak dibutuhkan segera. Misalnya, Anda dapat memiliki model yang dimuat hanya ketika pengguna mengklik sebuah tombol. Jadi model tersebut tidak akan diperhitungkan oleh Suspense.

Hal itu dapat mengakibatkan komponen fallback ditampilkan lagi saat sumber daya tersebut dibutuhkan. Untuk menghindarinya, kita dapat menggunakan fungsi preload dari berbagai Drei loaders.

Mari kita tunda tampilan King selama satu detik:

export const Experience = () => {
  const [kingVisible, setKingVisible] = useState(false);

  useEffect(() => {
    setTimeout(() => {
      setKingVisible(true);
    }, 1000);
  }, []);

  return (
    <>
      {/* ... */}
      {kingVisible && <King position-x={-3} rotation-y={-Math.PI / 4} />}
      {/* ... */}
    </>
  );
};

Ketika kita memuat ulang, kita dapat melihat bahwa King tidak ditampilkan segera. Jadi Suspense tidak akan menunggu untuk dimuat, yang menyebabkan komponen fallback ditampilkan lagi saat King dibutuhkan. (Meskipun hanya untuk waktu yang sangat singkat karena saya bekerja secara lokal)

Kita juga dapat melihat di tab Network bahwa King hanya dimuat setelah delay.

Untuk mencegah hal itu, kita dapat menggunakan fungsi preload dari hook useGLTF. Di akhir file King.jsx, tambahkan:

useGLTF.preload("/models/King.gltf");

Berhati-hatilah menggunakan path yang persis sama dengan yang digunakan di hook useGLTF.

Sekarang model sudah dimuat sejak awal, dan Suspense akan menunggu hingga model tersebut dimuat.

Ini seringkali menjadi solusi yang baik, tetapi bergantung pada proyek, Anda mungkin memutuskan untuk memuat sumber daya sesuai kebutuhan.

Misalnya, jika itu adalah sumber daya besar yang hanya akan digunakan oleh 5% pengguna, mungkin lebih baik memuatnya hanya ketika dibutuhkan.

Catatan: Secara default gltfjsx akan secara otomatis menambahkan kode preload di akhir file.

Anda dapat menghapus delay pada King dan CubeLoader fallback, itu hanya untuk contoh.

useProgress

Sekarang kita memahami bagaimana Suspense bekerja dengan React Three Fiber, mari kita jelajahi hook useProgress dari Drei untuk mendapatkan progres pemuatan sumber daya kita.

Hook ini mengembalikan objek dengan properti berikut:

  • active: boolean - true jika loader aktif
  • progress: number - persentase pemuatan
  • errors: any[] - array kesalahan yang terjadi selama pemuatan
  • item: any - item saat ini yang sedang dimuat
  • loaded: number - jumlah item yang telah dimuat
  • total: number - total jumlah item yang akan dimuat

Mari kita buat komponen LoadingScreen dengan useProgress:

// ...
import { useProgress } from "@react-three/drei";

const LoadingScreen = () => {
  const { progress } = useProgress();

  return (
    <div className="loading-screen">
      <div className="loading-screen__container">
        <h1 className="loading-screen__title">3D Web Agency</h1>
        <p>Loading... ({parseInt(progress)}%)</p>
      </div>
    </div>
  );
};

function App() {
  return (
    <>
      <LoadingScreen />
      <Canvas camera={{ position: [-4, 4, 12], fov: 30 }}>{/* ... */}</Canvas>
    </>
  );
}

export default App;

Untuk saat ini kita akan menjaganya sangat sederhana, kita hanya akan menampilkan progres dalam tag p.

Loading screen html

Kita bisa melihat bahwa progress diperbarui selama pemuatan hingga 100%, tetapi memerlukan beberapa penataan...

Seperti yang saya lakukan dalam pelajaran HTML, saya akan menulis CSS biasa tetapi gunakan solusi penataan favorit Anda. Agar terstruktur, saya akan mengikuti metodologi BEM (Block Element Modifier).

Dalam index.css, mari kita buat layar pemuatan kita menjadi layar penuh, tambahkan latar belakang gradasi abu-abu/putih, dan pusatkan kontennya:

.loading-screen {
  position: fixed;
  top: 0;
  left: 0;
  padding: 4rem;
  width: 100vw;
  height: 100vh;
  z-index: 1;
  display: grid;
  place-items: center;
  background-color: #b8c6db;
  background-image: linear-gradient(0deg, #b8c6db 0%, #f5f7fa 74%);
}

Untuk membuatnya lebih menarik secara visual, mari tambahkan font Inter dari Google Fonts:

@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;900&display=swap");

/* ... */

body {
  margin: 0;
  font-family: "Inter", sans-serif;
}

/* ... */

Dan buat title lebih besar:

/* ... */

.loading-screen__title {
  font-size: 4rem;
  font-weight: 900;
  text-transform: uppercase;
  color: #1a202c;
  margin: 0;
}

Loading screen css

Sekarang layar penuh kita tertutupi oleh layar pemuatan kita

Daripada menampilkan progress sebagai teks, mari kita tampilkan sebagai progress bar. Untuk melakukannya, kita akan menggunakan div dengan background-color yang akan di-animasi dengan nilai progress.

// ...
<div className="loading-screen__container">
  <h1 className="loading-screen__title">3D Web Agency</h1>
  <div className="progress__container">
    <div className="progress__bar" style={{ width: `${progress}%` }}></div>
  </div>
</div>
// ...

Kita mengatur width dari progress__bar sesuai dengan nilai progress.

.progress__container {
  width: 100%;
  height: 1rem;
  background-color: rgb(102 106 113 / 42%);
  position: relative;
  overflow: hidden;
  border-radius: 4px;
}

.progress__bar {
  width: 0;
  height: 100%;
  background-color: #1a202c;
  transition: width 0.5s ease-in-out;
}

Loading screen progress bar

progress__bar dianimasikan saat sumber daya dimuat

Ketika progress mencapai 100%, kita ingin menyembunyikan loading-screen dan menampilkan Canvas. Untuk melakukannya, kita akan menggunakan properti active dari useProgress:

const LoadingScreen = () => {
  const { progress, active } = useProgress();

  return (
    <div className={`loading-screen ${active ? "" : "loading-screen--hidden"}`}>
      <div className="loading-screen__container">
        <h1 className="loading-screen__title">3D Web Agency</h1>
        <div className="progress__container">
          <div
            className="progress__bar"
            style={{ width: `${progress}%` }}
          ></div>
        </div>
      </div>
    </div>
  );
};

Kita menambahkan kelas loading-screen--hidden ketika properti active adalah false

.loading-screen {
  /* ... */
  opacity: 1;
}

.loading-screen--hidden {
  animation: fade-out 0.5s ease-in-out forwards 1s;
}

@keyframes fade-out {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    visibility: hidden;
  }
}

Kita memainkan animasi fade-out saat kelas loading-screen--hidden ditambahkan. Kita juga menambahkan visibility: hidden pada penyelesaian animasi untuk mencegah pengguna berinteraksi dengan loading-screen ketika sudah tersembunyi dan mengganggu event Canvas.

Et voilà! Kita memiliki layar pemuatan yang menampilkan perkembangan saat sumber daya dimuat dan memudar untuk menampilkan adegan 3D ketika semuanya sudah dimuat.

Kesimpulan

Dalam pelajaran ini, kita membahas topik penting tentang pemuatan sumber daya. Anda memiliki dua alat lagi untuk membangun pengalaman 3D yang menakjubkan.

Keputusan untuk menggunakan layar pemuatan, sebuah fallback, keduanya, atau tidak sama sekali, terserah Anda. Itu tergantung pada konteks dan pengalaman pengguna yang ingin Anda berikan.

Drei library juga menyediakan Loader component yang dapat Anda gunakan untuk menampilkan layar pemuatan. Saya tidak membahasnya dalam pelajaran ini karena saya pikir itu tidak cukup dapat disesuaikan untuk aplikasi produksi.

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.