JavaScript利用canvas實現(xiàn)炫酷的碎片切圖效果
前言
今天分享一個炫酷的碎片式切圖效果,這個其實在自己的之前的博客上有實現(xiàn)過,本人覺得這個效果還是挺炫酷的,這次還是用我們的canvas來實現(xiàn),代碼量不多,但有些地方還是需要花點時間去理解的,需要點數(shù)學幾何理解能力,老規(guī)矩,我們還是先看效果再來看實現(xiàn)步驟。

需求分析
從上面我們看到圖片在切換的時候其實是一個一個的小碎片慢慢從點擊位置往外擴散開來,這一個個小碎片,在頁面中其實就是一個個的小方塊。這里的難點在于如何將一張完整的圖片切割成一個一個的小方塊分別進行渲染,還有就是這個棱形圖案的位置確定。
- 切割:這里我們可以以坐標系的形式來進行切割,每一個方塊都對應著它們自己在坐標系中的位置(x, y)
- 繪制:這里的重點在于drawImage方法
- 棱形擴散:這里需要點數(shù)學幾何理解能力,后面作圖理解
實現(xiàn)過程
坐標系
在實現(xiàn)之前,我們先來理解一個概念:「坐標系」
注意:這里所說的坐標系不是我們數(shù)學中的坐標系,但兩者又有些類似,不同點在于兩者的原點位置以及y軸的方向不同。

切割
這一步主要是為了確定每一個單元格的大小,單元格的長寬最好不要是最大公約數(shù)或最小公約數(shù),因為過大效果不夠炫,過小性能會有壓力。
我這里畫板長寬為 800 * 530 ,選取 16 * 15 為單元尺寸,即整個畫布由 50 * 35 共 1750 個單元格組成。切割分完單元格之后我們需要先計算一些基本的參數(shù)備用。
this.imgW?=?800;?//?圖片原始寬 this.imgH?=?530;?//?圖片原始高 this.conW?=?800;?//?畫布寬 this.conH?=?530;?//??畫布高 this.dw?=?16;?//?單元格寬 this.dh?=?15;?//?單元格高 this.I?=?this.conH?/?this.dh;?//單元行數(shù) this.J?=?this.conW?/?this.dw;?//?單元列數(shù) this.DW?=?this.imgW?/?this.J;?//?原圖單元寬 this.DH?=?this.imgH?/?this.I;?//?原圖單元高
「行數(shù) = 畫布高度 / 單元格高度;列數(shù) = 畫面寬度 / 單元格寬度」
繪制
本次繪制的重點在于drawImage這個方法,我們可以先來了解一下這個方法的參數(shù)及功能
drawImage
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
這個方法一共有9個參數(shù),作用是在畫布上繪制圖像??吹竭@么多參數(shù)是不是已經(jīng)被勸退了,哈哈
- 「image」:繪制到上下文的元素。允許任何的畫布圖像源,例如:
HTMLImageElement、SVGImageElement、HTMLVideoElement、HTMLCanvasElement、ImageBitmap、OffscreenCanvas或VideoFrame。 - 「sx」:(可選)需要繪制到目標上下文中的,
image的矩形(裁剪)選擇框的左上角 X 軸坐標??梢允褂?3 參數(shù)或 5 參數(shù)語法來省略這個參數(shù)。 - 「s y」:(可選)需要繪制到目標上下文中的,
image的矩形(裁剪)選擇框的左上角 Y 軸坐標??梢允褂?3 參數(shù)或 5 參數(shù)語法來省略這個參數(shù)。 - 「sWidth」:(可選)需要繪制到目標上下文中的,
image的矩形(裁剪)選擇框的寬度。如果不說明,整個矩形(裁剪)從坐標的sx和sy開始,到image的右下角結束??梢允褂?3 參數(shù)或 5 參數(shù)語法來省略這個參數(shù)。使用負值將翻轉這個圖像。 - 「sHeight」:(可選)需要繪制到目標上下文中的,
image的矩形(裁剪)選擇框的高度。使用負值將翻轉這個圖像。 - 「dx」:
image的左上角在目標畫布上 X 軸坐標。 - 「dy」:
image的左上角在目標畫布上 Y 軸坐標。 - 「dWidth」:
image在目標畫布上繪制的寬度。允許對繪制的image進行縮放。如果不說明,在繪制時image寬度不會縮放。注意,這個參數(shù)不包含在 3 參數(shù)語法中。 - 「dHeight」:
image在目標畫布上繪制的高度。允許對繪制的image進行縮放。如果不說明,在繪制時image高度不會縮放。注意,這個參數(shù)不包含在 3 參數(shù)語法中。
這9個參數(shù)我們可以這樣來記憶,第一個參數(shù)是圖像源,接下來的四個參數(shù)指的是原圖,最后四個參數(shù)指的是畫布
切割&渲染
這里我們主要是將一張圖片切割成一個個的小碎片,是這些碎片拼起來就是一張完整的圖片。
class?ChipBanner?{
??constructor()?{
????this.cvs?=?document.querySelector("#chip");
????this.ctx?=?this.cvs.getContext("2d");
????this.imgList?=?document.querySelectorAll(".bg");
????this.imgIndex?=?0;
????this.isAnimating?=?false;
????this.imgW?=?800;?//圖片原始寬/高
????this.imgH?=?530;
????this.conW?=?800;?//畫布寬/高
????this.conH?=?530;
????this.dw?=?16;?//畫布單元寬/高
????this.dh?=?15;
????this.I?=?this.conH?/?this.dh;?//單元行/列數(shù)
????this.J?=?this.conW?/?this.dw;
????this.DW?=?this.imgW?/?this.J;?//原圖單元寬/高
????this.DH?=?this.imgH?/?this.I;
??}
??init()?{
????this.ctx.beginPath();
????for?(let?i?=?0;?i?<?this.I;?i++)?{
??????for?(let?j?=?0;?j?<?this.J;?j++)?{
????????this.chipDraw(this.imgList[this.imgIndex],?i,?j);
??????}
????}
????this.ctx.closePath();
????this.ctx.stroke();
??}
??drawText()?{
????this.ctx.font?=?"150px?serif";
????this.ctx.strokeStyle?=?"white";
????this.ctx.strokeText("1024",?500,?500);
??}
??chipDraw(img,?i,?j)?{
????this.drawText();
????//負責繪制,i:?單元行號;j:?單元列號
????this.ctx.drawImage(
??????img,
??????this.DW?*?j,
??????this.DH?*?i,
??????this.DW,
??????this.DH,
??????this.dw?*?j,
??????this.dh?*?i,
??????this.dw,
??????this.dh
????);
??}
}
這里正確拼出來看到的和正常圖片沒有任何區(qū)別

