Jump to content
Search Community

is it possible to make this animation in gsap? or Do I have to learn three.js as well

R007 test
Moderator Tag

Recommended Posts

Hey there!

 

GSAP is an animation library, it doesn't do any rendering (painting stuff onto the screen) It just animates things that are already painted.

So it's up to you to do the 'painting'. GSAP will animate pretty much whatever you throw at it, but you have to make those things first. They could be HTML elements, SVG graphics, three.js 3D models. Then you can animate them with GSAP.

So no, you can't create shader graphics with GSAP, but you can animate properties of three.js objects with GSAP.

 

People use GSAP and three.js together. It's a good toolset. Complimentary not supplementary.

Link to comment
Share on other sites

  • 2 weeks later...

Hello @R007 !
 

The image isn't animated. The image is fixe and each pixels are animated (distortioned) with THREE.js.

 

For do that, you need to mesh a custom Shader that uniform your image (called texture) and some data for your texture (called DataTexture) on a Plane Geometry Face. You can use the SharderMaterial of THREE.js for that.

 

Put a look into this code (result: https://www.awesomescreenshot.com/video/13995579?key=ff81768b65c3645d42370c8b649b2197 (just know, the result video is done on a computer with bad framerate... it's more smooth in real)) :

 

'use strict';
import * as THREE from './three/three.module.js';

export default class WaterEffect{
	
	constructor(options){

		this.options = options;
		this.scene = new THREE.Scene();

		this.container = this.options.dom;

		this.width = this.container.offsetWidth;
		this.height = this.container.offsetHeight;
		this.renderer = new THREE.WebGLRenderer();
		this.renderer.domElement.id = 'bg-water';
		this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
		this.renderer.setSize(this.width, this.height);
		//this.renderer.setClearColor(0xeeeeee, 1);
		this.renderer.physicallyCorrectLights = true;
		this.renderer.outputEncoding = THREE.sRGBEncoding;

		this.container.appendChild(this.renderer.domElement);

		const frustumSize = 1; 
		const aspect = window.innerWidth / window.innerHeight;

		this.camera = new THREE.OrthographicCamera(frustumSize / - 2, frustumSize / 2, frustumSize / 2, frustumSize / -2, -1000, 1000);

		this.camera.position.set(0, 0, 2);


		this.time = 0;

		this.mouse = {
			x: 0,
			y: 0,
			prevX: 0,
			prevY: 0,
			vX: 0,
			vY: 0
		}
	}

	addObjects(){

		this.size = 32;
		const width = this.size;
		const height = this.size;

		const size = width * height;
		const data = new Float32Array( 4 * size );

		for (let i = 0; i < size; i++) {

			let r = Math.random() * 10;
			const stride = i * 4;

			data[ stride ] = r;
			data[ stride + 1 ] = r;
			data[ stride + 2 ] = r;
			data[ stride + 3 ] = 255;

		}

		// used the buffer to create a DataTexture

		this.texture = new THREE.DataTexture( data, width, height, THREE.RGBAFormat, THREE.FloatType);
		
		this.texture.magFilter = this.texture.minFilter = THREE.LinearFilter; //THREE.NearestFilter;

		this.material = new THREE.ShaderMaterial({
			extensions: {
				derivatives: true
			},
			side: THREE.DoubleSide,
			uniforms: {
				time: {
					value: 0
				},
				resolution: {
					value: new THREE.Vector4()
				},
				uTexture: {
					value: this.textures[0]
				},
				uDataTexture: {
					value: this.texture
				}
			},
			vertexShader: `
				varying vec2 vUv;
				void main() {
			        vUv = uv;
			        gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
			    }
			`,
			fragmentShader: `
				uniform float time;
				uniform float progress;
				uniform sampler2D uDataTexture;
				uniform sampler2D uTexture;
				uniform vec4 resolution;

				varying vec2 vUv;
				varying vec3 vPosition;
				float PI = 3.141592653589793238;
				void main(){
					vec2 newUV = (vUv - vec2(0.5))*resolution.zw + vec2(0.5);
					vec4 color = texture2D(uTexture, vUv);
					vec4 offset = texture2D(uDataTexture, vUv);
					gl_FragColor = vec4(vUv,0.0,1.);
					gl_FragColor = vec4(offset.r,0.,0.,1.);
					gl_FragColor = color;
					gl_FragColor = texture2D(uTexture, newUV - 0.05*offset.rg);
				}
			`
		});

		this.geometry = new THREE.PlaneGeometry(1, 1, 1, 1);
		this.plane = new THREE.Mesh(this.geometry, this.material);

		this.scene.add(this.plane);
	}

	mouseEvents(){
		window.addEventListener('mousemove', (e) => {
			this.mouse.x = e.clientX/this.width;
			this.mouse.y = e.clientY/this.height;

			this.mouse.vX = this.mouse.x - this.mouse.prevX;
			this.mouse.vY = this.mouse.y - this.mouse.prevY;

			this.mouse.prevX = this.mouse.x;
			this.mouse.prevY = this.mouse.y;

		});
	}

	updateDataTexture(){
		let data = this.texture.image.data;

		for (var i = 0; i < data.length; i+=4) {
			data[i] *= 0.91;
			data[i+1] *= 0.91;
		}

		
		let gridMouseX = this.size*this.mouse.x;
		let gridMouseY = this.size*(1-this.mouse.y);
		let maxDist = this.size/4;

		for (var i = 0; i < this.size; i++) {
			for (var j = 0; j < this.size; j++) {
				
				let distance = (gridMouseX - i)**2 + (gridMouseY - j)**2;
				let maxDistSq = maxDist**2;

				if(distance<maxDistSq){
					let index = 4*(i + this.size*j);

					let power = maxDist/Math.sqrt(distance);
					//if(distance < 1) power = 1;

					data[index] += this.mouse.vX * power;
					data[index+1] -= this.mouse.vY * power;
				}

			}
		}

		this.mouse.vX *= 0.91;
		this.mouse.vY *= 0.91;
		

		this.texture.needsUpdate = true;
	}

	render(){

		this.time += 0.05;
		this.updateDataTexture();
		this.material.uniforms.time.value = this.time;
		requestAnimationFrame(this.render.bind(this));
		this.renderer.render(this.scene, this.camera);
	}

	setupResize(){
		window.addEventListener('resize', this.resize.bind(this));
	}

	resize(){
		this.width = this.container.offsetWidth;
		this.height = this.container.offsetHeight;
		this.renderer.setSize(this.width, this.height);
		this.camera.aspect = this.width / this.height;

		/*
		* Cover Image
		*/
		let a1, a2;
		this.imageAspect = 1./1.5;
		if(this.height/this.width > this.imageAspect){
			a1 = (this.width/this.height) * this.imageAspect;
			a2 = 1;
		} else {
			a1 = 1;
			a2 = (this.height/this.width) / this.imageAspect;
		}

		this.material.uniforms.resolution.value.x = this.width;
		this.material.uniforms.resolution.value.y = this.height;
		this.material.uniforms.resolution.value.z = a1;
		this.material.uniforms.resolution.value.w = a2;

		this.camera.updateProjectionMatrix();
	}
}

Run it:

import * as THREE from './three/three.module.js';
import WaterEffect from './water.js';

class App{

	constructor(){
		this.gwater = new WaterEffect({
        		dom: your_container_here
        	});
      
      		this.loader();
	}

	loader(){
		this.gwater.loadingManager = new THREE.LoadingManager();
		this.gwater.textureLoader = new THREE.TextureLoader(this.gwater.loadingManager);

		this.gwater.textures = [];

		this.gwater.textures.push(this.gwater.textureLoader.load('/wp-content/themes/gate-child/assets/images/mono.png'));
	

		this.gwater.loadingManager.onLoad = () => {
			
			/*
			* Alright, THREE.js is ready, run IT
			*/

			this.gwater.addObjects();
			this.gwater.resize();
			this.gwater.render();
			this.gwater.setupResize();
			this.gwater.mouseEvents();

		}
	}
}

new App();

You can reach THREE.js Loading Progression before onLoad... Put a look here: https://threejs.org/docs/index.html?q=loading#api/en/loaders/managers/LoadingManager

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...