使用JS前端技術(shù)實(shí)現(xiàn)靜態(tài)圖片局部流動(dòng)效果
聲明:本文涉及圖文和模型素材僅用于個(gè)人學(xué)習(xí)、研究和欣賞,請(qǐng)勿二次修改、非法傳播、轉(zhuǎn)載、出版、商用、及進(jìn)行其他獲利行為。
背景
如果你有玩過(guò) ?? 《王者榮耀》、《陰陽(yáng)師》 等手游,一定注意到過(guò)它的啟動(dòng)動(dòng)畫(huà)、皮膚立繪卡片等場(chǎng)景,經(jīng)常采用靜態(tài)底圖加局部液態(tài)流動(dòng)效果的簡(jiǎn)單動(dòng)畫(huà),這些流動(dòng)動(dòng)畫(huà)可能出現(xiàn)在緩緩流動(dòng)的水流 ??、迎風(fēng)飄動(dòng)的旗幟 ??、游戲角色衣袖 ??♀?、隨著時(shí)間緩動(dòng)的云、雨、霧天氣效果 ? 等。這種過(guò)渡效果不僅節(jié)省了開(kāi)發(fā)全量動(dòng)畫(huà)的成本,而且使得游戲畫(huà)面更加熱血、冒險(xiǎn)、奧德賽、高級(jí),也更加容易吸引玩家氪金 ??。
本文使用前端開(kāi)發(fā)技術(shù),結(jié)合 SVG 和 CSS 來(lái)實(shí)現(xiàn)類似的液化流動(dòng)效果。本文包含的知識(shí)點(diǎn)主要包括:mask-image 遮罩、feTurbulence 和 feDisplacementMap 濾鏡、filter 屬性、canvas 繪制方法、TimelineMax 動(dòng)畫(huà)以及input[type=file] 本地圖片資源加載等。
效果
先來(lái)看看實(shí)現(xiàn)效果,下面幾個(gè)示例以及 ?? 文章 Banner 圖都是應(yīng)用了由本文內(nèi)容生成的液態(tài)流動(dòng)動(dòng)畫(huà)效果。由于GIF 圖壓縮比較嚴(yán)重,動(dòng)畫(huà)效果看起來(lái)不是很流暢 ??,大家不妨通過(guò)以下演示頁(yè)面鏈接,親自體驗(yàn)一下效果,生成自己的 傳說(shuō)、典藏 皮膚立繪吧 ??。
???? 在線體驗(yàn):https://dragonir.github.io/paint-heat-map/
???? 在線體驗(yàn):https://codepen.io/dragonir/full/qBoxQKW
?? 霧氣擴(kuò)散 塞爾達(dá)傳說(shuō):曠野之息

?? 衣袖飄動(dòng) 貂蟬:貓影幻舞

?? 湖光波動(dòng)

?? 文字液化

?? ps:體驗(yàn)頁(yè)面部署在 Gitpage 上傳圖片功能不是真正上傳到服務(wù)器,而是只會(huì)加載到瀏覽器本地,頁(yè)面不會(huì)獲取任何信息,大家可以放心體驗(yàn),不用擔(dān)心隱私泄漏問(wèn)題。
實(shí)現(xiàn)
頁(yè)面主要由 2 部分構(gòu)成,頂部用于加載圖片 ,并且可以通過(guò)按住 ?? 鼠標(biāo)劃動(dòng)的方式繪制熱點(diǎn)路徑,給圖片添加流動(dòng)效果;底部是控制區(qū)域,點(diǎn)擊按鈕 ?? 清除畫(huà)布,可以清除繪制的流動(dòng)動(dòng)畫(huà)效果、點(diǎn)擊按鈕 ?? 切換圖片可以加載本地的圖片。