再來看一張拼錯的圖

剛開始幾何坐標那里沒寫對,拼出來就成這樣了,哈哈,看著就像動畫幀卡住的樣子。
動畫
這里主要是要找出某個點周圍棱形范圍內(nèi)的所有點的坐標,然后在清除這些坐標圖案的同時,開始繪制下一張圖片。
「菱形線上的點與坐標的 行號差值的絕對值 + 列號差值的絕對值 = 距離」
找出坐標棱形范圍內(nèi)所有的點
countAround(i,?j,?dst)?{
????let?arr?=?[];
????for?(let?m?=?i?-?dst;?m?<=?i?+?dst;?m++)?{
??????for?(let?n?=?j?-?dst;?n?<=?j?+?dst;?n++)?{
????????if?(
??????????Math.abs(m?-?i)?+?Math.abs(n?-?j)?==?dst?&&
??????????m?>=?0?&&
??????????n?>=?0?&&
??????????m?<=?this.I?-?1?&&
??????????n?<=?this.J?-?1
????????)?{
??????????arr.push({?x:?m,?y:?n?});
????????}
??????}
????}
????return?arr;
??}
清除單元格畫布
chipClear(i,?j)?{
????this.ctx.clearRect(this.dw?*?j,?this.dh?*?i,?this.dw,?this.dh);
}
合并&動畫
start(i,?j)?{
????if?(this.isAnimating)?return;
????this.isAnimating?=?true;
????this.imgIndex++;
????if?(this.imgIndex?>?this.imgList.length?-?1)?this.imgIndex?=?0;
????let?_this?=?this,
??????dst?=?0,
??????timer?=?setInterval(()?=>?{
????????let?resArr?=?_this.countAround(i,?j,?dst);
????????resArr.forEach((item)?=>?{
??????????_this.chipClear(item.x,?item.y);??//?清除單元格
??????????_this.chipDraw(_this.imgList[_this.imgIndex],?item.x,?item.y);?//?繪制下一張圖片
????????});
????????if?(!resArr.length)?{
??????????clearInterval(timer);
??????????_this.isAnimating?=?false;
????????}
????????dst++;
??????},?30);
??}
大功告成,這樣就實現(xiàn)了一個炫酷的碎片式切圖效果了~

到此這篇關于JavaScript利用canvas實現(xiàn)炫酷的碎片切圖效果的文章就介紹到這了,更多相關JavaScript canvas碎片切圖效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
原生JS實現(xiàn)輪播效果+學前端的感受(防止走火入魔)
下面小編就為大家?guī)硪黄鶭S實現(xiàn)輪播效果+學前端的感受(防止走火入魔)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-08-08
解決layui的form里的元素進行動態(tài)生成,驗證失效的問題
今天小編就為大家分享一篇解決layui的form里的元素進行動態(tài)生成,驗證失效的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09
兼容IE,firefox的獲取節(jié)點的文本值的javascript代碼
javascript獲取節(jié)點的文本值,已經(jīng)考慮了兼容性。大家可以放心使用。注意了這里的兼容沒有使用innerText,如果要使用兼容innerText,請參考腳本之家以前發(fā)布的文章。2009-12-12
JS中使用new Date(str)創(chuàng)建時間對象不兼容firefox和ie的解決方法(兩種)
這篇文章主要介紹了JS中使用new Date(str)創(chuàng)建時間對象不兼容firefox和ie的解決方法的相關資料,需要的朋友可以參考下2016-12-12

