本文將用一個極簡的例子詳細講解如何用原生JS一步步實現完整的圖片預覽和查看功能,無任何第三方依賴,兼容PC與H5,實現了觸屏雙指縮放等,干貨滿滿。
為提升閱讀體驗,正文中代碼展示均有部分省略處理,完整代碼可以在文末查看。
實現圖片預覽/查看的關鍵在于 CSS3 中的 transform 變換,該屬性應用于元素在2D或3D上的旋轉,縮放,移動,傾斜等等變換,通過設置 translate(x,y) 即可偏移元素位置,設置scale即可縮放元素,當然你也可以只設置 matrix 來完成上述所有操作,這涉及到矩陣變換的知識,本文使用的均是CSS提供的語法糖進行變換操作。
PC上的點擊、移動,H5的手勢操作,都離不開DOM事件監聽。例如鼠標移動事件對應 mousemove,移動端因為沒有鼠標則對應 touchmove,而本文將介紹如何僅通過指針事件來進行多端統一的事件監聽。在監聽事件中我們可以通過 event 對象獲取各種屬性,例如常用的 offsetX、offsetY 相對偏移量,clientX、clientY 距離窗口的橫坐標和縱坐標等。
除此之外可能還需要具備一點數學基礎,如果像我這樣數學知識幾乎都還給了高中老師的話可以復習下向量的加減計算。
在開始前我們先準備一個圖片列表,并綁定好點擊事件,當點擊圖片時,通過 document.createElement 創建元素,然后把圖片節點克隆進蒙層中,這對你來說并不難,簡單實現如下。
<div id="list">
<img class="item" src="...." />
............
</div>
/* 圖片預覽樣式 */
.modal {
touch-action: none;
user-select: none;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.75);
}
.modal > img {
position: absolute;
padding: 0;
margin: 0;
transform: translateZ(0);
}
let cloneEl=null
let originalEl=null
document.getElementById('list').addEventListener('click', function (e) {
e.preventDefault()
if (e.target.classList.contains('item')) {
originalEl=e.target // 緩存原始圖DOM節點
cloneEl=originalEl.cloneNode(true) // 克隆圖片
originalEl.style.opacity=0
openPreview() // 打開預覽
}
})
function openPreview() {
// 創建蒙層
const mask=document.createElement('div')
mask.classList.add('modal')
// 添加在body下
document.body.appendChild(mask)
// 注冊蒙層的點擊事件,關閉彈窗
const clickFunc=function () {
document.body.removeChild(this)
originalEl.style.opacity=1
mask.removeEventListener('click', clickFunc)
}
mask.addEventListener("click", clickFunc)
mask.appendChild(cloneEl) // 添加圖片
}
// 用于修改樣式的工具類,并且可以減少回流重繪,后面代碼中會頻繁用到
function changeStyle(el, arr) {
const original=el.style.cssText.split(';')
original.pop()
el.style.cssText=original.concat(arr).join(';') + ';'
}
這時候我們成功添加一個打開預覽的蒙層效果了,但克隆出來的圖片位置是沒有指定的,此時需要用 getBoundingClientRect() 方法獲取一下元素相對于可視窗口的距離,設置為圖片的起始位置,覆蓋在原圖片的位置之上,以取代文檔流中的圖片。
// ......
// 添加圖片
const { top, left }=originalEl.getBoundingClientRect()
changeStyle(cloneEl, [`left: ${left}px`, `top: ${top}px`])
mask.appendChild(cloneEl)
效果如下,看起來像點擊高亮圖片的感覺:
接下來我們需要實現焦點放大的效果,簡單來說就是計算兩點之間的位移距離作為 translate 偏移量,將圖片偏移到屏幕中心點位置,然后縮放一定的比例來達到查看效果,通過 transition 實現過渡動畫。
中心點位置我們可以通過 window 下的 innerWidth 和 innerHeight 來獲取瀏覽器可視區域寬高,然后除以2即可得到中心點坐標。
const { innerWidth: winWidth, innerHeight: winHeight }=window
// 計算自適應屏幕的縮放值
function adaptScale() {
const { offsetWidth: w, offsetHeight: h }=originalEl // 獲取文檔中圖片的寬高
let scale=0
scale=winWidth / w
if (h * scale > winHeight - 80) {
scale=(winHeight - 80) / h
}
return scale
}
// 移動圖片到屏幕中心位置
const originalCenterPoint={ x: offsetWidth / 2 + left, y: offsetHeight / 2 + top }
const winCenterPoint={ x: winWidth / 2, y: winHeight / 2 }
const offsetDistance={ left: winCenterPoint.x - originalCenterPoint.x + left, top: winCenterPoint.y - originalCenterPoint.y + top }
const diffs={ left: ((adaptScale() - 1) * offsetWidth) / 2, top: ((adaptScale() - 1) * offsetHeight) / 2 }
changeStyle(cloneEl, ['transition: all 0.3s', `width: ${offsetWidth * adaptScale() + 'px'}`, `transform: translate(${offsetDistance.left - left - diffs.left}px, ${offsetDistance.top - top - diffs.top}px)`])
// 動畫結束后消除定位重置的偏差
setTimeout(()=> {
changeStyle(cloneEl, ['transition: all 0s', `left: 0`, `top: 0`, `transform: translate(${offsetDistance.left - diffs.left}px, ${offsetDistance.top - diffs.top}px)`])
offset={ left: offsetDistance.left - diffs.left, top: offsetDistance.top - diffs.top } // 記錄值
}, 300)
這里先利用絕對定位 left top 來設置克隆元素的初始位置,再通過 translate 偏移位置,是為了更自然地實現動畫效果,動畫結束后再將絕對定位的數值歸零并把偏移量加進 translate 中,并且這里我并沒有直接使用 scale 放大元素,而是將比例轉化為寬高的變化。最終效果如下:
在PC實現圖片縮放相對是比較簡單的,我們利用滾輪事件監聽并改變 scale 值即可。重點是利用 deltaY 值的正負來判斷滾輪是朝上還是朝下:
let origin='center'
let scale=1
// 注冊事件
mask.addEventListener('mousewheel', zoom, { passive: false })
// 滾輪縮放
const zoom=(event)=> {
if (!event.deltaY) {
return
}
event.preventDefault()
origin=`${event.offsetX}px ${event.offsetY}px`
if (event.deltaY < 0) {
scale +=0.1 // 放大
} else if (event.deltaY > 0) {
scale >=0.2 && (scale -=0.1) // 縮小
}
changeStyle(cloneEl, ['transition: all .15s', `transform-origin: ${origin}`, `transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`])
}
因為縮放始終都以圖片中心為原點進行縮放,這顯然不符合我們的操作習慣,所以在上面的代碼中,我們通過鼠標當前的偏移量即 offsetX、offsetY 的值改變 transform-origin 來動態設置縮放的原點,效果如下:
乍一看好像沒什么問題,事實上如果鼠標不斷移動且幅度很大時會出現抖動,需要消除原點位置突然改變帶來的影響才能完全解決這個問題(起初我并未發現,后面在做移動端縮放時簡直是災難級體驗)而由于PC上問題并不明顯,這里先按下不表,后面會詳細提到。
由于縮放導致圖像發生變化,我們自然地想到要靠移動來觀察圖片,此時體現在PC端的是按住鼠標拖拽,移動端則是手指點擊滑動,而兩者各自的事件監聽顯然并不共通,如以移動事件為例,PC端對應的是 mousemove 事件而移動端則是 touchmove 事件,這就意味著如果我們要做到兼容多端,就必須注冊多個事件監聽。
那么有沒有一種事件可以做到同時監聽鼠標操作和手指操作呢?答案是有的!那就是 指針事件(Pointer events),它被設計出來就是為了便于提供更加一致與良好的體驗,無需關心不同用戶和場景在輸入硬件上的差異。接下來我們就以此事件為基礎來完成各項操作功能。
指針 是輸入設備的硬件層抽象(比如鼠標,觸摸筆,或觸摸屏上的一個觸摸點),它能指向一個具體表面(如屏幕)上的一個(或一組)坐標,可以表示包括接觸點的位置,引發事件的設備類型,接觸表面受到的壓力等。
PointerEvent 接口繼承了所有 MouseEvent 中的屬性,以保障原有為鼠標事件所開發的內容能更加有效的遷移到指針事件。
移動圖片的實現是比較簡單的,在每次指針按下時我們記錄 clientX、clientY 為初始值,移動時計算當前的值與初始點位的差值加到 translate 偏移量中即可。需要注意的是每次移動事件結束時都必須更新初始點位,否則膨脹的偏移距離會使圖片加速逃逸可視區域。另外當抬起動作結束時,會觸發 click 事件,所以注意加入全局變量標記以及定時器進行一些判斷處理。
let startPoint={ x: 0, y: 0 } // 記錄初始觸摸點位
let isTouching=false // 標記是否正在移動
let isMove=false // 正在移動中,與點擊做區別
// 鼠標/手指按下
window.addEventListener('pointerdown', function (e) {
e.preventDefault()
isTouching=true
startPoint={ x: e.clientX, y: e.clientY }
})
// 鼠標/手指抬起
window.addEventListener('pointerup', function (e) {
isTouching=false
setTimeout(()=> {
isMove=false
}, 300);
})
// 鼠標/手指移動
window.addEventListener('pointermove', (e)=> {
if (isTouching) {
isMove=true
// 單指滑動/鼠標移動
offset={
left: offset.left + (e.clientX - startPoint.x),
top: offset.top + (e.clientY - startPoint.y),
}
changeStyle(cloneEl, [`transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`, `transform-origin: ${origin}`])
// 注意移動完也要更新初始點位,否則圖片會加速逃逸可視區域
startPoint={ x: e.clientX, y: e.clientY }
}
})
在 TouchEvent 的事件對象中,我們可以找到 touches 這個數組,在移動端通常都是利用這個數組來判斷觸點個數的,例如 touches.length > 1 即是多點操作,這是我們實現雙指縮放的基礎。但在 指針事件 中卻找不到類似的對象(MDN對其描述只是擴展了 MouseEvent 的接口),想來 Touch 對象只服務于 TouchEvent 這點其實也可以理解,所以要自己實現對觸摸點數的記錄。
這里我們使用 Map 數組對觸摸點進行記錄(通過這個實例你可以看到 Map 數組純 api 操作增刪改有多么優雅)。其中我們利用 pointerId 標識觸摸點,移動事件中根據事件對象的 pointerId 來更新對應觸點(指針)的數據,當觸點抬起時則從Map中刪除點位:
let touches=new Map() // 觸摸點數組
window.addEventListener('pointerdown', function (e) {
e.preventDefault()
touches.set(e.pointerId, e) // TODO: 點擊存入觸摸點
isTouching=true
startPoint={ x: e.clientX, y: e.clientY }
if (touches.size===2) {
// TODO: 判斷雙指觸摸,并立即記錄初始數據
}
})
window.addEventListener('pointerup', function (e) {
touches.delete(e.pointerId) // TODO: 抬起時移除觸摸點
// .....
})
window.addEventListener('pointermove', (e)=> {
if (isTouching) {
isMove=true
if (touches.size < 2) {
// TODO: 單指滑動,或鼠標拖拽
} else {
// TODO: 雙指縮放
touches.set(e.pointerId, e) // 更新點位數據
// .......
}
}
})
Map 是二維數組,可以利用 Array.from 轉成普通數組即可通過 index 下標取值。
簡單在手機瀏覽器上測試后發現,這個數組偶爾會不停增加(例如在滑動頁面時),也就是 pointerup 會出現不能正確刪除對應點位的情況,或者說被意外中斷了,此時會觸發 pointercancel 事件監聽(對應 touchcancel 事件),我們必須在這里清空數組,這是容易被忽略的一點,原本 TouchEvent 中的 touches 并不需要處理。
window.addEventListener('pointercancel', function (e) {
touches.clear() // 可能存在特定事件導致中斷,所以需要清空
})
現在我們有了對觸摸點判斷的基礎,就可以開始實現縮放了,當雙指接觸屏幕時,記錄兩點間距離作為初始值,當雙指在屏幕上捏合,兩點間距不停發生變化,此時存在一個變化比例=當前距離 / 初始距離,該比例作為改變 scale 的系數就能得到新的縮放值。
在上一篇文章手寫拖拽效果中我也講到了如何在JS中使用數學方法計算兩點間距離,下面介紹另一種常見的簡潔寫法,Math.hypot() 函數返回其參數的平方和的平方根:
// 坐標點1: start,坐標點2: end,求兩點距離:
Math.hypot(end.x - start.x, end.y - start.y)
// 所以為什么上面代碼可以計算兩點距離,因為等價于:
Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2))
回到代碼中,直接取出 touches 的前兩個點位,于兩點間獲取距離:
// 獲取距離
function getDistance() {
const touchArr=Array.from(touches)
if (touchArr.length < 2) {
return 0
}
const start=touchArr[0][1]
const end=touchArr[1][1]
return Math.hypot(end.x - start.x, end.y - start.y)
}
繼續完善上面的事件監聽代碼:
let touches=new Map() // 觸摸點數組
let lastDistance=0 // 記錄最后的雙指初始距離
let lastScale=1 // 記錄下最后的縮放值
window.addEventListener('pointerdown', function (e) {
// .........
if (touches.size===2) { // TODO: 判斷雙指觸摸,并立即記錄初始數據
lastDistance=getDistance()
lastScale=scale // 把當前的縮放值存起來
}
})
window.addEventListener('pointerup', function (e) {
// .........
if (touches.size <=0) {
// .........
} else {
const touchArr=Array.from(touches)
// 雙指如果抬起了一個,可能還有單指停留在觸屏上繼續滑動,所以更新點位
startPoint={ x: touchArr[0][1].clientX, y: touchArr[0][1].clientY }
}
// .......
})
window.addEventListener('pointermove', (e)=> {
e.preventDefault()
if (isTouching) {
isMove=true
if (touches.size < 2) { // 單指滑動
// .......
} else {
// 雙指縮放
touches.set(e.pointerId, e)
const ratio=getDistance() / lastDistance // 比較距離得出比例
scale=ratio * lastScale // 修改新的縮放值
changeStyle(cloneEl, ['transition: all 0s', `transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`, `transform-origin: ${origin}`])
}
}
})
以上僅是實現了縮放的處理,而縮放原點還在默認的圖片中心,就和PC端一樣我們還要改變原點才顯得自然,對于雙指縮放來說,改變的只是兩點間距離,無論雙指間距如何改變,兩點連成的線段中心點是不會變的,所以我們只要通過兩點求出中心點坐標然后設置為縮放原點坐標即可:
window.addEventListener('pointermove', (e)=> {
// .......
// 雙指縮放
const ratio=getDistance() / lastDistance // 比較距離得出比例
scale=ratio * lastScale // 修改新的縮放值
const touchArr=Array.from(touches)
const start=touchArr[0][1]
const end=touchArr[1][1]
x=(start.offsetX + end.offsetX) / 2
y=(start.offsetY + end.offsetY) / 2
origin=`${x}px ${y}px`
changeStyle(cloneEl, ['transition: all 0s', `transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`, `transform-origin: ${origin}`])
// ........
})
這時縮放感覺是沒有問題了,但是每當往屏幕中的不同位置再多進行幾次操作時,圖片會突然間閃動一下位置,到最后幾乎不受控制。
這就回到前面提到的,原點位置突然改變帶來的偏移量引起了圖片位置的閃動,這段偏移是如何產生的呢?我們畫兩張圖看下,在原點變化的前后圖像的坐標點發生了哪些變化:
如上圖,原點為 O 時,我們取右下角的點設為點 A,圖像放大2倍時 A 點變換到 B 點。
而當原點突然變為 O’ 時,點 A 在圖像放大2倍時則變換到了 B' 點。
我們可以把圖像的偏移抽象為圖像某個點位的偏移,這樣問題就變成了計算 BB' 的距離:
設原點 **O=(Ox , Oy)**,點 A=(x, y),縮放值為 s,OA 向量乘縮放倍數得出 OB 的向量:
點 B 坐標就等于 OB 向量加上原點 O 的坐標:
同理得出點 B' 的坐標:
BB' 的距離就是兩點相減后的結果,兩點已在上面得出,代入計算過程這里就不多寫了,最終化簡的結果如下:
在進行縮放時我們主動改變 scale 的值,那么 s 是已知的,雙指落下時是我們主動改變了縮放原點,(Ox , Oy) 和 (O'x , O'y) 這兩個點也是已知的,那么根據上面的式子就可以得出 BB' 的實際距離了,也就是圖像的偏移量。這么說有點抽象,我們還是回到代碼中,在雙指縮放時將這個偏移量減掉,同樣的在PC端的縮放中,我們也加入對偏移量的修正:
let scaleOrigin={ x: 0, y: 0, }
// 獲取中心改變的偏差
function getOffsetCorrection(x=0, y=0) {
const touchArr=Array.from(touches)
if (touchArr.length===2) {
const start=touchArr[0][1]
const end=touchArr[1][1]
x=(start.offsetX + end.offsetX) / 2
y=(start.offsetY + end.offsetY) / 2
}
origin=`${x}px ${y}px`
const offsetLeft=(scale - 1) * (x - scaleOrigin.x) + offset.left
const offsetTop=(scale - 1) * (y - scaleOrigin.y) + offset.top
scaleOrigin={ x, y }
return { left: offsetLeft, top: offsetTop }
}
window.addEventListener('pointermove', (e)=> {
// .......
// 雙指縮放
touches.set(e.pointerId, e)
const ratio=getDistance() / lastDistance
scale=ratio * lastScale
offset=getOffsetCorrection()
changeStyle(cloneEl, ['transition: all 0s', `transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`, `transform-origin: ${origin}`])
// ........
})
// 滾輪縮放
const zoom=(event)=> {
// ........
offset=getOffsetCorrection(event.offsetX, event.offsetY)
changeStyle(cloneEl, ['transition: all .15s', `transform-origin: ${origin}`, `transform: translate(${offset.left + 'px'}, ${offset.top + 'px'}) scale(${scale})`])
}
最終雙指縮放效果如下,啊~ 如此絲滑,不由得流下兩行熱淚
有些在正文中未提及,但同樣重要的小細節。
雖然瀏覽器滾動對應的其實是 scroll 事件,但我們在PC上滾動通常都是用利用滾輪(筆記本觸控板也被視作滾輪),所以在滾輪事件中阻止系統默認事件也就阻止了滾動,但不是完全阻止,因為滾動條沒隱藏的話還是可以拖動來滾動頁面的,在本文例子中并沒有針對滾動做什么處理,如果需要完全禁止滾動,應該在打開彈窗時為 body 設置 overflow 為 'hidden'。
注意滾輪事件(wheel)是可以觸發冒泡捕獲的,而滾動事件(scroll)卻無法觸發冒泡,了解更多可以看我在掘金的一篇文章:面試官:說說哪些瀏覽器事件不會冒泡 - 掘金。
至于移動端又是為什么阻止了滾動呢?得益于一個強大的CSS屬性,可能在開頭布局部分你就發現了這個屬性,沒錯,這里為彈層遮罩設置了 touch-action: none; 從而阻止了所有手勢效果,自然也就不會發生頁面滾動。該屬性在平時的業務代碼中也可用于優化移動端性能、解決 touchmove 的 passive 報錯等,這個我在之前另一篇文章中有提到,感興趣可以看看:學會一行CSS即可提升頁面滾動性能 - 掘金。
在本例的代碼中這個CSS本身是沒有意義的,為的只是觸發css3硬件加速來提升性能,那為什么不直接使用 translate3d() 呢?又或者使用 will-change: transform; 來告訴瀏覽器提升渲染性能呢?
答案是后兩者都會使移動端的圖片變模糊?。ˋndroid似乎不會)起初我發現圖片在手機上模糊的問題時,調試很久都沒定位到源頭,一籌莫展之際想起以前做H5網頁常使用 vant 框架,就想要不看看它源碼中的圖片預覽組件吧,很快我找到相關代碼位置,代碼截圖:
從代碼片段中我看到vant并沒有使用任何translate3d或scale3d屬性,看來是真的有坑了。其實我們使用translate3d提升性能也是把第三個參數一直設置為0(2d平面沒有Z軸)來實現的,這和translateZ(0)是等價的。 但奇怪的是單獨設置translateZ卻沒有引發問題。。
will-change 這個屬性,我也是最近無意中發現的,根據 MDN 文檔的描述,該屬性是用于提升性能的最后手段,怎么理解這句話呢?根據上面實踐的結論來看,應該可以認為是瀏覽器嘗試犧牲掉一些畫面質量來換取性能提升的一種手段。
相關鏈接:JS圖片預覽/查看效果(兼容PC與H5) - 碼上掘金
以上就是文章的全部內容,感謝看到這里,希望對你有所幫助或啟發!創作不易,如果覺得文章寫得不錯,可以點贊收藏支持一下,也歡迎關注,我會更新更多實用的前端知識與技巧。我是茶無味的一天,期待與你共同成長~
一、項目背景】
生活中經常會見到很多gif圖,那么gif圖到底是什么?GIF是一種位圖。簡單來說就是通過每一張張靜圖,通過控制它的關鍵幀,從而達到靜態圖動起來的效果。
這種GIF圖的效果,也可以用html+CSS3結合來做。
【二、項目目標】
完成GIF圖的制作。
【三、項目分析】
1、分析圖片。打開其中一張圖。
2、可以看到這張圖有45張不一樣動作的靜態圖合成。有點擊屬性。如圖所示:
看到這張照片是7020*156,一共有45幀。高度不變,寬度7020/45幀,就可以把每一幀的內容顯示出來。
【四、項目準備】
1、圖片:準備自己的喜歡的GIF靜態長圖,保存在文件夾。
2、軟件:Dreamweaver。
【五、項目實現】
1、創建div 存放圖片和文件,添加class屬性。
<body>
<div class="box">
<div class="box2">
</div>
</div>
</body>
2、添加CSS樣式
1) 設置box的寬、高、位置、背景顏色。
.box{
width: 300px;
height: 300px;
background: #ccc;
position: absolute;
left: 0px;
top: 0;
}
2)加載圖片,設置寬、高,-webkit-animation動畫效果。
.box2{
width: 156px;
height: 156px;
background: url("fox45.png");
-webkit-animation:aa 3s steps(45) infinite ;
}
@-webkit-keyframes aa{
100%{
background-position: -7020px 0;
}
}
CSS3 animation屬性中的steps實現GIF動圖(逐幀動畫)
steps(45)表示讓整個動畫在45個關鍵幀之間切換。這個松鼠的圖片中
包含了45幀,所以這里設置了45。而且我們的動畫時長是3s,也就是說每一幀
停留1s,這就和普通的GIF動圖達到了一樣的效果。
【六、效果展示】
1、點擊F12運行到瀏覽器。
2、點擊圖片,效果如下。
【七、總結】
1、本項目,就gif圖遇到的一些難點進行了分析及提供解決方案。
2、html+css也可以做出網站頁面的效果,在上面顯示圖片標題的地方不能用絕對定位,于是用的relative定位,這個地方是布局的核心部分。
3、按照操作步驟,自己嘗試去做。自己實現的時候,總會有各種各樣的問題,切勿眼高手低,勤動手,才可以理解的更加深刻。
4、需要本文源碼的小伙伴,后臺回復“GIF圖”四個字,即可獲取。
****看完本文有收獲?請轉發分享給更多的人****
IT共享之家
入群請在微信后臺回復【入群】
想學習更多Python網絡爬蟲與數據挖掘知識,可前往專業網站:http://pdcfighting.com/
紹一個比較常見的動畫效果。
在日常開發中,為了強調凸顯某些文本或者元素,會加一些掃光動效,起到吸引眼球的效果,比如文本的
或者是一個卡片容器,里面可能是圖片或者文本或者任意元素
除此之外,還有那種不規則的圖片,比如一張獎品圖片,只會在圖片本身出現掃光,透明度地方則不會
這些是如何實現的呢?一起看看吧
CSS掃光動畫的原理很簡單,就是一個普通的、從左到右的、無限循環的位移動畫
位移動畫可以選擇transform或者改變background-position都行。
至于掃光,我們只需要繪制一條斜向上45deg的線性漸變就可以了,示意如下
用CSS實現就是
background: linear-gradient(45deg, rgba(255,255,255,0) 40%, rgba(255, 255, 255, 0.7), rgba(255,255,255,0) 60%);
準備工作做好了,下面看 3 種不同場景的實現
首先來看文本掃光。
由于掃光在文本內部,所以需要將這個漸變作為文本的顏色。文本漸變色,可以用backgrond-clip:text來實現,假設HTML是這樣的
<h1 class="shark-txt">前端偵探</h1>
為了讓效果看起來更加明顯,我們用一個比較粗的字體
h1{
font-size: 60px;
font-family: "RZGFDHDHJ";
font-weight: normal;
color: #9747FF;
}
效果如下
現在我們通過background-clip來添加掃光,由于是裁剪背景,所以需要將當前文本顏色設置透明,建議通過-webkit-text-fill-color: transparent來設置,這樣可以保留文本原有顏色,好處是其他地方,比如background-color可以直接使用原有文本顏色currentColor,具體實現如下
.shark-txt{
-webkit-text-fill-color: transparent;
background: linear-gradient(45deg, rgba(255,255,255,0) 40%, rgba(255, 255, 255, 0.7), rgba(255,255,255,0) 60%) -100%/50% no-repeat currentColor;
-webkit-background-clip: text;
}
效果如下
最后就是讓這個掃光動起來了。
由于是在文本內部,所以這里可以通過改變background-position來實現掃光動畫了,動畫很簡單,如下
@keyframes shark-txt {
form{
background-position: -100%;
}
to {
background-position: 200%;
}
}
但是這樣做沒有動畫效果,完全不會動。
這是因為背景默認尺寸是100%,根據背景偏移百分比的計算規則,當背景尺寸等于容器尺寸時,百分比完全失效,具體規則如下
給定背景圖像位置的百分比偏移量是相對于容器的。值 0% 表示背景圖像的左(或上)邊界與容器的相應左(或上)邊界對齊,或者說圖像的 0% 標記將位于容器的 0% 標記上。值為 100% 表示背景圖像的 右(或 下)邊界與容器的 右(或 下)邊界對齊,或者說圖像的 100% 標記將位于容器的 100% 標記上。因此 50% 的值表示水平或垂直居中背景圖像,因為圖像的 50% 將位于容器的 50% 標記處。類似的,background-position: 25% 75% 表示圖像上的左側 25% 和頂部 75% 的位置將放置在距容器左側 25% 和距容器頂部 75% 的容器位置。
developer.mozilla.org/zh-CN/docs/…
(container width - image width) * (position x%)=(x offset value)
(container height - image height) * (position y%)=(y offset value)
所以這種情況下,我們可以手動改小一點背景尺寸,比如50%
.shark-txt {
-webkit-text-fill-color: transparent;
background: linear-gradient(45deg, rgba(255, 255, 255, 0) 40%, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0) 60%) -100% / 50% no-repeat currentColor;
-webkit-background-clip: text;
animation: shark-txt 2s infinite;
}
這樣就能完美實現文本掃光效果了
還有一種比較常見的是容器內的掃光動效,通常是在一個圓角矩形的容器里。
像這種情況下就不能直接用背景漸變了,因為會被容器內的其他元素覆蓋。所以我們需要創建一個偽元素,然后通過改變偽元素的位移來實現掃光動畫了。
假設有一個容器,容器內有一張圖片,HTML如下
<div class="shark-wrap card">
<img src="https://imgservices-1252317822.image.myqcloud.com/coco/b11272023/ececa9a5.7y0amw.jpg">
</div>
簡單修飾一下
.card{
width: 300px;
border-radius: 8px;
background-color: #FFE8A3;
}
.card img{
display: block;
width: 100%;
}
效果如下
下面通過偽元素來創建一個掃光層,設置位移動畫
.shark-wrap::after{
content: '';
position: absolute;
inset: -20%;
background: linear-gradient(45deg, rgba(255,255,255,0) 40%, rgba(255, 255, 255, 0.7), rgba(255,255,255,0) 60%);
animation: shark-wrap 2s infinite;
transform: translateX(-100%);
}
@keyframes shark-wrap {
to {
transform: translateX(100%);
}
}
效果如下
最后直接超出隱藏就行了
.shark-wrap{
overflow: hidden;
}
最終效果如下
也適合那種圓形頭像
其實前面兩種情況已經適合大部分場景了,其實還有一種情況,就是那種不規則的圖片掃光。這種圖片無法直接通過overflow:hidden去隱藏多余部分,比如這樣
很明顯在圖片之外的地方也出現了掃光,無法做到掃光在圖形的"內部"。
那么,有沒有辦法根據圖片的外形去裁剪呢?當然也是有辦法的,這里需要用到CSS mask遮罩。
簡單來說,就是直接將該圖片作為遮罩圖片,這樣只有形狀內的部分可見,形狀外的直接被裁剪了
在上一種場景的情況下,只需要在此基礎之上,添加一個完全相同的 mask遮罩就行了
<div class="shark-wrap" style="-webkit-mask: url(https://imgservices-1252317822.image.myqcloud.com/coco/s09252023/3af9e8de.00uqxe.png) 0 0/100%">
<img class="logo" src="https://imgservices-1252317822.image.myqcloud.com/coco/s09252023/3af9e8de.00uqxe.png">
</div>
這樣就可以把掃光多余的部分裁剪掉了
換張圖也能很好適配
以上所有 demo 可以查看以下鏈接
以上就本文的全部內容了,共介紹了3種不同的掃光場景,你學到了嗎?下面總結一下重點
*請認真填寫需求信息,我們會在24小時內與您取得聯系。