Scrolling usando thumbnail

En esta entrada vamos a explicar cómo desarrollar un scrolling de texto usando para ello una miniatura del texto que se va a mostrar. Dispondremos de una capa con el texto y otra capa con la minuatura del texto, la cual remarca que parte del texto se está enseñando.

Scrolling

Este efecto ha sido testeado en IE6 y en Firefox 1.5 bajo Windows XP, en Opera es posible que no funcione debido a un bug que lleva arrastrando bastante tiempo.

La primera de las capas contiene el texto que se debe mostrar. Tiene que tener la propiedad overflow: hidden para evitar que se haga scrolling de forma independiente a nuestro thumbnail, aunque se podría controlar el scrolling y cuando este se produzca modificar la miniatura. Para facilitar el desarrollo hemos tomado un tamaño en particular, y hemos usado la etiqueta pre para cortar las líneas a nuestro gusto, evitando así el que el texto se ordene como el navegador decida. También es posible que el texto no tenga un formato rígido, pero el desarrollo se complicaría sobre manera, porque habría que estar calculando proporciones entre el tamaño real y el reducido. El estilo necesitado sería el siguiente:

  /* Contenedor del texto */
#contenedor {
width: 340px;
height: 427px;
position: relative;
overflow: hidden;
font-size: 12px;
white-space: pre;
}

La capa quedaría de la siguiente manera:

<div id="contenedor">
<pre>Lorem ipsum dolor sit amet, consectetuer
adipiscing elit. Vestibulum ac nibh vel nisl
...</pre>
</div>

La segunda capa es la que se encarga del thumbnail. Está compuesta a su vez de tres capas: la que se encuentra al fondo es la ventana que indica el texto que se está mostrando (su tamaño está calculado a ojo), la siguiente capa es la que contiene el texto y la capa que está por encima del resto funciona como tapadera para evitar que se seleccione el texto que se encuentra debajo (usando una gif transparente de tamaño 1×1). Los estilos necesarios son los siguientes:

  /* Contenedor de la miniatura */
#miniatura {
font-size: 4px;
position: absolute;
top: 4px;
left: 360px;
width: 92px;
cursor: crosshair;
border: 2px solid #009090;
white-space: pre;
background: #999999;
}
/* Ventanita que muestra que es lo que se está mostrando */
#ventana {
position: absolute;
top: 0px;
background: #EEEEEE;
width: 100%;
}
/* Contenedor del texto de la miniatura */
#contenedor-texto {
position: absolute;
width: 92px;
z-index: 2;
}
/* Capa que evita que se pulse en la miniatura */
#tapa {
position: absolute;
background: url(sp.gif);
width: 100%;
height: 100%;
z-index: 4;
}

También es necesario que se capturen 3 eventos, para controlar el funcionamiento del scrolling: onmousedown para saber cuando empieza el movimiento del scrolling, onmouseup para saber cuando acaba el scrolling y onmousemove para el movimiento del scrolling.

<div id="miniatura"
onmousedown="estadoScrolling(event, this, true)"
onmouseup="estadoScrolling(event, this, false)"
onmousemove="scrollingCapa(event, this)">
</div>

Ahora solo nos falta ver el código Javascript necesario para hacer funcionar el scrolling. El desarrollo se centra en 3 procesos, uno que crea el la miniatura según el contenido del texto (creaMiniatura), otro que inicializa/finaliza el scrolling (estadoScrolling) y uno que se encarga del scrolling (scrollingCapa). A parte existen unas variables globales y un método para obtener el valor numérico de los estilos de un objeto:

  var altoTot = 427;  // Alto de las ventanas
var altoVtn = 135;  // Alto de la ventanita que
// indica que es lo que se muestra
var anchoTot = 400;  // Ancho de la ventana del texto
var anchoVtn = 92;  // Ancho de la ventanita que
// indica que es lo que se muestra
var movimiento = false;  // Si se debe mover
var desp = 0;  // Desplazamiento que hay que
// tener en cuenta cuando se
// pincha dentro de la ventanita
// Devuelve el valor numerico de una
// propiedad de los estilos (elimina el px)
function valorCSS(obj, tipo) {
var aux = eval("parseFloat(obj.style." + tipo +
".substring(0,obj.style." + tipo + ".length-2))");
return aux;
}

El método que se encarga de crear la miniatura cogerá el contenido del texto y lo incluirá en la capa del thumbnail (usando para ello innerHTML), a parte de inicializar los estados de las capas, cogerá todas las imágenes incluidas en la miniatura y las modificará de tamaño conservando proporciones, para ello es necesario que el width y el height se indiquen en el estilo de la imagen:

  // Crea la miniatura segun el texto
// del contenedor
function creaMiniatura() {
// Contenedor del texto
var orig = document.getElementById("contenedor");
// Contenedor de la miniatura
var mini = document.getElementById("miniatura");
// Inicializo la posición del scroll para cuando se haga refresh
orig.scrollTop = "0px";
// Se oculta la miniatura hasta que acabe
mini.style.display = "none";
// La miniatura está formada por 3 capas:
// 1. la ventanita que indica que se está mostrando
// 2. el texto
// 3. una tapadera para evitar que se seleccione el texto
mini.innerHTML = '<div id="ventana"></div><div id="contenedor-texto">'+orig.innerHTML+'</div><div id="tapa"></div>';
// Inicialización de valores
mini.style.height = altoTot+"px";
document.getElementById("contenedor-texto").style.height = altoTot+"px";
document.getElementById("ventana").style.height = altoVtn+"px";
document.getElementById("ventana").style.top = "0px";
// Se cogen todas las imagenes (que deben tener su
// alto y ancho fijados) y se redimensionan para que
// tengan un tamaño proporcional
var imgs = mini.getElementsByTagName("IMG");
var alto = orig.scrollHeight;
var porc = anchoVtn/anchoTot;
for (var i=0; i<imgs.length; i++) {
imgs[i].style.width = (porc*valorCSS(imgs[i], "width"))+"px";
imgs[i].style.height = (porc*valorCSS(imgs[i], "height"))+"px";
}
// Se muestra la miniatura
mini.style.display = "block";
}

Activa o desactivar el scrolling se hará usando una variable global (movimiento). Es necesario controlar si se pulsa dentro de la ventana o fuera, si ocurre dentro se debe calcular la distancia entre la parte superior de la ventanita y la posición del ratón, para que cuando se inicie el movimiento de la ventana sea relativo a la posición del ratón:

  // Empieza o finaliza el movimiento
// de la ventanita
function estadoScrolling(evt, obj, ok) {
movimiento = ok;
// Si hay movimiento, hay que tener
// en cuenta si se ha pinchado dentro
// de la ventanita, si es así, se toma
// la distancia desde el ratón hasta el "top"
// de la ventanita, para luego sumar esa distancia
if (ok) {
var y = evt.layerY? evt.layerY : evt.offsetY; // Diferencias entre IE y Firefox;
var vtn = document.getElementById("ventana");
if (y < valorCSS(vtn, "top") ||
y > valorCSS(vtn, "top")+altoVtn) {
// si se ha pulsado fuera se
// desplaza a la posición y se
// toma como desplazamiento = 0
scrollingCapa(evt, obj);
desp = 0;
} else {
// si se ha pulsado dentro se
// toma la longitud del desplazamiento
desp = y - valorCSS(vtn, "top");
}
}
}

Por último, nos encargamos del scrolling del texto. Para ello tenemos que desplazar el texto según la posición del ratón en la ventana de la miniatura. El desplazamiento se hace mediante la propiedad scrollTop, el cual no funciona correctamente en Opera:

  // Mueve la ventanita según la posición del ratón
// y desplaza (scrolling) el texto
function scrollingCapa(evt, obj) {
if (movimiento) {
var y = evt.layerY? evt.layerY : evt.offsetY; // Diferencias entre IE y Firefox;
// Ventana que contiene el texto
var orig = document.getElementById("contenedor");
// Alto total del contenedor del texto
var alto = orig.scrollHeight;
// Scrolling del texto
orig.scrollTop = (alto*(y-desp)/obj.offsetHeight)-10;
// Movimiento de la ventana
var vtn = document.getElementById("ventana");
var pos = parseInt(y-desp) > altoTot-altoVtn?
altoTot-altoVtn :
y-desp-2;
pos = pos < 0? 0: pos;
vtn.style.top = pos+"px";
}
}

Espero que os guste esta forma de realizar scrolling, siempre está disponible a mejoras, como por ejemplo incluir otros elementos en el texto a parte de imágenes, o a fallos.