引言
在三維視覺化中,會涉及到很多動畫,其中貼圖動畫是其中很重要的一種,本文介紹幾種貼圖動畫的思路,供大家一起探討。
流動動畫
流動動畫通過設定貼圖的repeat屬性,並不斷改變貼圖物件的offset讓貼圖產生流動效果。 這種動畫不難實現,首先載入貼圖,如下所示:
let img = new Image();
img.src = './images/path.png';
let texture = new eg.Texture(img);
img.onload = function () {
texture.needsUpdate = true;
}
texture.repeat.set(100,1);
tube.material.map= texture;
function render(){
tube.material.map.wrapS = eg.RepeatWrapping;
tube.material.map.offset.set(offset,0);
tube.material.map.needsUpdate = true;
offset += 0.01;
}
上面程式碼,實現了一個tube(管道),然後給管道加了一個貼圖texture。 在渲染的時候,不斷更新texture物件的offset的值,此時就可以生產流動的動畫。如下圖所示:
雪碧圖動畫(Sprite Sheet)
圖集也就是常說的雪碧圖,就是把一系列小圖按照一定的佈局放到一張大圖上面。 在使用的時候,擷取大圖的一部分來獲取某個小圖。 這在web端是一種常用的手段,通常用於減少圖片數量,從而降低網路請求數量。
通過雪碧圖的方式,可以把動畫的系列動作的每一幀都佈局在雪碧圖上。 然後通過雪碧圖建立texture物件,設定貼圖的repeat和offset,讓每次繪製獲取雪碧圖上的某一幀影像,不斷改變offset,就可以形成繪製不同幀的動畫效果。比如下面的圖片:
下面這個threejs的demo,就是這樣的效果,所以此處不再贅述程式碼,有興趣的讀者可以檢視demo的原始碼。
https://stemkoski.github.io/T... 效果如下圖所示:
GIF動畫
gif圖片本身自帶動畫,如果gif放到Image物件上,動畫會自動播放,只是當把gif作為貼圖物件的圖片的時候。 不會自動播放動畫。
要自動播放gif動畫,需要使用解析gif的庫,把gif圖片的每一幀解析出來, 並把每一幀影像繪製到一個canvas上,把canvas作為貼圖物件的圖片。大致程式碼如下:
載入gif圖片,並解析圖片。其中解析圖片用到了一個庫omggif,利用裡面的GifReader可以解析gif圖片的幀資料:
import { GifReader } from 'omggif';
const loader = new FileLoader(this.manager);
loader.setPath(this.path);
loader.setResponseType('arraybuffer');
loader.load(url, (response) => {
const gifData = new Uint8Array(response);
const reader = new GifReader(gifData);
if (onLoad) onLoad(reader);
}, onProgress, onError);
然後不斷的更新貼圖的影像:
draw() {
if (!this.reader) {
return;
}
const { reader, image, context } = this;
const { width, height } = image;
const frameNum = ++this.frameNumber % reader.numFrames();
const frameInfo = reader.frameInfo(frameNum);
if (frameNum === 0) {
// always clear canvas to start
context.clearRect(0, 0, width, height);
} else if (this.previousFrameInfo && this.previousFrameInfo.disposal === 2) {
// disposal was "restore to background" which is essentially "restore to transparent"
context.clearRect(this.previousFrameInfo.x,
this.previousFrameInfo.y,
this.previousFrameInfo.width,
this.previousFrameInfo.height);
}
const imageData = context.getImageData(0, 0, width, height);
reader.decodeAndBlitFrameRGBA(frameNum, imageData.data);
context.putImageData(imageData, 0, 0);
this.needsUpdate = true;
this.previousFrameInfo = frameInfo;
this.timeoutId = setTimeout(this.draw.bind(this), (frameInfo.delay || 2) * 10);
}
最終的gif貼圖效果如下圖所示
APNG動畫
APNG圖片和gif圖片是類似的,也是動畫圖片。 不過相對於gif來說。APNG可以設定半透明,邊緣鋸齒不嚴重,所以使用APNG的圖片的效果要優於gif圖片。
原理上類似,也是解析APNG圖片,然後把沒一幀一次繪製到canvas上,並不斷更新texture物件。 解析APNG圖片,使用了一個開源庫,APNG-canvas。 有興趣讀者可以自行研究,此處不重點講述。
解析完成後,可以把解析的幀集合進行繪製,程式碼如下:
draw() {
if (!this.reader) {
return;
}
const { reader, image, context } = this;
const { width, height } = image;
const frameNum = ++this.frameNumber % reader.numFrames;
const frameInfo = reader.frames[frameNum];
if (frameNum === 0) {
// always clear canvas to start
context.clearRect(0, 0, width, height);
// } else if (this.previousFrameInfo && this.previousFrameInfo.disposal === 2) {
} else if (this.previousFrameInfo) {
// disposal was "restore to background" which is essentially "restore to transparent"
context.clearRect(this.previousFrameInfo.left,
this.previousFrameInfo.top,
this.previousFrameInfo.width,
this.previousFrameInfo.height);
}
const imageData = context.getImageData(0, 0, width, height);
// reader.decodeAndBlitFrameRGBA(frameNum, imageData.data);
// context.putImageData(imageData, 0, 0);
context.drawImage(frameInfo.img,frameInfo.left,frameInfo.top,frameInfo.width,frameInfo.height);
this.needsUpdate = true;
this.previousFrameInfo = frameInfo;
this.timeoutId = setTimeout(this.draw.bind(this), frameInfo.delay);
最終的apng貼圖效果如下圖所示
總結
本文介紹了 theejs 貼圖動畫的多種實現思路。 包括 紋理流動,雪碧圖,gif和apng動畫。 通過這些動畫能力,可以建立出豐富多彩的視覺化效果。
如果對視覺化感興趣,可以和我交流,微信541002349(可入微信群)。
關注公號“ITMan彪叔” 可以及時收到更多有價值的文章。