Errores Javascript

Ticker (NewsTicker) con MooTools

Icono de Desarrollo webComo implementar un ticker de noticias, o cualquier listado sin importar las etiquetas de listas, con MooTools (habitualmente con efecto scroll).[...]

  • 25
  • abr
  • 2010

Publicado por J.A.Cobo. Guardado en Desarrollo web, JavaScript.
hace 4 meses y 2 semanas No hay comentarios

Contexto

Hace poco tiempo que decidí cambiar de framework Javascript, en concreto desde que vi los problemas que jQuery tenía para trabajar con DOM distinto de HTML. Mi elección entonces fue volver a Prototype para más tarde pasarme a MooTools.

Objetivo

Ahora estoy migrando el Javascript que uso en algunos sitios a esta librería, MooTools, y con esta entrada trato de ilustrar lo intuitivo y fácil que resulta trabajar con MooTools para extender Javascript y adaptarlo de una forma estándar a tus propósitos. Es, a diferencia de jQuery, para lo que está hecha esta librería.

Newsticker

Aunque es espectacular el ritmo de crecimiento de los plugins para MooTools en su sección Forge y en concreto los slideshow de varios tipos, lo cierto es que no encontraba un scroll de noticias sencillo -técnicamente más conocidos como ticker o newsticker-.

Haberlos los hay, pero ninguno satisfacía mis necesidades pues: o generaban marcado que no me parecía correcto; o se basaban en procesos demasiado estrictos, demasiado complicados; o se basaban más bien en CSS. …O sea, todo demasiado estricto y yo solo quería implementar la funcionalidad para cualquier listado independientemente del estilo CSS asignado y de su marcado.
Así que, como en otras ocasiones -en las BeTools por ejemplo- e inspirado en la idea de Antonio Lupetti para jQuery (es un ciclo de posiciones básicamente), decidí ponerme manos a la obra y reinventar la rueda porque a veces sigue siendo la mejor solución para conseguir exactamente lo que quieres, como en este caso.

Código NewsTicker con MooTools

Esta es la clase que he implementado, nada del otro mundo pero que me funciona perfecta y hace exactamente lo que quiero:

Funcionalidad
  • Me permite aplicarlo a cualquier elemento que contenga hijos, ya sea un listado o no, aunque obviamente lo recomendable y más fiable son los listados.
  • Me permite mantener el estilo original de los elementos abstrayéndose de si se usan o no para el ticker. El ticker simplemente aplica el estilo mínimo para la funcionalidad, en este caso la visualización de un solo item del listado y de acuerdo a la altura original del elemento. Es decir, olvídate de anchos o altos estrictos preestablecidos, es la funcionalidad y punto.
  • El resto ya es lo normal: tiempo entre repeticiones, establecer un efecto, etc.

