今天分享一個炫酷的碎片式切圖效果,這個其實在自己的之前的部落格上有實現過,本人覺得這個效果還是挺炫酷的,這次還是用我們的canvas來實現,程式碼量不多,但有些地方還是需要花點時間去理解的,需要點數學幾何理解能力,老規矩,我們還是先看效果再來看實現步驟。
如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖
第一時間獲取最新文章~
從上面我們看到圖片在切換的時候其實是一個一個的小碎片慢慢從點選位置往外擴散開來,這一個個小碎片,在頁面中其實就是一個個的小方塊。這裡的難點在於如何將一張完整的圖片切割成一個一個的小方塊分別進行渲染,還有就是這個稜形圖案的位置確定。
在實現之前,我們先來理解一個概念:座標系
注意:這裡所說的座標系不是我們數學中的座標系,但兩者又有些類似,不同點在於兩者的原點位置以及y軸的方向不同。
這一步主要是為了確定每一個單元格的大小,單元格的長寬最好不要是最大公約數或最小公約數,因為過大效果不夠炫,過小效能會有壓力。
我這裡畫板長寬為 800 * 530 ,選取 16 * 15 為單元尺寸,即整個畫布由 50 * 35 共 1750 個單元格組成。切割分完單元格之後我們需要先計算一些基本的引數備用。
this.imgW = 800; // 圖片原始寬
this.imgH = 530; // 圖片原始高
this.conW = 800; // 畫布寬
this.conH = 530; // 畫布高
this.dw = 16; // 單元格寬
this.dh = 15; // 單元格高
this.I = this.conH / this.dh; //單元行數
this.J = this.conW / this.dw; // 單元列數
this.DW = this.imgW / this.J; // 原圖單元寬
this.DH = this.imgH / this.I; // 原圖單元高
行數 = 畫布高度 / 單元格高度;列數 = 畫面寬度 / 單元格寬度
本次繪製的重點在於drawImage這個方法,我們可以先來了解一下這個方法的引數及功能
drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
這個方法一共有9個引數,作用是在畫布上繪製影象。看到這麼多引數是不是已經被勸退了,哈哈
HTMLImageElement
、SVGImageElement
、HTMLVideoElement
、HTMLCanvasElement
、ImageBitmap
、OffscreenCanvas
或 VideoFrame
。image
的矩形(裁剪)選擇框的左上角 X 軸座標。可以使用 3 引數或 5 引數語法來省略這個引數。image
的矩形(裁剪)選擇框的左上角 Y 軸座標。可以使用 3 引數或 5 引數語法來省略這個引數。image
的矩形(裁剪)選擇框的寬度。如果不說明,整個矩形(裁剪)從座標的 sx
和 sy
開始,到 image
的右下角結束。可以使用 3 引數或 5 引數語法來省略這個引數。使用負值將翻轉這個影象。image
的矩形(裁剪)選擇框的高度。使用負值將翻轉這個影象。image
的左上角在目標畫布上 X 軸座標。image
的左上角在目標畫布上 Y 軸座標。image
在目標畫布上繪製的寬度。允許對繪製的 image
進行縮放。如果不說明,在繪製時 image
寬度不會縮放。注意,這個引數不包含在 3 引數語法中。image
在目標畫布上繪製的高度。允許對繪製的 image
進行縮放。如果不說明,在繪製時 image
高度不會縮放。注意,這個引數不包含在 3 引數語法中。這9個引數我們可以這樣來記憶,第一個引數是影象源,接下來的四個引數指的是原圖,最後四個引數指的是畫布
這裡我們主要是將一張圖片切割成一個個的小碎片,是這些碎片拼起來就是一張完整的圖片。
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; //單元行/列數
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
);
}
}
這裡正確拼出來看到的和正常圖片沒有任何區別
再來看一張拼錯的圖
剛開始幾何座標那裡沒寫對,拼出來就成這樣了,哈哈,看著就像動畫幀卡住的樣子。
這裡主要是要找出某個點周圍稜形範圍內的所有點的座標,然後在清除這些座標圖案的同時,開始繪製下一張圖片。
菱形線上的點與座標的 行號差值的絕對值 + 列號差值的絕對值 = 距離
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);
}
大功告成,這樣就實現了一個炫酷的碎片式切圖效果了~
喜歡的同學歡迎點個贊呀,想要檢視原始碼的同學快來公眾號回覆碎片吧~
我是南玖,我們下期見!!!
-------------------------------------------
個性簽名:智者創造機會,強者把握機會,弱者坐等機會。做一個靈魂有趣的人!
如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖 第一時間獲取最新的文章~
歡迎加入前端技術交流群:928029210(QQ)
掃描下方二維條碼關注公眾號,回覆進群,拉你進前端學習交流群(WX),這裡有一群志同道合的前端小夥伴,交流技術、生活、內推、面經、摸魚,這裡都有哈,快來加入我們吧~ 回覆資料,獲取前端大量精選前端電子書及學習視訊~