Comunidad de diseño web y desarrollo en internet online

Detección de choque entre elementos con Plugin HitTest en jQuery

En ocasiones requerimos conocer si un objeto “choca” con otro, hitTest es el método conocido para realizar esa tarea, si bien Javascript, no lo incluye, podemos recurrir a averiguar cómo funciona y construir nuestra propia función, podremos usar frameworks o librerías, para agilizar nuestro trabajo. Para no estar copiando y pegando el código, lo ideal es crear un archivo para estas líneas de código e incluirlo rápidamente en nuestro HTML en una sola línea, por lo que al final podríamos tener un plugin.

El plugin


Siguiendo mi propio consejo de averiguar cómo funciona y construirlo se me dio por crear mi propio plugin HitTest para Jquery. Hace unos días requerí de usar este método y busqué en google un plugin que ya había utilizado pero no lo encontré tan rápido, así que me fui a buscarlo entre todas las carpetas que tengo, al encontrarlo y luego usarlo me di cuenta que no reaccionaba al colisionar con unos objetos. La anterior vez que lo empleé funcionaba porque era muy sencillo lo que estaba haciendo. Averiguando su funcionamiento, sólo usé la parte que me servía, pero al final terminé creando un plugin para Jquery, Aquí pueden ver cómo construir uno.

Pueden ver cómo funciona y descargarlo aquí.

Ejemplo


Un uso común de hitTest en páginas web, es el uso de un div que cambie su propiedad a Fixed cuando el borde superior del área de trabajo del navegador choque con este, y después el div parezca detenerse al llegar a una cierta parte de la página.
Además de la función hitTest requerimos de lo siguiente para que funcione:

HTML


Crearemos dos divs, uno que active el hitTest y otro que lo detenga, después de la etiqueta body colocamos el primer div:

Código :

<!-- Este div desencadena el método hiTest -->
<div id="des"></div>

El segundo, va después del div que queremos mover:

Código :

<!-- Este otro detiene el div al que le cambiamos la propiedad position -->
<div id="limite"></div>

Definimos cuánto se moverá el div #aMover por medio del div que lo contiene (#contFix), definimos el alto de este último div con CSS.

El código completo de HTML es:

Código :

<!doctype html>
<html>
<head>
   <meta charset="utf-8"/>
   <title>Ejemplo de HitTest para Jquery</title>
   <link rel="stylesheet" href="css/est.css">
   <!-- Jquery -->
   <script type="text/javascript" src="../js/jquery-1.7.2.min.js"></script>
   <!-- Plugin HitTest para Jquery -->
   <script type="text/javascript" src="../des/jquery.hittest.min.js"></script>
   <!-- Acciones -->   
   <script type="text/javascript" src="js/acc.js"></script>
</head>
<body>
   <!-- Este div desencadena el método hiTest -->
   <div id="des"></div>
   <section id="cont">
      <section>
      </section>
      <aside>
         <div id="estatico"></div>
         <!-- Div contenedor y que define la distancia que recorrerá el div a mover o a cambiar propiedad position -->
         <div id="contFix">
            <!-- Div que cambiará de propiedad position -->
            <div id="aMover"></div>
            <!-- Este otro detiene el div al que le cambiamos la propiedad position -->
            <div id="limite"></div>
         </div>
      </aside>
   </section>
</body>
</html>

CSS


Al div #des o desencadenador, le agregamos position fixed de tal forma que siempre se encuentre en esa posición de nuestra área de trabajo sin importar si movemos el scroll, un width al 100% para que abarque el ancho de nuestra ventana, en este caso tiene un borde, pero puede ser sustituido por un height de un 1px. Un top 0 podría ayudar.

Código :

#des{
   position:fixed;
   display: block;
   border:1px solid #f84642;
   width:100%;
}

Definimos cuánto va ha recorrer nuestro div #aMover, en este caso son 450px, no hay que olvidar la propiedad position en relative, debido a que los divs (#aMover y #limite) que contendrá la propiedad absolute.

Código :

aside #contFix{
   height:450px;
   background: #ff9138;
   position:relative;
}

Colocamos a nuestro div #aMover, la propiedad position en absolute, de tal forma que cuando el div llegue al límite se quede en esa posición al colocarle la propiedad bottom 0, y si el desencadenador no ha tocado el div, éste se quede en la parte superior del div #contFix con un top 0. No incluir top 0 en el CSS de este div, podría causar conflictos con Jquery.

Código :

#contFix #aMover{
   position:absolute;
   display: block;
   height:250px;
   width:100%;
   background: rgba(255,243,56,0.5);
}

Para la parte final del css, al div #limite, le colocamos la propiedad position en absolute y lo mandamos hasta la parte inferior del div #contFix por medio de un bottom -1px, de tal forma que este div salga fuera del contendor (#contFix).

Código :

#contFix #limite{
   display: block;
   position:absolute;
   width:100%;
   bottom:-1px;
   border:1px solid #f84642;
}

El código completo de CSS es:

Código :

