Эффект искажения на three.js
Эффект перетекания одного изображения в другое на шейдерах, с gsap анимацией на канвасе
CSS
canvas{
position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
JS
Библиотеки three.js и GSAPhttps://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.min.js
https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.1/TweenMax.min.js
скрипт// シェーダーの作成
const vertexSrc = `
precision mediump float;
attribute vec4 position;
attribute vec2 uv;
varying vec2 vUv;
void main() {
gl_Position = position;
vUv = vec2( (position.x + 1.)/2., (-position.y + 1.)/2.);
}
`
const fragmentSrc = `
precision mediump float;
uniform float uTrans;
uniform sampler2D uTexture0;
uniform sampler2D uTexture1;
uniform sampler2D uDisp;
varying vec2 vUv;
float quarticInOut(float t) {
return t < 0.5
? +8.0 * pow(t, 4.0)
: -8.0 * pow(t - 1.0, 4.0) + 1.0;
}
void main() {
// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/gl_FragCoord.xhtml
vec4 disp = texture2D(uDisp, vec2(0., 0.5) + (vUv - vec2(0., 0.5)) * (0.2 + 0.8 * (1.0 - uTrans)) );
float trans = clamp(1.6 * uTrans - disp.r * 0.4 - vUv.x * 0.2, 0.0, 1.0);
trans = quarticInOut(trans);
vec4 color0 = texture2D(uTexture0, vec2(0.5 - 0.3 * trans, 0.5) + (vUv - vec2(0.5)) * (1.0 - 0.2 * trans));
vec4 color1 = texture2D(uTexture1, vec2(0.5 + sin( (1. - trans) * 0.1), 0.5 ) + (vUv - vec2(0.5)) * (0.9 + 0.1 * trans));
gl_FragColor = mix(color0, color1 , trans);
}
`;
// デモに使用する画像URL
const assetUrls = [
'https://s3-us-west-2.amazonaws.com/s.cdpn.io/13842/water.jpg',
'https://s3-us-west-2.amazonaws.com/s.cdpn.io/13842/water2.jpg',
'https://s3-us-west-2.amazonaws.com/s.cdpn.io/13842/disp.jpg'
];
/**
** 初期化開始
*/
// レンダラーの初期化
let renderer = new THREE.WebGLRenderer();
let canvas = renderer.domElement;
document.body.appendChild( canvas );
let scene = new THREE.Scene();
let obj = {trans: 0}
var cnt = 0;
let textureArr = [];
// カメラの初期化
let camera = new THREE.OrthographicCamera( -1, 1, 1, -1, 1, 1000 );
camera.position.z = 1;
// テクスチャの初期化
assetUrls.forEach( (url, index) =>{
let img = new Image();
let texture = new THREE.Texture();
texture.flipY= false;
textureArr.push(texture);
img.onload = function(_index, _img){
let texture = textureArr[_index];
texture.image = _img;
texture.needsUpdate = true;
cnt++;
if(cnt == 3) start();
}.bind(this, index, img);
img.crossOrigin = "Anonymous";
img.src = url;
})
let mat = new THREE.RawShaderMaterial( {
uniforms: {
uTrans: { value: obj.trans },
uTexture0: {value: textureArr[0]},
uTexture1: {value: textureArr[1]},
uDisp: {value: textureArr[2]},
},
vertexShader: vertexSrc,
fragmentShader: fragmentSrc
} );
let geo = new THREE.PlaneGeometry(2, 2);
let mesh = new THREE.Mesh(geo, mat)
scene.add(mesh);
resize();
function start(){
loop();
}
function loop(){
mat.uniforms.uTrans.value = obj.trans;
renderer.render(scene, camera);
requestAnimationFrame(loop);
}
function resize(){
let size = Math.min(window.innerWidth, window.innerHeight) * 0.8;
if(size > 450) size = 450;
renderer.setSize(size, size);
}
window.addEventListener("resize", function() {
resize();
});
canvas.addEventListener('mouseenter', function(){
TweenMax.killTweensOf(obj);
TweenMax.to(obj, 1.5, {trans: 1});
});
canvas.addEventListener('mouseleave', function(){
TweenMax.killTweensOf(obj);
TweenMax.to(obj, 1.5, {trans: 0});
});