Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

วิธีสร้างภาพซูม (Image Zoom) แบบ Hover ด้วย CSS และ JavaScript


ตัวอย่างโค้ด

สร้างเอฟเฟกต์ซูมภาพแบบ "Loupe" (แว่นขยาย) เมื่อ hover เมาส์บนภาพ ภาพขยายจะแสดงใน container ข้างเคียง

<!DOCTYPE html>
<html lang="th">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Image Zoom</title>
  <style>
    * { box-sizing: border-box; }
    body { font-family: 'Segoe UI', sans-serif; padding: 20px; }
    
    .img-zoom-container {
      position: relative;
      display: inline-block;
      max-width: 100%;
    }
    .img-zoom-container img {
      max-width: 100%;
      height: auto;
      display: block;
    }
    
    /* แว่นขยาย (Lens) */
    .img-zoom-lens {
      position: absolute;
      border: 2px solid #d4d4d4;
      width: 100px;
      height: 100px;
      border-radius: 50%;
      cursor: crosshair;
      pointer-events: none; /* ให้ event ผ่านไปยังรูปภาพ */
      display: none;
      box-shadow: 0 0 0 9999px rgba(0,0,0,0.3); /* มืดบริเวณนอก */
    }
    
    /* ภาพผลลัพธ์ (Zoomed Result) */
    .img-zoom-result {
      display: inline-block;
      border: 1px solid #d4d4d4;
      width: 400px;
      height: 400px;
      background-repeat: no-repeat;
      background-size: cover;
      box-shadow: 0 4px 12px rgba(0,0,0,0.15);
      border-radius: 8px;
      margin-left: 20px;
      vertical-align: top;
    }
    
    @media (max-width: 900px) {
      .img-zoom-result {
        width: 100%;
        height: 300px;
        margin-left: 0;
        margin-top: 20px;
      }
    }
  </style>
</head>
<body>
  <h1>Image Zoom แบบ Hover (Loupe Effect)</h1>
  <p>วางเมาส์บนภาพด้านซ้ายเพื่อดูภาพขยายทางขวา</p>
  
  <div class="img-zoom-container">
    <img class="myimage" src="https://picsum.photos/id/1015/600/400" alt="Sample Image">
    <div class="img-zoom-result" id="zoomResult"></div>
  </div>

  <script>
    const imgEle = document.querySelector('.myimage');
    const resultEle = document.getElementById('zoomResult');
    
    enlargeImage(imgEle, resultEle);
    
    function enlargeImage(imgEle, resultEle) {
      let zoomLens, xRatio, yRatio;
      
      // สร้าง lens element
      zoomLens = document.createElement('DIV');
      zoomLens.className = 'img-zoom-lens';
      imgEle.parentElement.insertBefore(zoomLens, imgEle);
      
      // คำนวณอัตราส่วนการขยาย
      function updateRatios() {
        // รอให้รูปโหลดเสร็จ
        if (imgEle.naturalWidth === 0) {
          imgEle.addEventListener('load', updateRatios, { once: true });
          return;
        }
        xRatio = resultEle.offsetWidth / zoomLens.offsetWidth;
        yRatio = resultEle.offsetHeight / zoomLens.offsetHeight;
        
        resultEle.style.backgroundImage = `url('${imgEle.src}')`;
        resultEle.style.backgroundSize = `${imgEle.naturalWidth * xRatio}px ${imgEle.naturalHeight * yRatio}px`;
      }
      
      updateRatios();
      
      // Event listeners
      imgEle.addEventListener('mousemove', moveLens);
      imgEle.addEventListener('touchmove', moveLens);
      imgEle.addEventListener('mouseenter', () => zoomLens.style.display = 'block');
      imgEle.addEventListener('mouseleave', () => {
        zoomLens.style.display = 'none';
        resultEle.style.backgroundPosition = 'center center';
      });
      
      function moveLens(e) {
        e.preventDefault();
        const pos = getCursorPos(e);
        let x = pos.x - zoomLens.offsetWidth / 2;
        let y = pos.y - zoomLens.offsetHeight / 2;
        
        // จำกัดขอบเขต
        x = Math.max(0, Math.min(x, imgEle.offsetWidth - zoomLens.offsetWidth));
        y = Math.max(0, Math.min(y, imgEle.offsetHeight - zoomLens.offsetHeight));
        
        zoomLens.style.left = x + 'px';
        zoomLens.style.top = y + 'px';
        
        // อัพเดท background-position ของผลลัพธ์
        resultEle.style.backgroundPosition = `-${x * xRatio}px -${y * yRatio}px`;
      }
      
      function getCursorPos(e) {
        const rect = imgEle.getBoundingClientRect();
        const clientX = e.touches ? e.touches[0].clientX : e.clientX;
        const clientY = e.touches ? e.touches[0].clientY : e.clientY;
        return {
          x: clientX - rect.left - window.pageXOffset,
          y: clientY - rect.top - window.pageYOffset
        };
      }
    }
  </script>
</body>
</html>

การทำงาน

  • เมื่อ hover บนภาพ → แสดงวงกลม "แว่นขยาย" (Lens)
  • เลื่อนเมาส์ → Lens ตามไปพร้อมอัพเดทภาพขยายใน container ข้างเคียง
  • รองรับ Touch events สำหรับมือถือ/แท็บเล็ต
  • Responsive: บนจอเล็ก ภาพขยายจะอยู่ด้านล่างแทน

หลักการทำงาน (Logic)

  1. คำนวณ Ratio: xRatio = resultWidth / lensWidth - บอกว่า 1px ของ lens เท่ากับกี่ px ของภาพจริง
  2. ตำแหน่ง Lens: ตามเมาส์ ลบครึ่งขนาด lens เพื่อให้เมาส์อยู่กึ่งกลาง
  3. Clamp ขอบเขต: ไม่ให้ lens ออกนอกขอบภาพ
  4. Background Position: -x * xRatio, -y * yRatio เลื่อนพื้นหลังภาพขยาย

แนวทางปรับปรุง

  • เพิ่ม loading="lazy" ให้รูปภาพ
  • ใช้ IntersectionObserver เริ่มทำงานเฉพาะเมื่อ scroll มาเห็น
  • เพิ่มปุ่ม toggle zoom on/off
  • รองรับ pinch-to-zoom บนมือถือ
  • เพิ่ม gallery หลายรูป (click เปลี่ยนรูป)

CSS-only Alternative (Simple)

.zoom-container:hover img { transform: scale(2); }
.zoom-container { overflow: hidden; }

วิธีนี้ง่ายกว่าแต่ไม่มี "loupe" แยกต่างหาก