*{margin:0;}
#des{
   position:fixed;
   display: block;
   border:1px solid #f84642;
   width:100%;
}
#cont{
   display: block;
   max-width:1024px;
   margin:0 auto;
   height:1500px;
   padding:0;
}
   #cont section{
      display: inline-block;
      width:70%;
      height:100%;
      background:#efeaa0;
   }
   #cont aside{
      display: inline-block;
      width:29%;
      float:right;
      height:100%;
      background:#d4972e;
   }
   aside #estatico, aside #contFix{
      display: block;
      width:100%;
   }
   aside #estatico{
      height:230px;
      background: #ff9138;
      margin-bottom:10px;
   }
   aside #contFix{
      height:450px;
      background: #ff9138;
      position:relative;
   }
      #contFix #aMover{
         position:absolute;
         display: block;
         height:250px;
         width:100%;
         background: rgba(255,243,56,0.5);

      }
      #contFix #limite{
         display: block;
         position:absolute;
         width:100%;
         bottom:-1px;
         border:1px solid #f84642;
      }

JavaScript – Jquery


Para la parte de JavaScript en este ejemplo usamos la función scroll, aunque también se puede usar un Timer para detectar cuando el desencadenador (#des) choque con el contenedor (#contFix). Cuando el desencadenador (#des) se encuentre fuera del contenedor (#contFix), al div #aMover, le colocamos la propiedad position: absolute, top: 0 y “borramos” la propiedad bottom, si no “borramos” esta última propiedad, el navegador podría no usar el top y seguir usando la propiedad bottom.

Si el div #des no hace colisión con el div #contFix, es porque éste se encuentra o arriba del contendor y necesitamos que el div #aMover se encuentre en la parte superior del div #contFix, o ha dejado atrás al contenedor, por lo que éste ya no es visible y no importa si el div #aMover, se encuentra en top 0, por lo que el siguiente código es válido:

Código :

if($('#des').hittest('#contFix') == false){
   //Si el div #des no hace hitTest con #contFix, el div #aMover debe colocarse en la parte superior de #contFix, por lo que debe ser position absolute y top 0, "borrando" también la propiedad bottom.
   //Esta condición será valida cuando el #des deje atrás a #contFix, no importará si el div #aMover, se encuentre en la parte superior de #contFix, debido a que estos dos no serán visibles. 
   $('#aMover').css({'position':'absolute','top':0,'bottom':'','width':'100%'});
}

De no ser verdad la condición anterior, debemos cambiar la propiedad position al div #aMover a fixed, al chocar el desencadenador (#des) con el contenedor (#contFix), por lo que el div #aMover inicia su recorrido, colocamos el div #aMover en top 0, de otra forma este div se movería a la posición real que debería tener si por defecto le pusiéramos fixed.

Código :

else{
   //Cuando el div #des hace hitTest con #contFix, el div #aMover, debe moverse con el div #des, para lo cual cambiamos la propiedad position a Fixed y colocamos top 0.
   $('#aMover').css({'position':'fixed','top':0,'width':$('#aMover').width()});
}

La siguiente condición se da cuando el div #aMover choca con el div #limite, deteniendo el div #aMove y colocándolo en parte inferior del div #contFix, por medio de un position absolute y un bottom 0, sin olvidar “borrar” la propiedad top. Se podría decir que esta condición anula la condición anterior.

Código :

if($('#limite').hittest('#aMover') == true){
   //Si el div a #aMover hace hitTest con el div #limite, para que parezca que detenemos el div #aMover, colocamos un position absolute, un bottom 0 y "borramos" top.         
   $('#aMover').css({'position':'absolute','top':'','bottom':0,'width':'100%'});
}

El código completo de JavaScript es:

Código :

$('document').ready(function(){
   $(window).scroll(function(){
      if($('#des').hittest('#contFix') == false){
         //Si el div #des no hace hitTest con #contFix, el div #aMover debe colocarse en la parte superior de #contFix, por lo que debe ser position absolute y top 0, "borrando" también la propiedad bottom.
         //Esta condición será valida cuando el #des deje atrás a #contFix, no importará si el div #aMover, se encuentre en la parte superior de #contFix, debido a que estos dos no serán visibles. 

         $('#aMover').css({'position':'absolute','top':0,'bottom':'','width':'100%'});
      }else{
         //Cuando el div #des hace hitTest con #contFix, el div #aMover, debe moverse con el div #des, para lo cual cambiamos la propiedad position a Fixed y colocamos top 0.

         $('#aMover').css({'position':'fixed','top':0,'width':$('#aMover').width()});
      }
      if($('#limite').hittest('#aMover') == true){
         //Si el div a #aMover hace hitTest con el div #limite, para que parezca que detenemos el div #aMover, colocamos un position absolute, un bottom 0 y "borramos" top.
         
         $('#aMover').css({'position':'absolute','top':'','bottom':0,'width':'100%'});
      }
   })
})

El ejemplo funcionando.

Notas:


Este plugin tiene dos características importantes, la primera es que detecta cuándo un objeto ha colisionado con otro, así sea solo por 1px, la segunda es que no importa qué elemento se coloque primero, el resultado será igual, true o false.

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate