epelpad

[WebGL] Efectos de distorsión liquida

[WebGL] Efectos de distorsión liquida
presentacion

Distorsion



Un pase de diapositivas con efectos de distorsión de líquido en WebGL impulsado por PixiJS y GSAP.


*Toca la imagen para ir al demo

Hoy queremos compartir un interesante efecto de distorsión con usted. El concepto principal de esta demostración es utilizar un mapa de desplazamiento para distorsionar una imagen subyacente, dándole diferentes tipos de efectos. Para demostrar las transiciones de tipo líquido entre las imágenes, hemos creado una presentación de diapositivas.

Lo que generalmente hace un mapa de desplazamiento, es usar una imagen como textura, que luego se aplica a un objeto, dando la ilusión de que el objeto subyacente está envuelto alrededor de esa textura. Esta es una técnica comúnmente usada en muchas áreas diferentes, pero hoy exploraremos cómo esto se puede aplicar a una presentación de imágenes simple.

Utilizaremos PixiJS como nuestro motor de renderizado y filtro y GSAP para nuestras animaciones.

Empezando
desplazamiento

Para tener un efecto de desplazamiento, necesita una textura de mapa de desplazamiento. En este código de demostración hemos proporcionado diferentes tipos de texturas que puedes usar, pero por supuesto puedes crear uno propio, por ejemplo usando la herramienta de renderizado de Photoshop. Tenga en cuenta que las dimensiones de esta imagen afectan el resultado final, por lo que jugar con texturas de diferentes tamaños, podría darle diferentes efectos.

Una regla general es que su imagen de textura debe ser una potencia de textura de 2 tamaños. Lo que esto significa es que su anchura y altura se puede duplicar o dividir por 2. Esto asegura que su textura se optimiza para correr rápido, sin consumir demasiada memoria. En otras palabras, las dimensiones sugeridas para su imagen de textura (ancho y / o altura), serían: 8, 16, 32, 64, 128, 256, 512, 1024, 2048 etc.

Para las demos, hemos creado una presentación de diapositivas que, al navegar, muestra el efecto como transición en las diapositivas. También agregaremos otras opciones, pero solo pasaremos por la idea principal del efecto de distorsión.

Margen
webGL

Nuestro marcado base para esta demo es realmente mínimo. Sólo necesitamos los botones de navegación para nuestro deslizador y un envoltorio para las diapositivas. Utilizamos este envoltorio para pasar nuestros diapositivas a nuestro componente, por lo tanto lo ocultamos de forma predeterminada con CSS. Este enfoque de marcado a JS puede simplificar la tarea de agregar imágenes a la presentación de diapositivas, cuando se trabaja en un entorno más dinámico. Sin embargo, si te conviene mejor, simplemente puedes pasarlos fácilmente como una matriz, al inicializar.


<div class="slide-wrapper">
    <div class="slide-item">
        <h3 class="slide-item__title">Slide 1</h3>
        <img src="..." class="slide-item__image">
    </div>
    <div class="slide-item">
        <h3 class="slide-item__title">Slide 2</h3>
        <img src="..." class="slide-item__image">
    </div>
    <div class="slide-item">
        <h3 class="slide-item__title">Slide 3</h3>
        <img src="..." class="slide-item__image">
    </div>                                
</div>

<a href="#" class="scene-nav scene-nav--prev" data-nav="previous">PREV</a>
<a href="#" class="scene-nav scene-nav--next" data-nav="next">NEXT</a> 


CSS
[WebGL] Efectos de distorsión liquida

En nuestro CSS ocultamos nuestro envoltorio y colocamos los botones de navegación en los bordes izquierdo y derecho de la ventana gráfica.


.slide-wrapper {
  display: none;
}

.scene-nav {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 10;
  display: inline-block;
}

.scene-nav--next {
  right: 2%;
}

