body {
margin: 0;
}
canvas {
position: absolute;
}
const frag = `
precision highp float;
varying vec2 vTextureCoord;
uniform vec4 inputSize;
uniform sampler2D uT0;
uniform vec2 uT0_Size;
uniform sampler2D uT1;
uniform vec2 uT1_Size;
uniform float uProgress;
uniform float uStrokeWidth;
float hash21(vec2 p) {
p = fract(p*vec2(1.34, 435.345));
p += dot(p, p+34.23);
return fract(p.x*p.y);
}
vec2 coverUV(vec2 planeSize, vec2 imageSize, vec2 texCoodr, vec2 pivot) {
vec2 ratio = planeSize / imageSize;
vec2 scale = ratio / max(ratio.x, ratio.y);
return (texCoodr - pivot) * scale + pivot;
}
void main(void) {
vec2 grid = vec2(6.0, 4.0);
vec2 gUV = fract(vTextureCoord.xy * grid);
vec2 gID = floor(vTextureCoord.xy * grid);
float offs = abs(1.0 + hash21(gID));
float progress = clamp(mix(-offs, 1.0 + offs, uProgress), 0.0, 1.0);
float angle = progress * 3.1415926;
float x = (gUV.x - 0.5) / cos(angle) + 0.5;
float z = (x - 0.5) * sin(angle);
float sy = mix(1.0, 0.35, z);
float y = (gUV.y - 0.5) / sy + 0.5;
vec2 cellUV = vec2(x, y);
if (progress > 0.5) cellUV.x = 1.0 - cellUV.x;
vec2 visible = step(abs(cellUV - 0.5), vec2(0.5) + inputSize.zw * 0.5);
float mask = min(visible.x, visible.y);
float sp = mix(-1.0, 2.0, uProgress);
if (sp < 0.0) sp = mix(1.0, 0.9, abs(sp));
else if (sp < 1.0) sp = 1.0;
else sp = mix(1.0, 0.9, sp - 1.0);
vec2 uv = (gID + cellUV) / grid;
uv = (uv - 0.5) * sp + 0.5;
vec2 uv0 = coverUV(inputSize.xy, uT0_Size.xy, uv, vec2(0.5));
vec2 uv1 = coverUV(inputSize.xy, uT1_Size.xy, uv, vec2(0.5));
vec2 lw = uStrokeWidth * (0.5 - abs(progress - 0.5)) * inputSize.zw * grid * 0.5;
vec2 line = step(abs(gUV - 0.5), vec2(0.5) - lw + inputSize.zw * 0.5);
float lines = 1.0 - min(line.x, line.y);
vec4 lineColor = vec4(0.15, 0.15, 0.15, 0.75) * lines;
vec4 t0 = texture2D(uT0, uv0);
vec4 t1 = texture2D(uT1, uv1);
vec4 tex = mix(t0, t1, step(0.5, progress));
vec4 color = mix(vec4(0.0), tex, mask);
gl_FragColor = vec4(mix(color.rgb, lineColor.rgb, lineColor.a), 1.0);
}`;
const app = new PIXI.Application({
autoStar: false,
resizeTo: window
});
document.body.appendChild(app.view);
const background = PIXI.Sprite.from(PIXI.Texture.WHITE);
background.width = app.screen.width;
background.height = app.screen.height;
app.stage.addChild(background);
const filter = new PIXI.Filter(null, frag, {
uProgress: 0,
uStrokeWidth: 10,
uT0: PIXI.Texture.WHITE,
uT0_Size: [1, 1],
uT1: PIXI.Texture.WHITE,
uT1_Size: [1, 1],
});
background.filters = [filter];
let time = 0;
app.ticker.add((delta) => {
time += delta;
let p = time / 60 % 10 / 10;
p = 1 - Math.abs(p - 0.5) * 2;
filter.uniforms.uProgress = p;
background.width = app.screen.width;
background.height = app.screen.height;
});
const loaderImageOptions = {
loadType: PIXI.LoaderResource.LOAD_TYPE.IMAGE,
};
PIXI.Loader.shared
.add('img0', 'https://images.unsplash.com/photo-1526336024174-e58f5cdd8e13?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NXx8Y2F0fGVufDB8fDB8&ixlib=rb-1.2.1&auto=format&fit=crop&w=640&q=80', loaderImageOptions)
.add('img1', 'https://images.unsplash.com/photo-1569591159212-b02ea8a9f239?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=640&q=80', loaderImageOptions)
.load((loader, resources) => {
const tex0 = resources.img0.texture;
filter.uniforms.uT0 = tex0;
filter.uniforms.uT0_Size = [tex0.baseTexture.width, tex0.baseTexture.height];
const tex1 = resources.img1.texture;
filter.uniforms.uT1 = tex1;
filter.uniforms.uT1_Size = [tex1.baseTexture.width, tex1.baseTexture.height];
app.start();
});
This Pen doesn't use any external CSS resources.