From 707eb0f5c15f499ed3d5d2f57b077ee15ed21bd0 Mon Sep 17 00:00:00 2001 From: tuyenio Date: Thu, 25 Jun 2026 08:32:35 +0700 Subject: [PATCH] =?UTF-8?q?docs:=20spec=20t=E1=BB=91i=20=C6=B0u=20hi?= =?UTF-8?q?=E1=BB=87u=20n=C4=83ng=20web=20(ph=C3=A2n=20t=C3=ADch=20nguy?= =?UTF-8?q?=C3=AAn=20nh=C3=A2n=20+=20ph=C6=B0=C6=A1ng=20=C3=A1n=20theo=20g?= =?UTF-8?q?iai=20=C4=91o=E1=BA=A1n)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../2026-06-25-toi-uu-hieu-nang-design.md | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-25-toi-uu-hieu-nang-design.md diff --git a/docs/superpowers/specs/2026-06-25-toi-uu-hieu-nang-design.md b/docs/superpowers/specs/2026-06-25-toi-uu-hieu-nang-design.md new file mode 100644 index 0000000..14b11c2 --- /dev/null +++ b/docs/superpowers/specs/2026-06-25-toi-uu-hieu-nang-design.md @@ -0,0 +1,71 @@ +# Thiết kế: Tối ưu hiệu năng web phy-vr360 + +- **Ngày:** 2026-06-25 +- **Trạng thái:** Đã duyệt thiết kế, chờ lập kế hoạch triển khai +- **Chiến lược:** Hướng A — tối ưu theo giai đoạn, ưu tiên việc rẻ–lợi-cao +- **Ràng buộc người dùng:** Giữ thẩm mỹ tổng thể nhưng bỏ lớp thừa; được phép nén cả ảnh và model 3D. + +--- + +## 1. Vấn đề + +Web khi chạy làm laptop nóng/quạt rú, cuộn giật, trải nghiệm không mượt. Cảm nhận là **toàn bộ site đều nặng**, không khu trú một chỗ. + +## 2. Nguyên nhân gốc (đã phân tích) + +### Nghiêm trọng +1. **Trang chủ chồng 3 ngữ cảnh WebGL chạy liên tục** — `hero-shader-background.tsx`: + - 2× `MeshGradient` (shader GPU paper-design) animation vô hạn 60fps. + - 1× `HeroPanoramaBackground` (`hero-panorama-background.tsx`): WebGLRenderer three.js, quả cầu xoay, `requestAnimationFrame` render **vô điều kiện**, tải ảnh panorama vài MB chỉ để làm nền mờ 58%. + - `mix-blend-mode: soft-light` + filter SVG `feTurbulence`. + - Không có `IntersectionObserver`/`visibilitychange` → chạy cả khi cuộn khỏi vùng nhìn hoặc đổi tab. +2. **Ảnh panorama nặng, không tối ưu** — 26 ảnh JPG **6144×3072**, 2–4MB/ảnh (tổng ~70MB), nạp thẳng làm texture three.js ở độ phân giải gốc. Cache `panoramaTextureCache` (`VirtualTour.tsx:214`) giữ **toàn bộ** texture vĩnh viễn → tràn VRAM trên máy card tích hợp → thrashing/khựng. `next.config.ts` rỗng. +3. **Model 3D `.glb` khổng lồ** — `chuong-2.glb` 40MB, `chuong-1.glb` 27MB → đơ tab khi parse. `AntiquityViewer.tsx` render mọi frame vô điều kiện, pixelRatio tới 2. + +### Phụ (tích lũy) +4. **CSS animation luôn-bật + hiệu ứng nặng** (`globals.css`): nền `body` chồng 4 lớp gradient + `body::after` `mix-blend-mode: multiply` full màn hình; `public-ambient-pan` 18s vô hạn; gradient-text flow; logo/CTA "breathe"; header "glint"; lớp `.site-grain` nhiễu SVG full màn hình; **`backdrop-filter: blur(18px)`** trên mọi `.public-panel`/`.public-card` + `will-change: transform` (ép tạo layer, tốn bộ nhớ). +5. **Điểm tốt sẵn có:** Tour viewer chính (`VirtualTour.tsx:1502`) đã render **theo nhu cầu** (`needsRenderRef`) — giữ và nhân rộng mô hình này. + +## 3. Phạm vi + +**Trong phạm vi:** trang chủ + các trang công khai, tour viewer 360, viewer cổ vật 3D, pipeline tài nguyên (ảnh + model), cấu hình build, ngân sách CSS. + +**Ngoài phạm vi:** thay đổi nội dung/bố cục lớn, thêm tính năng mới, thiết kế lại giao diện. + +## 4. Giải pháp theo giai đoạn + +### Giai đoạn 1 — Chặn việc chạy nền vô ích *(đòn bẩy lớn nhất, rủi ro thấp)* +- **Hero:** thay `HeroPanoramaBackground` (canvas three.js xoay) bằng **ảnh tĩnh đã tối ưu** (next/image hoặc CSS background) → loại bỏ 1 ngữ cảnh WebGL + texture vài MB. Giảm **2 → 1** `MeshGradient`; bỏ `mix-blend-mode: soft-light`; gỡ filter SVG `feTurbulence` nếu không cần. +- **Gate mọi WebGL/animation nền** bằng `IntersectionObserver` + `document.visibilitychange`: ngoài màn hình hoặc đổi tab → **dừng `requestAnimationFrame`/`setAnimationLoop`**. Áp dụng cho hero (nếu còn shader) và `AntiquityViewer`. +- **AntiquityViewer:** chuyển sang **render theo nhu cầu** (theo mô hình tour viewer) — chỉ vẽ khi `controls` đổi hoặc đang tự xoay; pixelRatio tối đa **1.5**; dừng khi ngoài màn hình; đảm bảo chỉ **một** viewer hoạt động tại một thời điểm (lazy-mount, không khởi tạo tất cả `.glb` cùng lúc). + +### Giai đoạn 2 — Pipeline tài nguyên +- **Ảnh panorama:** thêm script Node (`sharp`) tạo bản tối ưu: + - Resize tối đa **4096px** chiều rộng (giữ tỉ lệ 2:1), xuất **WebP** chất lượng ~80 (cân nhắc AVIF). Mục tiêu giảm 70–85% dung lượng. + - Tour viewer nạp bản `.webp`; giữ LQIP blur-up sẵn có (`chua-thai-lac.lqip.ts`). +- **Model 3D:** dùng `gltf-transform` (Draco/meshopt + nén texture/resize) → 40MB ⟶ ước tính 3–8MB. Thêm `DRACOLoader`/meshopt decoder vào `AntiquityViewer`; lazy-load model. +- **Cache texture có giới hạn (LRU):** chỉ giữ cảnh hiện tại + lân cận, dispose phần xa → tránh tràn VRAM. Cân nhắc tắt mipmap cho ảnh cầu (giảm chi phí upload), kiểm chứng chất lượng. + +### Giai đoạn 3 — Ngân sách hiệu ứng CSS +- Giảm **`backdrop-filter: blur`**: hạ bán kính hoặc thay bằng nền bán trong suốt cho phần tử phổ biến; **bỏ `will-change: transform`** ở phần tử luôn hiển thị (chỉ đặt khi cần, ví dụ lúc hover). +- Tắt bớt animation **vô hạn** ít giá trị: `public-ambient-pan`, gradient-text flow, logo/CTA "breathe", header glint, `.site-grain`. **Giữ** hiệu ứng xuất hiện-khi-cuộn (chạy 1 lần) và marquee. +- Gọn lại lớp gradient nền `body`/`public-page`. + +### Giai đoạn 4 — Chia nhỏ bundle & tải +- `dynamic import` các thành phần nặng three.js (tour viewer, `AntiquityViewer`) để bundle chính nhẹ; đảm bảo `framer-motion`/`lottie-react` chỉ tải nơi cần. +- Cấu hình `next.config.ts`: bật `images` (AVIF/WebP, `deviceSizes`) và tối ưu build hợp lệ cho Next 16. + +## 5. Cách đo thành công (nghiệm thu) +- **Trang chủ lúc rảnh ≈ 0% GPU** (không còn rAF chạy ngầm); cuộn ~60fps; không còn quạt rú. +- Số ngữ cảnh WebGL trên trang chủ: **3 → 0–1**. +- Dung lượng tải trang giảm rõ (ảnh panorama & model 3D). +- Lighthouse Performance tăng; không hồi quy giao diện nhìn thấy được. + +## 6. Rủi ro & lưu ý +- **Next.js 16 bản đã chỉnh sửa** (theo `AGENTS.md`): trước khi sửa `next.config`/dynamic-import phải đọc tài liệu trong `node_modules/next/dist/docs/`. +- Nén ảnh/model có thể đổi nhẹ chất lượng → kiểm chứng bằng mắt sau mỗi bước; giữ file gốc (không ghi đè, xuất ra thư mục/định dạng mới). +- Việc gate animation phải dọn dẹp listener đúng cách để tránh rò rỉ bộ nhớ. + +## 7. Nguyên tắc thực thi +- Làm tuần tự theo giai đoạn; **đo lại sau mỗi giai đoạn** trước khi sang bước kế tiếp. +- Mỗi thay đổi giữ đúng pattern/định dạng code hiện có của dự án.