.scene-nav--prev {
  left: 2%;


Estableciendo el escenario
presentacion

La idea es bastante simple: agregamos todos nuestros diapositivas en un contenedor, aplicamos el filtro de desplazamiento y hacemos render. Luego, al hacer clic en los botones de navegación, establecemos la propiedad alfa de la imagen actual en 0, configuramos la siguiente en 1 y ajustamos el filtro de desplazamiento mientras navegamos.


var renderer            = new PIXI.autoDetectRenderer();
var stage               = new PIXI.Container();
var slidesContainer     = new PIXI.Container();
var displacementSprite  = new PIXI.Sprite.fromImage( displacementImage );
var displacementFilter  = new PIXI.filters.DisplacementFilter( displacementSprite );

// Add canvas to the HTML
document.body.appendChild( renderer.view );

// Add child container to the stage
stage.addChild( slidesContainer );

// Set the filter to stage
stage.filters = [displacementFilter];        

// We load the sprites to the slides container and position them at the center of the stage
// The sprites array is passed to our component upon its initialization
// If our slide has text, we add it as a child to the image and center it
function loadPixiSprites( sprites ) {
  
  for ( var i = 0; i < sprites.length; i++ ) {
    
    var texture = new PIXI.Texture.fromImage( sprites[i] );
    var image   = new PIXI.Sprite( texture );

    if ( texts ) {

      // Base styles for our Text
      var textStyle = new PIXI.TextStyle({
        fill: '#ffffff', 
        wordWrap: true,
        wordWrapWidth: 400
      });

      var text = new PIXI.Text( texts[i], textStyle);
      image.addChild( text );
      
      // Center each to text to the image
      text.anchor.set(0.5);
      text.x = image.width / 2;
      text.y = image.height / 2;      
               
    }
    
    image.anchor.set(0.5);
    image.x = renderer.width / 2;
    image.y = renderer.height / 2;            

    slidesContainer.addChild( image );
    
  } 
  
}; 


Ésa sería la disposición más básica que usted necesitaría para que la escena fuera lista. Lo siguiente que queremos hacer es manejar los clics de los botones de navegación. Como dijimos, cuando el usuario hace clic en el botón siguiente o anterior, cambiamos la propiedad alfa de la diapositiva correspondiente y modificamos nuestro Filtro de desplazamiento. Utilizamos una línea de tiempo simple para esto, que podría por supuesto personalizar en consecuencia.


// We listen at each navigation element click and call the move slider function 
// passing it the index we want to go to
var currentIndex = 0;
var slideImages = slidesContainer.children;
var isPlaying = false;  

for ( var i = 0; i < nav.length; i++ ) {
  
  var navItem = nav[i];

  navItem.onclick = function( event ) {

    // Make sure the previous transition has ended
    if ( isPlaying ) {
      return false;
    }     

    if ( this.getAttribute('data-nav') === 'next' ) {

      if ( that.currentIndex >= 0 && that.currentIndex < slideImages.length - 1 ) {
        moveSlider( currentIndex + 1 );
      } else {
        moveSlider( 0 );
      }

    } else {

      if ( that.currentIndex > 0 && that.currentIndex < slideImages.length ) {
        moveSlider( currentIndex - 1 );
      } else {
        moveSlider( spriteImages.length - 1 );
      }            

    }

    return false;

  }
  
}


// Our transition between the slides
// On our timeline we set the alpha property of the relevant slide to 0 or 1 
// and scale out filter on the x & y axis accordingly
function moveSlider( newIndex ) {

    isPlaying = true;

    var baseTimeline = new TimelineMax( { onComplete: function () {
        that.currentIndex = newIndex;
        isPlaying = false;
    }}); 
    
    baseTimeline
        .to(displacementFilter.scale, 1, { x: 200, y: 200  })
        .to(slideImages[that.currentIndex], 0.5, { alpha: 0 })
        .to(slideImages[newIndex], 0.5, { alpha: 1 })          
        .to(displacementFilter.scale, 1, { x: 20, y: 20 } );

}; 


Finalmente, tenemos que renderizar nuestra escena y opcionalmente agregar algunas animaciones predeterminadas.


// Use Pixi's Ticker class to render our scene 
// similar to requestAnimationFrame
var ticker = new PIXI.ticker.Ticker();
ticker.add( function( delta ) {
    
    // Optionally have a default animation
    displacementSprite.x += 10 * delta;
    displacementSprite.y += 3 * delta;
    
    // Render our stage
    renderer.render( stage );

}); 



Demostración de trabajo
Distorsion

Esto debe resumir las partes más básicas de cómo funciona la demostración y darle un buen punto de partida si desea editarlo de acuerdo a sus necesidades. Sin embargo, si no desea meterse con demasiado código y necesita una demostración de trabajo rápido para jugar por su cuenta, hay varias opciones que podría utilizar al inicializar el componente. Simplemente incluya el script en su página y agregue el siguiente código donde quiera que muestre su presentación de diapositivas. Juegue con diferentes valores para comenzar y no olvide probar diferentes texturas de desplazamiento para diferentes efectos.


// Select all your images
var spriteImages = document.querySelectorAll( '.slide-item__image' ); 
var spriteImagesSrc = [];
var texts = [];

for ( var i = 0; i < spriteImages.length; i++ ) {
  
  var img = spriteImages[i];
  
  // Set the texts you want to display to each slide 
  // in a sibling element of your image and edit accordingly
  if ( img.nextElementSibling ) {
    texts.push(img.nextElementSibling.innerHTML);
  } else {
    texts.push('');
  }
  
  spriteImagesSrc.push( img.getAttribute('src' ) );
  
}

// Initialise the Slideshow
var initCanvasSlideshow = new CanvasSlideshow({
  
  // pass the images you want as an array
  sprites: spriteImagesSrc, 
  
  // if you want your slides to have title texts, pass them as an array
  texts: texts,                                                                     
  
  // set your displacement texture
  displacementImage: 'https://imgur.com/a/Ea3wo', 
  
  // optionally start with a default animation 
  autoPlay: true, 

  // [x, y] controls the speed for your default animation
  autoPlaySpeed: [10, 3], 
  
  // [x, y] controls the effect amount during transitions
  displaceScale: [200, 70], 

  // choose whether or not you slideshow will take up all the space of the viewport
  fullScreen: true,

  // If you choose to not have a fullscreen slideshow, set the stage's width & height accordingly
  stageWidth: 800,
  stageHeight: 600,

  // add you navigation element. Should have a 'data-nav' attribute with a value of next/previous
  navElement: document.querySelectorAll( '.scene-nav' ),

  // will fit the filter bounding box to the renderer
  displaceAutoFit: false

});


Interactivo
imagen

Lo último que queremos hacer es opcionalmente hacer nuestro escenario interactivo. Eso es en lugar de jugar automáticamente, tienen nuestro efecto interactuar con nuestro ratón. Sólo tiene que configurar la propiedad interactiva para que sea verdad y jugar con el ratón.

var initCanvasSlideshow = new CanvasSlideshow({
  interactive: true
  ...
});


En todas las interacciones del ratón escuchamos el evento correspondiente, y basándonos en los datos del evento, escalamos nuestro evento de desplazamiento respectivamente. Se parece a esto:

// Set our container to interactive mode
slidesContainer.interactive = true;
slidesContainer.buttonMode = true;       

// Our animation
var rafID, mouseX, mouseY;

function rotateSpite() {
  displacementSprite.rotation += 0.001;
  rafID = requestAnimationFrame( rotateSpite );
}

slidesContainer.pointerover = function( mouseData ){
  mouseX = mouseData.data.global.x;
  mouseY = mouseData.data.global.y;   
  TweenMax.to( displacementFilter.scale, 1, { x: "+=" + Math.sin( mouseX ) * 100 + "", y: "+=" + Math.cos( mouseY ) * 100 + ""  });   
  rotateSpite();
}      
            
slidesContainer.pointerdown = function( mouseData ){
  mouseX = mouseData.data.global.x;
  mouseY = mouseData.data.global.y;         
  TweenMax.to( displacementFilter.scale, 1, { x: "+=" + Math.sin( mouseX ) * 1200 + "", y: "+=" + Math.cos( mouseY ) * 200 + ""  });   
}     

slidesContainer.pointerout = function( mouseData ){
  TweenMax.to( displacementFilter.scale, 1, { x: 0, y: 0 });
  cancelAnimationFrame( rafID );
}     

slidesContainer.pointerup = function( mouseData ){
  TweenMax.to( displacementFilter.scale, 1, { x: 0, y: 0 });                      
  cancelAnimationFrame( rafID );
}              


¡Simple como eso! Espero que esta demo le da un buen punto de partida para jugar con diferentes filtros y hacer que sea fácil para usted crear su propio.

Por: Yannis Yannakopoulos



desplazamiento
webGL

0 comentarios - [WebGL] Efectos de distorsión liquida