今回はPixiJSを使って画像にグリッチエフェクトを付与する方法をざっくり書いていこうと思います。
他にも方法はあると思いますが何かの参考になれば。
必要なもの
今回のやり方で必要なのはPixiJS本体とPixiJS Filtersの二つです。
グリッチエフェクトはPixiJS Filtersにある既存のものを使います。
import * as PIXI from 'pixi.js';
import * as filters from 'pixi-filters';
HTMLとCSS
今回のエフェクトではimgタグに付けたclassをターゲットとして付与します。HTMLとCSSの記述自体はシンプルな者です。
ただし対象のDOMがabsoluteやfixedなどでレイアウトされてる場合はうまくいかない事があると思うので注意してください。
<div class="vfxwrap"><img class="vfx" src="photo.jpg"></div>
.vfxwrap{
position: relative;
}
.vfx{
visibility: hidden;
}
.glitch_canvas{
position: absolute;
display: block;
font-size: 0;
pointer-events:none;
}
画像の位置をデザインする際は.vfxwrapに対してstyleを当てて行く事になります。
.vfxに対してhiddenをかけているのは画像の見た目自体はcanvasに描画しているのでimgタグ自体は見えなくしておくためです。
.glitch_canvasへのstyleはPixiJSで追加するcanvasサイズの拡張に対応するためのものになります。
JavaScriptの記述
必要な事としては、
・DOMから画像情報を取得
・PixiJSでcanvasに画像を描画、それを元のDOMの位置に追加
・それらのcanvasにグリッチフィルターを付与
基本的な考え方はこんな感じになります。細かいところはだいたいコメントに書いておきました。
// 画像のサイズを取得してcanvasを生成
let vfxdom = document.querySelectorAll('.vfx');
let vfxlist = new Array;
let applist = new Array;
let imgloadcount = 0; // 画像読み込み用のカウント
const padding = 80; // グリッチでDOMからはみ出す値
// 画像が読み込まれたらの処理
for (let i = 0; i < vfxdom.length; i++) {
let img = new Image();
img.onload = function() {
imgloadcount++;
if (imgloadcount === vfxdom.length) {
setCanvasDom();
}
}
img.src = vfxdom[i].src;
}
function setCanvasDom(){
for (let i = 0; i < vfxdom.length; i++) {
const clientRect = vfxdom[i].getBoundingClientRect();
const w = clientRect.width;
const h = clientRect.height;
// PIXI appを定義
let app = new PIXI.Application({
width: w + padding * 2,
height: h + padding * 2,
antialias: true,
transparent: true
});
// canvasのDOMを配置するための枠を作成
const canvasWrap = document.createElement("div");
canvasWrap.setAttribute('class','glitch_canvas');
canvasWrap.setAttribute('style','margin-left:'+padding*-1+'px;margin-top:'+padding*-1+'px;');
canvasWrap.appendChild(app.view); // 作成したcanvasにpixi appを追加
// そのcanvasを対象のDOMの前に配置
vfxdom[i].parentNode.insertBefore(canvasWrap, vfxdom[i]);
// canvasに転写する画像情報をセット
const srcdata = vfxdom[i].src;
vfxlist.push(PIXI.Sprite.from(srcdata));
vfxlist[i].x = padding;
vfxlist[i].y = padding;
vfxlist[i].width = w;
vfxlist[i].height = h;
// 図形を描画する為のContainerを定義。画像Spriteをcontainerに追加。
const container = new PIXI.Container();
container.addChild(vfxlist[i]);
// Containerをappに追加
app.stage.addChild(container);
applist.push(app);
}
canvasRenderer();
}
function canvasRenderer(){
for (let i = 0; i < applist.length; i++) {
let counter = 0;
// PIXI appのループ用
applist[i].ticker.add(function(time) {
applist[i].ticker.update(time);
counter++;
if (0 < counter && counter < 12) {
const filter_glitch = new filters.GlitchFilter({
slices:5,
offset:80,
direction:10*time,
fillMode:0,
average:false,
seed:0,
red:[0,0],
blue:[0,0],
green:[0,0],
});
filter_glitch.padding = 100;
vfxlist[i].filters = [filter_glitch];
}else if(counter === 13){
const _filter_glitch = new filters.GlitchFilter({
slices:0,
offset:0,
direction:0,
fillMode:1,
average:false,
seed:0,
red:[0,0],
blue:[0,0],
green:[0,0],
});
vfxlist[i].filters = [_filter_glitch];
}else if(200 < counter){
counter = 0;
}
});
} // endfor
}
// リサイズの場合の処理
window.addEventListener("resize", function(event) {
for (let i = 0; i < applist.length; i++) {
// 現在のDOMを取得
const clientRect = vfxdom[i].getBoundingClientRect();
const w = clientRect.width;
const h = clientRect.height;
// PIXI Spriteのサイズを変更
vfxlist[i].width = w;
vfxlist[i].height = h;
// canvasのサイズを変更
applist[i].renderer.resize(w + padding * 2, h + padding * 2);
}
});
一見長いですがやってる事は割とシンプルなのでそこまで難しくはないはずです。今回はグリッチフィルターを設定していますが、その他にもいろいろなフィルターがあるのでそれらを試してみるのも楽しいと思いますよ。
※WPプラグインのバグでJavaScript内の半角「&」が正しく出ず文字コード「&」になってしまうので、実際に動かすときはそこだけ半角&に直してください。
HTML5 Canvas