借助 Input Range 實現圖片對比功能
之前在項目中做了一個圖片對比的工具,演示如下:
有一些有趣實用的小細節,一起看看如何實現的吧!
一、布局
布局比較簡單,兩張圖片上下重疊在一起就行了。
<div class="wrap">
<img class="img" src="img1" >
<img class="img" src="img2">
</div>
這里可以只給第一個圖片設置絕對定位,有幾個好處。
- 第1張圖片層級自動就高了。
- 第1張圖片仍在原處,也無需設置 left、top。
- 第1張圖片不占空間,父級高度就由第2張圖片決定了。
.img:first-of-type{
position: absolute
}
示意如下:
二、滑動圖片
這里有個滑動的操作,如果直接使用 JS
來實現的話可能是這樣(實際還有更多細節)。
wrap.onmousedown = () => {}
document.onmousemove = () => {}
document.onmouseup = () => {}
其實在這里大可不必這么麻煩,完全可以借助「原生標簽」的能力。提到"滑動",是不是可以想到 input range 呢?
<input type="range">
顯然這個效果與實際相差甚遠,不過完全可以用CSS定制一下,主要是去除背景色。
[type=range]{
margin: 0;
-webkit-appearance: none;
position: absolute;
height: 100%;
max-width: 100%;
background: none;
overflow: hidden;
z-index: 2;
}
::-webkit-slider-runnable-track {
background-color: transparent; /*去除背景*/
height: 100%;
cursor: ew-resize;
}
::-webkit-slider-thumb {
-webkit-appearance: none;
width: 4px;
height: 100%;
background-color: #18a0fb;
}
這樣就變成了一個「背景透明,只剩下滑塊」的滑動條了,并且整個范圍都是可以滑動的。
然后把滑動條覆蓋在圖片之上就行了。
<div class="wrap">
<img class="img" src="img1" >
<input type="range">
<img class="img" src="img2">
</div>
三、裁切圖片
現在需要在滑動過程中,動態裁剪上面的圖片,可以通過 input 的 oninput 事件監聽當前的進度,這里可以通過 CSS 變量記錄在父級。
input.oninput = () => {
this.parentNode.style.setProperty('--pos', this.value / 100);
}
有了這個 CSS 變量,很多種方式都可以實現裁剪效果了,想了想至少有以下幾種方式。
1、直接改變width
如果是普通的 div 標簽,可以直接通過 overflow: hidden來裁剪內容,這里是圖片,沒有單獨的父級,只能直接改變圖片的寬高。
.img:first-child{
width: calc( var(--pos, .5) * 100% );
height: 100%
}
但是這樣很明顯會有一個問題,圖片被拉伸變形了。
怎么解決呢?其實通過圖片的 「object-」* 屬性就可以完美解決了。
object-fit - CSS:層疊樣式表 | MDN (mozilla.org)[2]
object-position - CSS:層疊樣式表 | MDN (mozilla.org)[3]
這里用cover可以滿足我們的需求。
.img:first-child{
width: calc( var(--pos, .5) * 100% );
height: 100%;
object-fit: cover;
object-position: left;
}
這樣就正常了。
2、clip-path裁剪
可以直接利用 clip-path 中的 inset 函數裁剪一個矩形。inset的四個參數分別表示距離上、右、下、左的距離,示意如下:
用代碼實現就是。
.img:first-child{
clip-path: inset( 0 calc( 100% - var(--pos, .5) * 100% ) 0 0 )
}
這種實現簡單快速,也很好理解。
3、mask遮罩
還可以通過 mask遮罩 實現,具體做法就是繪制一個矩形,然后改變遮罩的尺寸mask-size。
用代碼實現就是。
.img:first-child{
-webkit-mask: linear-gradient(red,red) left center no-repeat; /*漸變繪制一個矩形*/
-webkit-mask-size: calc( var(--pos, 0.5) * 100% ) 100%;
}
同樣能得到這樣的效果:
你也可以查看以下任意鏈接:
- img-diff (juejin.cn)[4]
- img-diff (codepen.io)[5]
- img-diff (runjs.work)[6]
四、總結一下
以上實現了一個圖片對比的小交互,主要特點是通過 input range 的原生特性,省去了大量鼠標交互邏輯
接著是 CSS 變量的運用,JS只是一個媒介,很多交互就可以直接在 CSS 中完成了
然后在裁剪圖片中用到了3種方式,3種不同的思路相信可以帶來不一樣的思維空間,可以視情況自行選擇。
參考鏈接:
[1]你可能不知道的絕對定位: https://juejin.cn/post/7204633786934607929。
[2]object-fit - CSS:層疊樣式表 | MDN (mozilla.org): https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-fit。
[3]object-position - CSS:層疊樣式表 | MDN (mozilla.org): https://developer.mozilla.org/zh-CN/docs/Web/CSS/object-position。
[4]img-diff (juejin.cn): https://code.juejin.cn/pen/7255942145469579319。
[5]img-diff (codepen.io): https://codepen.io/xboxyan/pen/XWgMLxg。
[6]img-diff (runjs.work): https://runjs.work/projects/f62ed9adee7f426b。