?? 注意,還有一個(gè)隱形的功能,當(dāng)你繪制完成時(shí),可以點(diǎn)擊 ?? 鼠標(biāo)右鍵,然后選擇保存圖片,保存的這張圖片就是我們繪制流體動(dòng)畫(huà)路徑的熱點(diǎn)圖,利用這張熱點(diǎn)圖,使用本文的 CSS 知識(shí),就能把靜態(tài)圖片轉(zhuǎn)化成動(dòng)態(tài)圖啦!
HTML 頁(yè)面結(jié)構(gòu)
#sketch 元素主要是用于繪制和加載流動(dòng)效果熱點(diǎn)圖的畫(huà)板;#button_container 是頁(yè)面底部的按鈕控制區(qū)域;svg 元素用于利用其 filter 濾鏡實(shí)現(xiàn)液態(tài)流動(dòng)動(dòng)畫(huà)效果,包括 feTurbulence 和 feDisplacementMap 濾鏡。
<main id="sketch">
<canvas id="canvas" data-img=""></canvas>
<div class="mask">
<div id="maskInner" class="mask-inner"></div>
</div>
</main>
<section class="button_container">
<button class="button">清除畫(huà)布</button>
<button class="button"><input class="input" type="file" id="upload">上傳圖片</button>
</section>
<svg>
<filter id="heat" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
<feTurbulence id="heatturb" type="fractalNoise" numOctaves="1" seed="2" />
<feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="22" in="SourceGraphic" />
</filter>
</svg>
?? feTurbulence 和 feDisplacementMap
feTurbulence:濾鏡利用Perlin噪聲函數(shù)創(chuàng)建了一個(gè)圖像,利用它可以實(shí)現(xiàn)人造紋理比如說(shuō)云紋、大理石紋等模擬濾鏡效果。feDisplacementMap:映射置換濾鏡,該濾鏡用來(lái)自圖像中從in2到空間的像素值置換圖像從in到空間的像素值。即它可以改變?cè)睾蛨D形的像素位置,通過(guò)遍歷原圖形的所有像素點(diǎn),feDisplacementMap重新映射替換一個(gè)新的位置,形成一個(gè)新的圖形。該濾鏡在業(yè)界的主流應(yīng)用是對(duì)圖形進(jìn)行形變,扭曲,液化。
CSS 樣式
接著看看樣式的實(shí)現(xiàn),main 元素作為主容器并將主圖案作為背景圖片;canvas 作為畫(huà)布占據(jù) 100% 的空間位置;.mask 和 .mask-inner 用于生成如下圖所示熱點(diǎn)路徑與背景圖相溶的效果,這種效果是借助 mask-image 實(shí)現(xiàn)的。最后,為了生成動(dòng)態(tài)流動(dòng)效果,.mask-inner 通過(guò) filter: url(#heat) 將前面生成的 svg 作為濾鏡來(lái)源,后續(xù)即將在 JavaScript 中通過(guò)不間斷修改 svg 濾鏡的屬性,來(lái)生成液態(tài)流動(dòng)動(dòng)畫(huà)。
main {
position: relative;
background-image: url('bg.jpg');
background-size: cover;
background-position: 100% 50%;
}
canvas {
opacity: 0;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.mask {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
mask-mode: luminance;
mask-size: 100% 100%;
backdrop-filter: hard-light;
mask-image: url('mask.png');
}
.mask-inner {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('bg.jpg') 0% 0% repeat;
background-size: cover;
background-position: 100% 50%;
filter: url(#heat);
mask-image: url('mask.png')
}

?? mask-image
mask-image CSS 屬性用于設(shè)置元素上遮罩層的圖像。
語(yǔ)法:
// 默認(rèn)值,透明的黑色圖像層,也就是沒(méi)有遮罩層。 mask-image: none; // <mask-source><mask>或CSS圖像的url的值 mask-image: url(masks.svg#mask1); // <image> 圖片作為遮罩層 mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent); mask-image: image(url(mask.png), skyblue); // 多個(gè)值 mask-image: image(url(mask.png), skyblue), linear-gradient(rgba(0, 0, 0, 1.0), transparent); // 全局值 mask-image: inherit; mask-image: initial; mask-image: unset;
兼容性:

? 此功能某些瀏覽器尚在開(kāi)發(fā)中,需要使用瀏覽器前綴以兼容不同瀏覽器。
JavaScript 方法
① 繪制熱點(diǎn)圖
監(jiān)聽(tīng)鼠標(biāo)移動(dòng)和點(diǎn)擊事件,在 canvas 上繪制波動(dòng)路徑熱點(diǎn)。
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var sketch = document.getElementById('sketch');
var sketchStyle = window.getComputedStyle(sketch);
var mouse = { x: 0, y: 0 };
canvas.width = parseInt(sketchStyle.getPropertyValue('width'));
canvas.height = parseInt(sketchStyle.getPropertyValue('height'));
canvas.addEventListener('mousemove', e => {
mouse.x = e.pageX - canvas.getBoundingClientRect().left;
mouse.y = e.pageY - canvas.getBoundingClientRect().top;
}, false);
ctx.lineWidth = 40;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.strokeStyle = 'black';
canvas.addEventListener('mousedown', () => {
ctx.beginPath();
ctx.moveTo(mouse.x, mouse.y);
canvas.addEventListener('mousemove', onPaint, false);
}, false);
canvas.addEventListener('mouseup', () => {
canvas.removeEventListener('mousemove', onPaint, false);
}, false);
var onPaint = () => {
ctx.lineTo(mouse.x, mouse.y);
ctx.stroke();
var url = canvas.toDataURL();
document.querySelectorAll('div').forEach(item => {
item.style.cssText += `
display: initial;
-webkit-mask-image: url(${url});
mask-image: url(${url});
`;
});
};
繪制完成后,可以在頁(yè)面中右鍵保存生成的波動(dòng)路徑熱點(diǎn)圖,直接將繪制滿意的熱點(diǎn)圖放到 CSS 中,就能給喜歡的圖片添加局部波動(dòng)效果了,下面這張圖片就是本示例頁(yè)面使用的波動(dòng)的熱點(diǎn)路徑圖。

② 生成動(dòng)畫(huà)
為了生成實(shí)時(shí)更新的波動(dòng)效果,本文使用了 TweenMax 來(lái)通過(guò)改變 feTurbulence 的 baseFrequency 屬性值來(lái)實(shí)現(xiàn),使用其他動(dòng)畫(huà)庫(kù)或使用 requestAnimationFrame 也是可以實(shí)現(xiàn)相同的功能。
feTurb = document.querySelector('#heatturb');
var timeline = new TimelineMax({
repeat: -1,
yoyo: true
}),
timeline.add(
new TweenMax.to(feTurb, 8, {
onUpdate: () => {
var bfX = this.progress() * 0.01 + 0.025,
bfY = this.progress() * 0.003 + 0.01,
bfStr = bfX.toString() + ' ' + bfY.toString();
feTurb.setAttribute('baseFrequency', bfStr);
}
}),
0);
③ 清除畫(huà)布
點(diǎn)擊清除畫(huà)布按鈕,可以清空已經(jīng)繪制的波動(dòng)路徑,主要是通過(guò)清除頁(yè)面元素 mask-image 的屬性值以及清 canvas 畫(huà)布來(lái)實(shí)現(xiàn)的。
function clear() {
document.querySelectorAll('div').forEach(item => {
item.style.cssText += `
display: none;
-webkit-mask-image: none;
mask-image: none;
`;
});
}
document.querySelectorAll('.button').forEach(item => {
item.addEventListener('click', () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
clear();
})
});
④ 切換圖片
點(diǎn)擊切換圖片,可以加載本地的一張圖片作為繪制底圖,該功能是通過(guò) input[type=file] 來(lái)實(shí)現(xiàn)圖片資源的獲取,然后通過(guò)修改 CSS 將它設(shè)置成新的畫(huà)布背景。
document.getElementById('upload').onchange = function () {
var imageFile = this.files[0];
var newImg = window.URL.createObjectURL(imageFile);
clear();
document.getElementById('sketch').style.cssText += `
background: url(${newImg});
background-size: cover;
background-position: center;
`;
document.getElementById('maskInner').style.cssText += `
background: url(${newImg});
background-size: cover;
background-position: center;
`;
};
到這里,全部功能都實(shí)現(xiàn)完畢了,大家趕快制作一張自己喜歡的 史詩(shī)皮膚 或 奧德賽小游戲 的啟動(dòng)頁(yè)面吧 ??。

?? 源碼地址:https://github.com/dragonir/paint-heat-map
總結(jié)
本文包含的新知識(shí)點(diǎn)主要包括:
mask-image遮罩元素feTurbulence和feDisplacementMapsvg濾鏡filter屬性Canvas繪制方法TimelineMax動(dòng)畫(huà)input[type=file]本地圖片資源加載
到此這篇關(guān)于使用JS前端技術(shù)實(shí)現(xiàn)靜態(tài)圖片局部流動(dòng)效果的文章就介紹到這了,更多相關(guān)js靜態(tài)圖片局部流動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決bootstrap下拉菜單點(diǎn)擊立即隱藏bug的方法
本篇文章主要介紹了解決bootstrap下拉菜單點(diǎn)擊立即隱藏bug的方法,具有一定的參考價(jià)值,有興趣的可以了解一下2017-06-06
js實(shí)現(xiàn)可拖動(dòng)DIV的方法
這篇文章主要介紹了js實(shí)現(xiàn)可拖動(dòng)DIV的方法,有需要的朋友可以參考一下2013-12-12
微信小程序中多個(gè)頁(yè)面?zhèn)鲄⑼ㄐ诺膶W(xué)習(xí)與實(shí)踐
剛接觸微信小程序,對(duì)里面的語(yǔ)法和屬性還不怎么了解,最近正在努力學(xué)習(xí)中,下面這篇文章主要給大家介紹了微信小程序中多個(gè)頁(yè)面?zhèn)鲄⑼ㄐ诺南嚓P(guān)資料,是最近學(xué)習(xí)的一個(gè)內(nèi)容總結(jié),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-05-05
Javascript中Microtask和Macrotask鮮為人知的知識(shí)點(diǎn)
這篇文章主要為大家介紹了Javascript中Microtask和Macrotask鮮為人知的知識(shí)點(diǎn)講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
JavaScript獲取兩個(gè)數(shù)組交集的方法
這篇文章主要介紹了JavaScript獲取兩個(gè)數(shù)組交集的方法,涉及javascript針對(duì)數(shù)組的相關(guān)操作技巧,需要的朋友可以參考下2015-06-06
javascript執(zhí)行環(huán)境及作用域詳解
這篇文章主要為大家詳細(xì)介紹了javascript執(zhí)行環(huán)境及作用域,分別針對(duì)javascript執(zhí)行環(huán)境及作用域進(jìn)行探討,感興趣的小伙伴們可以參考一下2016-05-05
js實(shí)現(xiàn)文章目錄索引導(dǎo)航(table of content)
這篇文章主要介紹了js實(shí)現(xiàn)文章目錄索引導(dǎo)航(table of content),需要的朋友可以參考下2020-05-05
JS實(shí)現(xiàn)顏色的10進(jìn)制轉(zhuǎn)化成rgba格式的方法
這篇文章主要介紹了JS實(shí)現(xiàn)顏色的10進(jìn)制轉(zhuǎn)化成rgba格式的方法,涉及javascript針對(duì)顏色數(shù)值轉(zhuǎn)換的相關(guān)運(yùn)算操作技巧,需要的朋友可以參考下2017-09-09

