要實現 html 中的圖片對比滑塊效果,1. 使用 css 的 clip-path 屬性和 JavaScript 交互控制;2. 構建包含兩張圖片和滑塊的 html 結構;3. 利用 css 定位使圖片層疊并裁剪上層圖片;4. 通過 javascript 監聽鼠標事件動態調整滑塊位置和裁剪區域。移動端優化需:5. 添加觸摸事件支持(touchstart、touchend、touchmove);6. 阻止默認滾動行為;7. 使用節流函數優化性能;8. 啟用懶加載和響應式圖片提升加載速度;9. 使用 will-change 屬性優化渲染性能。鍵盤控制方面:10. 添加鍵盤事件監聽以支持方向鍵操作;11. 設置 tabindex 使滑塊可聚焦;12. 提供焦點樣式和 aria 屬性增強可訪問性。自適應不同尺寸圖片的方法包括:13. 使用彈性容器設置寬度和高度;14. 通過 Object-fit 控制圖片填充方式;15. 在 javascript 中動態計算滑塊位置以適配窗口變化;16. 使用 srcset 或 picture 元素實現響應式圖片加載。
要實現 HTML 中的圖片對比滑塊(before-after)效果,核心在于利用 CSS 的 clip-path 屬性和 JavaScript 的交互控制。簡單來說,就是兩張圖片疊在一起,通過滑塊動態調整上面那張圖片的可見區域,露出下面的圖片,形成對比效果。
解決方案
-
HTML 結構:
<div class="image-comparison"> @@##@@ @@##@@ <div class="slider"> <div class="handle"></div> </div> </div>
這里,.image-comparison 是容器,包含 before 和 after 兩張圖片,以及一個滑塊 .slider,滑塊里有一個手柄 .handle。before.jpg 和 after.jpg 分別替換成你的實際圖片。
立即學習“前端免費學習筆記(深入)”;
-
CSS 樣式:
.image-comparison { position: relative; width: 600px; /* 根據你的圖片調整 */ height: 400px; /* 根據你的圖片調整 */ overflow: hidden; /* 隱藏超出容器的部分 */ } .image-comparison img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; /* 保證圖片比例 */ } .image-comparison .after { clip-path: polygon(0 0, 50% 0, 50% 100%, 0 100%); /* 初始狀態,after 圖片顯示一半 */ } .image-comparison .slider { position: absolute; top: 0; left: 50%; /* 初始位置在中間 */ width: 5px; height: 100%; background-color: rgba(0, 0, 0, 0.2); cursor: ew-resize; /* 鼠標樣式 */ z-index: 10; /* 保證滑塊在最上層 */ } .image-comparison .handle { position: absolute; top: 50%; left: -10px; /* 調整手柄位置 */ width: 25px; height: 25px; background-color: white; border-radius: 50%; border: 1px solid rgba(0, 0, 0, 0.3); transform: translateY(-50%); /* 垂直居中 */ }
關鍵點:clip-path 屬性用于裁剪 after 圖片,初始狀態只顯示一半。slider 的位置決定了裁剪的比例。
-
JavaScript 交互:
const comparison = document.querySelector('.image-comparison'); const slider = document.querySelector('.slider'); const afterImage = document.querySelector('.after'); let isDragging = false; slider.addEventListener('mousedown', (e) => { isDragging = true; }); document.addEventListener('mouseup', () => { isDragging = false; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const rect = comparison.getBoundingClientRect(); let position = (e.clientX - rect.left) / rect.width; if (position < 0) position = 0; if (position > 1) position = 1; slider.style.left = position * 100 + '%'; afterImage.style.clipPath = `polygon(0 0, ${position * 100}% 0, ${position * 100}% 100%, 0 100%)`; });
這段 JavaScript 代碼監聽鼠標事件,當鼠標按下并移動滑塊時,動態改變 slider 的 left 屬性和 afterImage 的 clip-path 屬性,從而實現對比效果。getBoundingClientRect() 獲取元素相對于視口的位置和大小,避免在滾動頁面時出現位置計算錯誤。
如何優化圖片對比滑塊的移動端體驗?
移動端體驗優化主要集中在觸摸事件的處理和性能優化。
-
觸摸事件支持:
將 mousedown、mouseup、mousemove 事件替換為 touchstart、touchend、touchmove。
slider.addEventListener('touchstart', (e) => { isDragging = true; }); document.addEventListener('touchend', () => { isDragging = false; }); document.addEventListener('touchmove', (e) => { if (!isDragging) return; const rect = comparison.getBoundingClientRect(); let position = (e.touches[0].clientX - rect.left) / rect.width; // 使用 touches[0].clientX if (position < 0) position = 0; if (position > 1) position = 1; slider.style.left = position * 100 + '%'; afterImage.style.clipPath = `polygon(0 0, ${position * 100}% 0, ${position * 100}% 100%, 0 100%)`; });
注意:touchmove 事件的 e 對象中,觸摸位置信息存儲在 e.touches 數組中,通常取第一個觸摸點 e.touches[0]。
-
阻止默認行為:
在 touchmove 事件處理函數中,調用 e.preventDefault() 阻止頁面滾動,避免影響滑塊操作。
document.addEventListener('touchmove', (e) => { if (!isDragging) return; e.preventDefault(); // 阻止頁面滾動 // ... });
-
節流 (Throttling) 或防抖 (Debouncing):
touchmove 事件觸發頻率很高,可能導致性能問題。使用節流或防抖技術限制事件處理函數的執行頻率。
function throttle(func, delay) { let timeoutId; let lastExec = 0; return function(...args) { const context = this; const now = Date.now(); if (!timeoutId) { if (now - lastExec >= delay) { func.apply(context, args); lastExec = now; } else { timeoutId = setTimeout(() => { func.apply(context, args); lastExec = Date.now(); timeoutId = null; }, delay - (now - lastExec)); } } }; } const throttledMoveHandler = throttle((e) => { const rect = comparison.getBoundingClientRect(); let position = (e.touches[0].clientX - rect.left) / rect.width; if (position < 0) position = 0; if (position > 1) position = 1; slider.style.left = position * 100 + '%'; afterImage.style.clipPath = `polygon(0 0, ${position * 100}% 0, ${position * 100}% 100%, 0 100%)`; }, 50); // 50ms 節流 document.addEventListener('touchmove', (e) => { if (!isDragging) return; e.preventDefault(); throttledMoveHandler(e); });
這里使用了節流函數 throttle,確保事件處理函數至少每 50 毫秒執行一次。
-
優化圖片加載:
使用懶加載 (Lazy Loading) 技術,只加載視口內的圖片,提高頁面加載速度。還可以使用響應式圖片,根據屏幕尺寸加載不同大小的圖片。
-
CSS will-change 屬性:
使用 will-change 屬性告訴瀏覽器哪些屬性將會改變,提前進行優化。
.image-comparison .slider { will-change: left; } .image-comparison .after { will-change: clip-path; }
如何讓圖片對比滑塊支持鍵盤控制?
鍵盤控制能提升可訪問性,方便無法使用鼠標或觸摸屏的用戶。
-
添加鍵盤事件監聽:
監聽 keydown 事件,檢測左右方向鍵。
slider.addEventListener('keydown', (e) => { let position = parseFloat(slider.style.left) / 100; // 獲取當前位置 if (e.key === 'ArrowLeft') { position -= 0.05; // 向左移動 5% } else if (e.key === 'ArrowRight') { position += 0.05; // 向右移動 5% } else { return; // 不是左右方向鍵,直接返回 } if (position < 0) position = 0; if (position > 1) position = 1; slider.style.left = position * 100 + '%'; afterImage.style.clipPath = `polygon(0 0, ${position * 100}% 0, ${position * 100}% 100%, 0 100%)`; });
-
使滑塊可聚焦:
給滑塊添加 tabindex=”0″ 屬性,使其可以通過 Tab 鍵聚焦。
<div class="slider" tabindex="0"> <div class="handle"></div> </div>
-
焦點樣式:
添加 CSS 樣式,當滑塊獲得焦點時,顯示明顯的視覺提示。
.image-comparison .slider:focus { outline: none; /* 移除默認 outline */ box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); /* 添加陰影 */ }
-
無障礙屬性 (ARIA):
添加 ARIA 屬性,增強可訪問性。
<div class="slider" tabindex="0" role="slider" aria-label="Image Comparison Slider" aria-valuemin="0" aria-valuemax="100" aria-valuenow="50"> <div class="handle"></div> </div>
- role=”slider”:聲明這是一個滑塊。
- aria-label:提供滑塊的描述。
- aria-valuemin 和 aria-valuemax:定義滑塊的最小值和最大值。
- aria-valuenow:動態更新滑塊的當前值。
在 JavaScript 中更新 aria-valuenow 屬性:
slider.addEventListener('keydown', (e) => { // ... (之前的代碼) slider.setAttribute('aria-valuenow', position * 100); // 更新 aria-valuenow });
如何處理不同尺寸圖片的自適應問題?
自適應的關鍵在于讓容器和圖片都具有彈性。
-
彈性容器:
使用百分比或 vw/vh 單位設置 .image-comparison 的寬度和高度,使其隨屏幕尺寸變化。
.image-comparison { width: 80vw; /* 寬度為視口寬度的 80% */ height: 50vh; /* 高度為視口高度的 50% */ }
-
圖片 object-fit 屬性:
object-fit: cover 保證圖片比例,并填充整個容器。如果圖片比例與容器比例不一致,圖片可能會被裁剪。object-fit: contain 則保證圖片完整顯示,可能會在容器中留白。
.image-comparison img { object-fit: cover; /* 或 object-fit: contain; */ }
-
clip-path 的單位:
clip-path 使用百分比單位,使其與容器尺寸同步變化。
.image-comparison .after { clip-path: polygon(0 0, 50% 0, 50% 100%, 0 100%); }
-
JavaScript 中的動態計算:
如果需要更精確的控制,可以在 JavaScript 中動態計算容器尺寸和滑塊位置。
function updateSlider() { const rect = comparison.getBoundingClientRect(); let position = parseFloat(slider.style.left) / 100; if (position < 0) position = 0; if (position > 1) position = 1; slider.style.left = position * 100 + '%'; afterImage.style.clipPath = `polygon(0 0, ${position * 100}% 0, ${position * 100}% 100%, 0 100%)`; } window.addEventListener('resize', updateSlider); // 監聽窗口大小變化 updateSlider(); // 初始調用
這段代碼在窗口大小變化時,重新計算滑塊位置和 clip-path,保證自適應效果。
-
響應式圖片:
使用
元素或 srcset 屬性,根據屏幕尺寸加載不同大小的圖片。 <picture> <source media="(max-width: 768px)" srcset="before-small.jpg"> @@##@@ </picture>
這樣,在小屏幕上加載 before-small.jpg,在大屏幕上加載 before.jpg,提高加載速度和用戶體驗。
通過上述方法,可以實現一個在各種屏幕尺寸下都能良好工作的圖片對比滑塊。