Actualizado

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/**
 * MooTools NewsTicker by Covi.
 * 
 * Inspirado en la idea de Antonio Lupetti 
 * (http://woork.blogspot.com/2009/05/how-to-implement-news-ticker-with.html)
 *
 * @author Covi
 * @copyright 2010 J.A.Cobo (Covi)
 * @version 0.1beta
 * @todo Interfaz y extender para ticker pequeño, slideshow...
 *
 * @license MIT License http://www.opensource.org/licenses/mit-license.php
Copyright (c) 2010 J. A. Cobo Ruz (Covi), inspired by Antonio Lupetti cycle idea.
 
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of
the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var NewsTicker = new Class({
	Implements: Options,
 
	options: {
		autostart: 		true, 		// Comenzar automático o no.
		animation: 		'fade', 	// Tipo de animación, por el momento: fade, slide (mode:horizontal|vertical).
		mode:			'vertical', // Para animación slide.
		transition:		'sine:out', // TODO slide con transición expo da error pues no crea el div vacío.
		speed: 			1000, 		// Velocidad de la transición en efectos.
		periodical: 	5000, 		// Periocidad de la iteración sobre los ítems.
		padding:		0, 			// Aplicar espaciado a cada ítem si se desea (en un futuro será estilo completo).
		fixedHeight:	true		// Alto fijo o flexible para adaptarse al alto de cada ítem.
	}, 
 
	/**
	 * Constructor
	 */
	initialize: function(el, options)
	{
		this.timer 		= null;
		this.container 	= el;
		this.setOptions(options);
		if ( null === this.container ) 
			throw new Error('El contenedor de la lista no se encontró.');
 
		// # Setup container: Establecer el alto al primer hijo del container:
		this.container.setStyles({
			'height':	this.container.getFirst().getComputedSize().totalHeight + this.options.padding.toInt(), 
			'overflow':	'hidden'
		});
 
		// Eventos y run...
		if ( true === this.options.autostart ) {
			this.run();
		}
	}, 
 
	/**
	 * Efecto.
	 * @access private
	 */
	_animate: function()
	{
		var item 		= this.container.getFirst();	// Primer elemento del listado
		var first 		= item.clone(); 				// Clonar el ITEM ORIGINAL para situarlo al final
 
		// Eliminar y efecto:
		switch ( this.options.animation ) {
			// # FADE:
			default:
			case 'fade':
				item.set('tween', {
					duration: 	this.options.speed, 
					transition: this.options.transition, 
					onComplete: function() {
						this._cycleItem(item, first);
					}.bind(this)
				}).tween('opacity', 0);
			break;
 
			// # SLIDE:
			case 'slide':
				item.set('slide', {
					duration: 	this.options.speed, 
					transition: this.options.transition, 
					mode: 		this.options.mode, 
					onComplete: function() {
						this._cycleItem(item, first);
					}.bind(this)
				}).slide('out');
			break;
		}
	}, 
 
	/**
	 * Ciclo: Eliminar el ítem y pasarlo al final.
	 * @access private
	 */
	_cycleItem: function(item, first)
	{
		if ( !$chk(item) || !$chk(first) ) 
			return;
		var itemHeight 	= item.getComputedSize().totalHeight; 	// Alto del ítem para ajustar el container
		// Eliminar y copiarlo al final:
		item.dispose();
		this.container.grab(first, 'bottom');
 
		// FIXME Eliminar el div vacío que deja slide (para MooTools 1.2.6)
		// @see https://mootools.lighthouseapp.com/projects/24057/tickets/194-fxslide-sometimes-clips-the-contents-of-divs
		if ( 'slide' === this.options.animation ) {
			var wrongDivSlide = this.container.getFirst();
			if ( 'div' == wrongDivSlide.get('tag') ) {
				wrongDivSlide.dispose();
				var nextItem 	= this.container.getFirst();
				// Para el ajustar el alto del container, volvemos a coger el ítem superior.
				itemHeight 		= nextItem.getComputedSize().totalHeight;
				if ( 'horizontal' == this.options.mode ) {
					var myFx = new Fx.Slide(nextItem, {
						duration: 	this.options.speed,
						transition: this.options.transition, 
						mode:		this.options.mode
					});
					myFx.hide().slideIn();
				}
			}
		}
 
		// Si no se establece como alto fijo, ajustamos el alto del container:
		if ( false === this.options.fixedHeight ) {
			this.container.setStyle('height', itemHeight);
		}
	}, 
 
	/**
	 * Inicia el bucle para iterar sobre los ítems.
	 */
	start: function()
	{
		this.timer = this._animate.periodical(this.options.periodical, this);
	}, 
 
	/**
	 * Detiene el bucle.
	 */
	stop: function()
	{
		$clear(this.timer);
	}, 
 
	/**
	 * Lanza todo el proceso: establece, si procede, el bucle y los eventos.
	 */
	run: function()
	{
		this.container.addEvents({
			"mouseover": 	function()	{ this.stop(); 	}.bind(this),
			"mouseout": 	function() 	{ this.start(); }.bind(this)
		});
		this.start();
	}
});
Uso

Por ejemplo para elementos con la clase newsticker:

1
2
3
4
5
6
$$('.newsticker').each(function(item){
	var myTicker = new NewsTicker(item, {
		periodical:		5000, 
		animation:		'slide'
	});
});
Opciones

Las opciones, valga la redundancia, son opcionales ya que se establecen las siguientes por defecto:

1
2
3
4
5
6
7
8
9
10
options: {
		autostart: 		true, 		// Comenzar automático o no.
		animation: 		'fade', 	// Tipo de animación, por el momento: fade, slide (mode:horizontal|vertical).
		mode:			'vertical', // Para animación slide.
		transition:		'sine:out', // TODO slide con transición expo da error pues no crea el div vacío.
		speed: 			1000, 		// Velocidad de la transición en efectos.
		periodical: 	5000, 		// Periocidad de la iteración sobre los ítems.
		padding:		0, 			// Aplicar espaciado a cada ítem si se desea (en un futuro será estilo completo).
		fixedHeight:	true		// Alto fijo o flexible para adaptarse al alto de cada ítem.
	}
  • (Boolean) Autostart (true): true|false. Comenzar o no automáticamente, obvio.
  • (String) Animation (‘fade’): fade|slide por el momento. Tipo de animación (efecto) a usar en las transiciones de ítems.
  • (String) Mode (‘vertical’): vertical|horizontal. Modo del efecto slide si se usa..
  • (String) Transition (‘bounce:out’): Fx.Transitions (sine|bounce|bounce:in|…). Tipo de transición para el efecto.
  • (Integer) Speed (1000): Número. Velocidad de la transición (opción anterior) del efecto.
  • (Integer) Periodical (5000): Número. Lapso de tiempo entre el intercambio de ítems.
  • (Integer) Padding (0): Número. Espaciado a aplicar al item.
    Me gustaría hacer de esto una opción para aplicar un estilo completo si se decide, daría más control sobre la visualización y corregiría posibles fallos que pudiera haber.
  • (Boolean) FixedHeight (true): Número. Alto fijo o flexible para adaptarse al alto de cada ítem.
Notas

Quiero extenderlo bastante más, mejorarlo, añadir más efectos… pero para ilustrar creo que sobra.
Este código se distribuye bajo la licencia del MIT (MIT License: http://www.opensource.org/licenses/mit-license.php), por lo tanto hay que mantener el copyright de la cabecera.

PD: Aún no tengo implementado MooTools en el blog y no hay demo pero pronto pondré alguna.

Demo

Demo y tests en MooShell:
http://mootools.net/shell/XaUJA/3/.

MooShell iframe

Por si no resultará obvio: pulsa en la pestaña RESULT para ver el resultado de la demo.

TODO

  1. Cada ítem debe tener su propia altura si los ítems no tienen la misma.
Acerca del autor

Avatar del autor J.A.Cobo, «Geek» apasionado por la tecnología, la historia y la aviación con especial interés en el Desarrollo Web basado en estándares, la aplicación semántica de la web y la accesibilidad en la misma. Hubo un tiempo en el que también escribía relatos, principal inspiración para iniciar un «blog».

No hay comentarios.
TrackBack URL
Compartir en:
Menéame
Del.icio.us
Google
Technorati

Aún no hay comentarios.

Dejar un comentario.no seas tímido

Campos personales


¿Qué opinas?

Licencia y acuerdo

Logo Creative Commons Los datos introducidos en este formulario se tratarán, en matería de seguridad, de acuerdo a la normativa española sobre protección de datos ( LOPD ) pese a no estar sujetos a ella. Los contenidos pasarán a ser liberados para Cultura Digital.org bajo las licencias Creative Commons BY-SA y GFDL1.3 como una obra derivada de la que el autor mantendrá el copyright.
Asegúrate de entender estos términos antes de enviar tu comentario.

Artículo 2: Ámbito de aplicación.
[...]
Punto 2. El régimen de protección de los datos de carácter personal que se establece en la presente Ley Orgánica no será de aplicación:
a) A los ficheros mantenidos por personas físicas en el ejercicio de actividades exclusivamente personales o domésticas.
[...]

El autor del blog, Juan Antonio Cobo, se reserva el derecho a decidir sobre la publicación de los comentarios de acuerdo a la lógica común, las buenas maneras y la relevancia de los mismos con la entrada.

Twitter

JuanAntonioCobo

  • - 1
  • Error de contenido secundario.