شاشة التحميل
في تجارب ثلاثية الأبعاد، من الشائع وجود نماذج معقدة وملمس كبير. يمكن أن يؤدي هذا إلى فترات تحميل طويلة. لتجنب الحصول على شاشة فارغة والانطباعات الأولى السيئة، يمكننا إضافة شاشة تحميل.
أعتقد شخصياً أنه عندما يكون ذلك ممكنا، يجب تجنب شاشة التحميل (إلا إذا كان اختيار تصميم). يجب أن تؤخذ تحسين النماذج والقوام في الاعتبار أولاً.
ولكن في بعض الأحيان ليس لديك خيار، وحتى النماذج المحسنة يمكن أن تبقى كبيرة أو لديك العديد منها. في هذه الحالة، يمكن أن تكون شاشة التحميل حلاً جيدًا.
Suspense
Suspense هو مكوّن تغليف في React يسمح لك بعرض مكوّن مؤقت أثناء انتظار تحميل البيانات.
تُشير تلك الـبيانات في التطبيقات التقليدية عادة إلى جلب البيانات أو تقسيم الكود. ولكن في React Three Fiber، يمكننا استخدامه للانتظار حتى تحميل النماذج والقوام.
useLoader
هوك يستخدم الوعود لـتحميل النماذج والقوام. لذا يمكننا استخدام Suspense
للانتظار حتى يتم حلها.
في starter pack لقد أضفت 4 نماذج في public/models/
كل منها بحجم +3MB. (وهو الكثير لتطبيق ويب حيث أنها تحتوي على نفس الرسوم المتحركة ويمكن/ينبغي تحسينها) سيمكننا هذا من رؤية كيفية عمل Suspense
.
عندما أقوم بإعادة التحميل، لدينا شاشة فارغة طويلة قبل أن يتم تحميل النماذج. هذا ليس تجربة مستخدم جيدة، يمكن للمستخدمين أن يعتقدوا أن التطبيق معطل ويغادرون.
إذا كنت تعمل محليًا أو لديك النماذج مخبأة في متصفحك، قد لا ترى الشاشة الفارغة.
لإجبار إعادة تحميل الموارد، يمكنك تعطيل التخزين المؤقت في علامة تبويب الشبكة في أدوات المطور الخاصة بالمتصفح.
يمكنك أيضًا إبطاء الشبكة مع خيار Throttling.
الآن يجب أن نرى كلنا الشاشة الفارغة، لنضيف مكوّن Suspense
مع خاصية 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;
مكوّن fallback
الخاص بنا هو مكعب بسيط، ليس أفضل شاشة تحميل، ولكنه كافٍ لفهم كيفية عمل Suspense
.
عندما نعيد التحميل، يمكننا أن نرى أن المكعب يُعرض أثناء تحميل النماذج. عندما يتم تحميلها، يتم استبدال المكعب بمكوّن Experience
.
Preload
مشكلة شائعة يمكن أن تواجهها هي أن الموارد الخاصة بك ليست مطلوبة فورًا. على سبيل المثال، قد يكون لديك نموذج يتم تحميله فقط عندما ينقر المستخدم على زر. لذا، لن يتم أخذه بعين الاعتبار من قبل Suspense
.
يمكن أن يؤدي ذلك إلى عرض مكون fallback
مرة أخرى عند الحاجة إلى المورد. لتجنب ذلك، يمكننا استخدام وظيفة preload
من Drei loaders المختلفة.
دعونا نؤخر عرض الـ King
لثانية واحدة:
export const Experience = () => { const [kingVisible, setKingVisible] = useState(false); useEffect(() => { setTimeout(() => { setKingVisible(true); }, 1000); }, []); return ( <> {/* ... */} {kingVisible && <King position-x={-3} rotation-y={-Math.PI / 4} />} {/* ... */} </> ); };
عند إعادة التحميل، يمكننا أن نرى أن الـ King
لم يتم عرضه فورًا. لذا، Suspense
لن ينتظر تحميله مما يؤدي إلى عرض مكون fallback
مرة أخرى عندما يتطلب الـ King
. (حتى وإن كان لفترة قصيرة جدًا كما أنني أعمل محليًا)
يمكننا أيضًا أن نرى في علامة تبويب Network أن الـ King
يتم تحميله فقط بعد مرور delay.
لمنع ذلك، يمكننا استخدام وظيفة preload
من الـ useGLTF
hook. في نهاية ملف King.jsx
، دعونا نضيف:
useGLTF.preload("/models/King.gltf");
انتبه لاستخدام المسار نفسه بالضبط كما هو مستخدم في الـ useGLTF
hook.
الآن النموذج يتم تحميله من البداية، وSuspense
سينتظر تحميله.
هذه غالبًا ما تكون حلًا جيدًا، ولكن استنادًا إلى المشروع، قد تقرر تحميل الموارد عند الطلب.
على سبيل المثال، إذا كان موردًا كبيرًا يصل إليه 5% فقط من المستخدمين، فقد يكون من الأفضل تحميله فقط عند الحاجة.
ملاحظة: بشكل افتراضي، gltfjsx
تلقائيًا سيضيف كود preload في نهاية الملف.
يمكنك إزالة التأخير على الـ King
ومكون CubeLoader
fallback كان ذلك لأغراض التوضيح.
useProgress
الآن نفهم كيف يعمل Suspense
مع React Three Fiber، دعونا نكتشف hook useProgress من Drei للحصول على التقدم في تحميل الموارد لدينا.
يُعيد هذا hook كائنًا يحتوي على الخصائص التالية:
- active:
boolean
-true
إذا كان المحمل نشطًا - progress:
number
- النسبة المئوية للتحميل - errors:
any[]
- مصفوفة من الأخطاء التي حدثت أثناء التحميل - item:
any
- العنصر الحالي الذي يتم تحميله - loaded:
number
- عدد العناصر المحملة - total:
number
- العدد الكلي للعناصر لتحميلها
لنقم بإنشاء مكون LoadingScreen
باستخدام 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;
في الوقت الحالي سنبقيه بسيطًا جدًا، سنعرض فقط التقدم في وسم p
.
يمكننا أن نرى أن progress
يتم تحديثه أثناء التحميل ليصل إلى 100٪، لكن يحتاج إلى بعض التنسيق...
كما فعلت في درس HTML، سأكتب CSS عادي لكن استخدم حل التنسيق المفضل لديك. للحفاظ على الهيكلة سأتبع منهجية BEM (Block Element Modifier).
في index.css
، دعونا نجعل شاشة التحميل كاملة الشاشة، نضيف خلفية متدرجة رمادي/أبيض، ونقوم بـ مركزة المحتوى:
.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%); }
لجعلها تبدو أكثر جاذبية، دعونا نضيف خط Inter من Google Fonts:
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;900&display=swap"); /* ... */ body { margin: 0; font-family: "Inter", sans-serif; } /* ... */
واجعل title
أكبر:
/* ... */ .loading-screen__title { font-size: 4rem; font-weight: 900; text-transform: uppercase; color: #1a202c; margin: 0; }
الآن شاشتنا الكاملة مغطاة بشاشة التحميل الخاصة بنا
بدلاً من عرض progress
كنص، دعونا نعرضه كـ شريط تقدم. للقيام بذلك، سنستخدم div
مع background-color
الذي سيتم تحريكه بقيمة 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> // ...
نقوم بتعيين width
لـ progress__bar
لقيمة 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; }
شريط progress__bar
يتم تحريكه عندما يتم تحميل الموارد
عندما يكون progress
عند 100%، نريد إخفاء loading-screen
وعرض الـ Canvas
. للقيام بذلك، سنستخدم الخاصية active
من 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> ); };
نضيف فئة loading-screen--hidden
عندما تكون الخاصية active
تساوي 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; } }
نشغل الرسوم المتحركة fade-out
عندما تتم إضافة الفئة loading-screen--hidden
. نضيف أيضًا visibility: hidden
عند اكتمال الرسوم المتحركة لمنع المستخدم من التفاعل مع loading-screen
عندما تكون مخفية وتتداخل مع أحداث Canvas
.
Et voilà! لدينا شاشة تحميل تعرض التقدم بينما الموارد تُحمل وتتلاشى لعرض المشهد ثلاثي الأبعاد عندما يكون كل شيء محمل.
الخاتمة
في هذا الدرس، غطينا الموضوع المهم المتعلق بـ تحميل الموارد. لديك أداتان إضافيتان في حزام أدواتك لبناء تجارب ثلاثية الأبعاد مذهلة.
قرار استخدام شاشة تحميل، أو حل احتياطي، أو كليهما، أو لا شيء، يعود لك. يعتمد الأمر على السياق وتجربة المستخدم التي تريد تقديمها.
يوفر مكتبة Drei أيضًا Loader component يمكنك استخدامه لعرض شاشة تحميل. لم أغطيها في هذا الدرس لأنه أعتقد أنها غير قابلة للتخصيص بما يكفي لتطبيق production.
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
One-time payment. Lifetime updates included.