Files
SonicForgeStudio/26_ZOOM_HTML.md
T
2026-07-21 11:22:43 +07:00

78 lines
3.0 KiB
Markdown

## 💡 Nguyên lý tính toán đúng (Zoom to Mouse Pointer)
Để điểm dưới con trỏ chuột đứng yên tại đúng vị trí đó sau khi zoom, bạn cần giữ nguyên **tỷ lệ thời gian (time ratio)** tại điểm con trỏ chuột so với chiều rộng hiện tại của vùng hiển thị (Viewport).
### **Công thức chuyển đổi:**
Giả sử thanh cuộn (Scrollbar) có vị trí xả hiện tại là `scrollLeft`:
1. **Tìm điểm thời gian tương đối tại vị trí chuột ($T_{mouse}$):**
$$T_{mouse} = \text{scrollLeft} + X_{mouse\_in\_canvas}$$
2. **Tính tỷ lệ zoom mới ($S_{new} / S_{old}$):**
$$\text{ratio} = \frac{\text{scale}_{new}}{\text{scale}_{old}}$$
3. **Cập nhật vị trí cuộn mới (`scrollLeft_{new}`):**
$$\text{scrollLeft}_{new} = (T_{mouse} \times \text{ratio}) - X_{mouse\_in\_canvas}$$
---
## 🛠️ Code mẫu ngắn gọn (Pure JS / Canvas)
Dưới đây là đoạn code lắng nghe sự kiện `wheel` (lăn chuột) trên Waveform Canvas/Container để xử lý zoom đúng chuẩn các phần mềm DAW:
```javascript
const container = document.getElementById('waveform-container');
let pixelsPerSecond = 100; // Tỉ lệ Zoom ban đầu (mức Zoom)
container.addEventListener('wheel', (e) => {
// Chỉ thực hiện zoom khi giữ phím Ctrl (hoặc bạn có thể bỏ condition này nếu muốn lăn chuột là zoom)
if (!e.ctrlKey) return;
e.preventDefault();
// 1. Lấy vị trí con trỏ chuột so với viền trái của Waveform Container (Viewport)
const rect = container.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
// 2. Tính tọa độ thời gian (giây) tại điểm con trỏ chuột đang chỉ vào
const currentScrollLeft = container.scrollLeft;
const timeAtMouse = (currentScrollLeft + mouseX) / pixelsPerSecond;
// 3. Tính tỉ lệ zoom mới (Phóng to / Thu nhỏ)
const zoomFactor = e.deltaY < 0 ? 1.2 : 0.8; // Lăn lên = phóng to, lăn xuống = thu nhỏ
const newPixelsPerSecond = Math.max(10, Math.min(2000, pixelsPerSecond * zoomFactor));
// 4. Cập nhật tỉ lệ zoom mới vào ứng dụng
pixelsPerSecond = newPixelsPerSecond;
// (Thực hiện render lại Waveform với pixelsPerSecond mới tại đây)
renderWaveform();
// 5. CẬP NHẬT SCROLLBAR: Cuộn lại sao cho điểm 'timeAtMouse' vẫn nằm đúng ở 'mouseX'
container.scrollLeft = (timeAtMouse * pixelsPerSecond) - mouseX;
}, { passive: false });
```
---
## 📌 Nhắc nhở thêm nếu dùng thư viện:
* **Nếu bạn dùng Canvas thuần:** Đảm bảo hàm `renderWaveform()` vẽ lại waveform dựa theo `pixelsPerSecond` mới trước khi cập nhật `container.scrollLeft`.
* **Nếu bạn đang dùng `wavesurfer.js`:** Thư viện này đã hỗ trợ sẵn logic này, bạn chỉ cần dùng method:
```javascript
wavesurfer.zoom(newPxPerSec);
```
*(Nếu WaveSurfer bản cũ bị trôi, bạn áp dụng lại công thức tính `scrollLeft` ở trên sau khi gọi lệnh `zoom()`)*.