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

3.0 KiB

💡 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}
  1. Tính tỷ lệ zoom mới (S_{new} / S_{old}):
\text{ratio} = \frac{\text{scale}_{new}}{\text{scale}_{old}}
  1. 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:

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:
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()).