利用JavaScript實(shí)現(xiàn)靜態(tài)圖片局部流動(dòng)效果
背景
如果你有玩過《王者榮耀》、《陰陽師》 等手游,一定注意到過它的啟動(dòng)動(dòng)畫、皮膚立繪卡片等場景,經(jīng)常采用靜態(tài)底圖加局部液態(tài)流動(dòng)效果的簡單動(dòng)畫,這些流動(dòng)動(dòng)畫可能出現(xiàn)在緩緩流動(dòng)的水流 、迎風(fēng)飄動(dòng)的旗幟 、游戲角色衣袖 、隨著時(shí)間緩動(dòng)的云、雨、霧天氣效果 等。這種過渡效果不僅節(jié)省了開發(fā)全量動(dòng)畫的成本,而且使得游戲畫面更加熱血、冒險(xiǎn)、奧德賽、高級(jí),也更加容易吸引玩家氪金 。
本文使用前端開發(fā)技術(shù),結(jié)合 SVG
和 CSS
來實(shí)現(xiàn)類似的液化流動(dòng)效果。本文包含的知識(shí)點(diǎn)主要包括:mask-image
遮罩、feTurbulence
和 feDisplacementMap
濾鏡、filter
屬性、canvas
繪制方法、TimelineMax
動(dòng)畫以及input[type=file]
本地圖片資源加載等。
效果
先來看看實(shí)現(xiàn)效果,下面幾個(gè)示例以及文章 Banner 圖都是應(yīng)用了由本文內(nèi)容生成的液態(tài)流動(dòng)動(dòng)畫效果。由于GIF 圖壓縮比較嚴(yán)重,動(dòng)畫效果看起來不是很流暢 ,大家不妨通過以下演示頁面鏈接,親自體驗(yàn)一下效果,生成自己的 傳說、典藏 皮膚立繪吧 。
- 在線體驗(yàn):https://dragonir.github.io/paint-heat-map/
- 在線體驗(yàn):https://codepen.io/dragonir/full/qBoxQKW
霧氣擴(kuò)散 塞爾達(dá)傳說:曠野之息
衣袖飄動(dòng) 貂蟬:貓影幻舞
湖光波動(dòng)
文字液化
ps:體驗(yàn)頁面部署在 Gitpage
上傳圖片功能不是真正上傳到服務(wù)器,而是只會(huì)加載到瀏覽器本地,頁面不會(huì)獲取任何信息,大家可以放心體驗(yàn),不用擔(dān)心隱私泄漏問題。
實(shí)現(xiàn)
頁面主要由2部分構(gòu)成,頂部用于加載圖片 ,并且可以通過按住鼠標(biāo)劃動(dòng)的方式繪制熱點(diǎn)路徑,給圖片添加流動(dòng)效果;底部是控制區(qū)域,點(diǎn)擊按鈕清除畫布,可以清除繪制的流動(dòng)動(dòng)畫效果、點(diǎn)擊按鈕切換圖片可以加載本地的圖片。
注意,還有一個(gè)隱形的功能,當(dāng)你繪制完成時(shí),可以點(diǎn)擊鼠標(biāo)右鍵,然后選擇保存圖片,保存的這張圖片就是我們繪制流體動(dòng)畫路徑的熱點(diǎn)圖,利用這張熱點(diǎn)圖,使用本文的 CSS
知識(shí),就能把靜態(tài)圖片轉(zhuǎn)化成動(dòng)態(tài)圖啦!
HTML 頁面結(jié)構(gòu)
#sketch
元素主要是用于繪制和加載流動(dòng)效果熱點(diǎn)圖的畫板;#button_container
是頁面底部的按鈕控制區(qū)域;svg
元素用于利用其 filter
濾鏡實(shí)現(xiàn)液態(tài)流動(dòng)動(dòng)畫效果,包括 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">清除畫布</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)人造紋理比如說云紋、大理石紋等模擬濾鏡效果。feDisplacementMap
:映射置換濾鏡,該濾鏡用來自圖像中從in2
到空間的像素值置換圖像從in
到空間的像素值。即它可以改變元素和圖形的像素位置,通過遍歷原圖形的所有像素點(diǎn),feDisplacementMap
重新映射替換一個(gè)新的位置,形成一個(gè)新的圖形。該濾鏡在業(yè)界的主流應(yīng)用是對(duì)圖形進(jìn)行形變,扭曲,液化。
CSS 樣式
接著看看樣式的實(shí)現(xiàn),main
元素作為主容器并將主圖案作為背景圖片;canvas
作為畫布占據(jù) 100%
的空間位置;.mask
和 .mask-inner
用于生成如下圖所示熱點(diǎn)路徑與背景圖相溶的效果,這種效果是借助 mask-image
實(shí)現(xiàn)的。最后,為了生成動(dòng)態(tài)流動(dòng)效果,.mask-inner
通過 filter: url(#heat)
將前面生成的 svg
作為濾鏡來源,后續(xù)即將在 JavaScript
中通過不間斷修改 svg
濾鏡的屬性,來生成液態(tài)流動(dòng)動(dòng)畫。
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è)置元素上遮罩層的圖像。
語法:
// 默認(rèn)值,透明的黑色圖像層,也就是沒有遮罩層。 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;
兼容性:
此功能某些瀏覽器尚在開發(fā)中,需要使用瀏覽器前綴以兼容不同瀏覽器。
JavaScript 方法
① 繪制熱點(diǎn)圖
監(jiān)聽鼠標(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}); `; }); };
繪制完成后,可以在頁面中右鍵保存生成的波動(dòng)路徑熱點(diǎn)圖,直接將繪制滿意的熱點(diǎn)圖放到 CSS
中,就能給喜歡的圖片添加局部波動(dòng)效果了,下面這張圖片就是本示例頁面使用的波動(dòng)的熱點(diǎn)路徑圖。
② 生成動(dòng)畫
為了生成實(shí)時(shí)更新的波動(dòng)效果,本文使用了 TweenMax
來通過改變 feTurbulence
的 baseFrequency
屬性值來實(shí)現(xiàn),使用其他動(dòng)畫庫或使用 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);
③ 清除畫布
點(diǎn)擊清除畫布按鈕,可以清空已經(jīng)繪制的波動(dòng)路徑,主要是通過清除頁面元素 mask-image
的屬性值以及清 canvas
畫布來實(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)擊切換圖片,可以加載本地的一張圖片作為繪制底圖,該功能是通過 input[type=file]
來實(shí)現(xiàn)圖片資源的獲取,然后通過修改 CSS
將它設(shè)置成新的畫布背景。
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)完畢了,大家趕快制作一張自己喜歡的 史詩皮膚 或 奧德賽小游戲 的啟動(dòng)頁面吧。
源碼地址:https://github.com/dragonir/paint-heat-map
總結(jié)
本文包含的新知識(shí)點(diǎn)主要包括:
mask-image
遮罩元素feTurbulence
和feDisplacementMap
svg
濾鏡filter
屬性Canvas
繪制方法TimelineMax
動(dòng)畫input[type=file]
本地圖片資源加載
到此這篇關(guān)于利用JavaScript實(shí)現(xiàn)靜態(tài)圖片局部流動(dòng)效果的文章就介紹到這了,更多相關(guān)JavaScript圖片局部流動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Three.js使用OrbitControls后修改相機(jī)旋轉(zhuǎn)方向無效解決辦法
three.js是用javascript寫的基于webGL的第三方3D庫,下面這篇文章主要給大家介紹了關(guān)于Three.js使用OrbitControls后修改相機(jī)旋轉(zhuǎn)方向無效的解決辦法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01js根據(jù)屬性刪除對(duì)象數(shù)組里的相應(yīng)對(duì)象
這篇文章主要介紹了js根據(jù)屬性刪除對(duì)象數(shù)組里的相應(yīng)對(duì)象,需要的朋友可以參考下2023-07-07javascript 字符 Escape,encodeURI,encodeURIComponent
下面是對(duì)字符串編碼轉(zhuǎn)換功能函數(shù)大家,大家可以根據(jù)實(shí)際需要選擇使用。2009-07-07JavaScript實(shí)現(xiàn)事件的中斷傳播和行為阻止方法示例
這篇文章主要給大家介紹了利用JavaScript實(shí)現(xiàn)事件的中斷傳播和行為阻止的方法示例,文中給出了詳細(xì)的介紹和示例代碼,相信對(duì)大家的理解和學(xué)習(xí)具有一定的參考借鑒價(jià)值,需要的朋友們下面來一起看看吧。2017-01-01利用谷歌地圖API獲取點(diǎn)與點(diǎn)的距離的js代碼
下面是一段利用谷歌地圖API獲取點(diǎn)點(diǎn)之間距離的代碼,采用V3版本中的DirectionsService()2012-10-10原生js實(shí)現(xiàn)移動(dòng)端Touch輪播圖的方法步驟
這篇文章主要介紹了原生js實(shí)現(xiàn)移動(dòng)端Touch輪播圖的方法步驟,touch輪播圖其實(shí)就是通過手指的滑動(dòng),來左右切換輪播圖,touch輪播圖其實(shí)就是通過手指的滑動(dòng),來左右切換輪播圖,2019-01-01