| Cỡ chữ:   
<!DOCTYPE html> <html> <head> <script src="https://cdn.tailwindcss.com"></script> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> <link rel="stylesheet" href="style.css"> </head> <body> <div class="w-full max-w-4xl bg-white rounded-xl shadow-lg p-6"> <div class="text-center mb-6"> <h1 class="text-3xl font-bold text-gray-900">Trình xem ảnh trên Canvas</h1> <p class="text-gray-600 mt-2">Sử dụng con lăn chuột để phóng to/thu nhỏ. Nhấn và kéo chuột để di chuyển ảnh.</p> </div> <!-- Vùng chứa Canvas --> <div class="mb-4 bg-gray-200 rounded-lg overflow-hidden"> <canvas id="imageCanvas"></canvas> </div> <!-- Bảng điều khiển --> <div class="flex flex-col sm:flex-row items-center justify-center gap-4"> <!-- Nút tải ảnh lên --> <div class="custom-file-button"> <label for="imageLoader" class="cursor-pointer bg-blue-600 text-white font-semibold py-2 px-5 rounded-lg hover:bg-blue-700 transition-colors duration-300 shadow-sm"> Tải ảnh lên </label> <input type="file" id="imageLoader" name="imageLoader" accept="image/*"/> </div> <!-- Các nút điều khiển zoom --> <div class="flex items-center gap-2"> <button id="zoomInBtn" class="w-10 h-10 flex items-center justify-center bg-gray-200 rounded-full hover:bg-gray-300 transition-colors duration-200 text-lg font-bold">-</button> <button id="resetBtn" class="bg-gray-200 text-gray-800 font-semibold py-2 px-5 rounded-lg hover:bg-gray-300 transition-colors duration-300">Reset</button> <button id="zoomOutBtn" class="w-10 h-10 flex items-center justify-center bg-gray-200 rounded-full hover:bg-gray-300 transition-colors duration-200 text-lg font-bold">+</button> </div> </div> </div> <script src="script.js"></script> </body> </html>
body { font-family: 'Inter', sans-serif; } canvas { cursor: grab; border: 2px solid #e2e8f0; border-radius: 0.5rem; } canvas:active { cursor: grabbing; } .custom-file-button input[type="file"] { display: none; }
// Lấy các phần tử DOM const canvas = document.getElementById('imageCanvas'); const ctx = canvas.getContext('2d'); const imageLoader = document.getElementById('imageLoader'); const zoomInBtn = document.getElementById('zoomInBtn'); const zoomOutBtn = document.getElementById('zoomOutBtn'); const resetBtn = document.getElementById('resetBtn'); // Khởi tạo ảnh và các biến trạng thái const img = new Image(); let scale = 1; let offsetX = 0; let offsetY = 0; let isDragging = false; let lastX = 0; let lastY = 0; const ZOOM_SENSITIVITY = 0.001; const BUTTON_ZOOM_FACTOR = 1.2; // Thiết lập kích thước canvas theo container function resizeCanvas() { const container = canvas.parentElement; canvas.width = container.clientWidth; canvas.height = window.innerHeight * 0.6; // Chiếm 60% chiều cao cửa sổ draw(); // Vẽ lại khi thay đổi kích thước } // Hàm vẽ chính function draw() { // Xóa toàn bộ canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Lưu trạng thái context hiện tại ctx.save(); // Di chuyển gốc tọa độ đến vị trí pan ctx.translate(offsetX, offsetY); // Phóng to/thu nhỏ từ gốc tọa độ mới ctx.scale(scale, scale); // Tính toán vị trí để vẽ ảnh sao cho nó căn giữa const x = (canvas.width / scale - img.width) / 2; const y = (canvas.height / scale - img.height) / 2; // Vẽ ảnh ctx.drawImage(img, x, y); // Khôi phục lại trạng thái context ctx.restore(); } // Xử lý khi tải ảnh xong img.onload = () => { resetTransformations(); }; // Hàm reset về trạng thái ban đầu function resetTransformations() { const canvasAspect = canvas.width / canvas.height; const imageAspect = img.width / img.height; if (imageAspect > canvasAspect) { scale = canvas.width / img.width; } else { scale = canvas.height / img.height; } offsetX = (canvas.width - img.width * scale) / 2; offsetY = (canvas.height - img.height * scale) / 2; draw(); } // Xử lý khi người dùng chọn file imageLoader.addEventListener('change', (e) => { const reader = new FileReader(); reader.onload = (event) => { img.src = event.target.result; } if(e.target.files[0]) { reader.readAsDataURL(e.target.files[0]); } }); // Xử lý sự kiện chuột canvas.addEventListener('mousedown', (e) => { isDragging = true; canvas.style.cursor = 'grabbing'; lastX = e.clientX; lastY = e.clientY; }); canvas.addEventListener('mouseup', () => { isDragging = false; canvas.style.cursor = 'grab'; }); canvas.addEventListener('mouseleave', () => { isDragging = false; canvas.style.cursor = 'grab'; }); canvas.addEventListener('mousemove', (e) => { if (!isDragging) return; const dx = e.clientX - lastX; const dy = e.clientY - lastY; offsetX += dx; offsetY += dy; lastX = e.clientX; lastY = e.clientY; draw(); }); // Xử lý con lăn chuột để zoom canvas.addEventListener('wheel', (e) => { e.preventDefault(); // Ngăn trang cuộn const rect = canvas.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; // Tính toán vị trí chuột trên ảnh trước khi zoom const mouseBeforeZoomX = (mouseX - offsetX) / scale; const mouseBeforeZoomY = (mouseY - offsetY) / scale; // Cập nhật scale const delta = e.deltaY * ZOOM_SENSITIVITY * -1; const newScale = scale * Math.exp(delta); scale = Math.max(0.1, Math.min(newScale, 20)); // Giới hạn mức zoom // Cập nhật offset để giữ nguyên vị trí chuột trên ảnh offsetX = mouseX - mouseBeforeZoomX * scale; offsetY = mouseY - mouseBeforeZoomY * scale; draw(); }); // Hàm zoom với tâm là trung tâm canvas function zoom(factor) { const centerX = canvas.width / 2; const centerY = canvas.height / 2; // Tính toán vị trí chuột trên ảnh trước khi zoom const centerBeforeZoomX = (centerX - offsetX) / scale; const centerBeforeZoomY = (centerY - offsetY) / scale; // Cập nhật scale const newScale = scale * factor; scale = Math.max(0.1, Math.min(newScale, 20)); // Giới hạn mức zoom // Cập nhật offset để giữ nguyên vị trí chuột trên ảnh offsetX = centerX - centerBeforeZoomX * scale; offsetY = centerY - centerBeforeZoomY * scale; draw(); } // Sự kiện cho các nút điều khiển zoomInBtn.addEventListener('click', () => zoom(1 / BUTTON_ZOOM_FACTOR)); zoomOutBtn.addEventListener('click', () => zoom(BUTTON_ZOOM_FACTOR)); resetBtn.addEventListener('click', resetTransformations); // Khởi tạo window.addEventListener('resize', resizeCanvas); // Tải ảnh mặc định khi bắt đầu img.src = 'https://placehold.co/1200x800/e2e8f0/333333?text=Tải+ảnh+của+bạn'; // Thiết lập kích thước canvas lần đầu resizeCanvas();