HTML5-Canvas Lauftexte mit RSS (Teil 2)

Canvas Lauftexte RSS
Canvas Lauftexte mit RSS
Bildquelle: Herbert Weber, Hildesheim, Kreuzgang san paolo fuori le mura 3, cropped, CC BY-SA 3.0

In diesem Artikel Canvas Lauftexte mit RSS aus unserer Workshop Serie Digital Signage Newsticker nutzen wir die erlernte Technik aus dem dem ersten Canvas-Artikel und befüllen den Canvas-Kontext mit Inhalten aus einem RSS-Feed. Da der Algorithmus zum Abholen und Verarbeiten des RSS-Feeds bereits in dem Beitrag über CSS3-Animationen ausführlich erörtert wurde, gehen wir nur auf die spezifischen Unterschiede ein.

Der Body-Bereich

Der statische HTML-Teil benötigt einige Änderungen, da mehr Javascript hinzukommt und wir das möglichst übersichtlich gestalten möchten.

<body onload="handleTicker()">
	<canvas id="myTicker" height="80">No Canvas</canvas>
</body>

Wir setzen hier den Canvas mit einer Höhe von 80px und geben ihm eine automatische Breite, denn er soll ja über den kompletten Anzeigebereich gehen. Als Identifikation wählen wir wieder myTicker. Das onload Ereignis wurde bereits hier beschrieben und verhält sich in diesem Beispiel natürlich identisch.

Im Stylesheet

<style>
	#myTicker
	{
		position: absolute;
		left: 0;
		top:0;	
	}
</style>

bekommt das Element eine absolute Position beginnend an der linken oberen Ecke. Das ist wichtig, weil dieses Konzept wegen der Berechnungen eine absolute Positionierung benötigt. Eine relative Positionierung würde außerdem den Canvas nicht exakt an die linke und obere Seiten setzen. Das Elternelement body besitzt nämlich standardmäßig einen Abstand von 8px zu seinem Elternelement html (Browserfenster).

Das komplette Javascript

window.addEventListener('resize', resizeCanvas, false);
const _move_pixel        = 1;
const _max_canvas_width  = 16384;
var MyCanvas	       = {};
var ctx	             = {};
var x                    = 0;
var ticker_content       = "";
var text_width           = 0;

function initCanvas()
{
	MyCanvas = document.getElementById("myTicker");
	ctx    = MyCanvas.getContext("2d");
}

function resizeCanvas()
{
	MyCanvas.width = window.innerWidth;
	ctx.font       = "30px Arial";
	text_width     = ctx.measureText(ticker_content).width;
	x              = MyCanvas.width;
}			

function isNewContentSizeValid(txt)
{
	return (ctx.measureText(txt).width  < _max_canvas_width);
}

function moveTicker()
{
	ctx.clearRect(0,0, MyCanvas.width, 80);
	if (x > -text_width)
		x = x - _move_pixel;
	else
		x = MyCanvas.width;
	ctx.fillText(ticker_content, x, 50);	
	window.requestAnimationFrame(moveTicker);
}

function displayTicker(ticker_text)
{
	ticker_content = ticker_text;
	resizeCanvas();
	window.requestAnimationFrame(moveTicker);
}

function createTickerOutput(feed_obj)
{
	var ticker_text = " +++ ";
	var tmp_text    = "";
	for (var i = 0; i < feed_obj.query.count; i++)
	{
		tmp_text =  feed_obj.query.results.item[i].title+ " +++ ";
		if (isNewContentSizeValid(ticker_text + tmp_text))
			ticker_text += tmp_text;
		else
			break;

	}
	return ticker_text;
}

function handleTicker(response)
{
	var feed_obj    = JSON.parse(response);
	var ticker_text = createTickerOutput(feed_obj);
	displayTicker(ticker_text, feed_obj.query.count);			
}		

function getRSS(url)
{
	var request_url = 'https://query.yahooapis.com/v1/public/yql?q=select title from rss where url="'+url+'"&format=json';
	var MyRequest = new XMLHttpRequest(); // a new request
	MyRequest.open("GET", request_url, true);
	MyRequest.onload = function (e)
	{
		if (MyRequest.readyState === 4)
		{
			if (MyRequest.status === 200)
			{
				handleTicker(MyRequest.responseText);
			}
			else
			{
				console.error(MyRequest.statusText);
			}
		}
	};
	MyRequest.onerror = function (e)
	{
		console.error(MyRequest.statusText);
	};
	MyRequest.send(null);
	return;
}

function start()
{
	initCanvas();
	getRSS("https://smil-control.de/blog/feed");
}

Hier wird es etwas komplizierter. Der Canvas soll sich flexibel verhalten und sich bei einer Änderung der Breite des Browserfensters anpassen. Um das zu erreichen, muss in Javascript ein sogenannter „Eventlistener“ deklariert werden. Dieser verweist bei Aktivierung auf eine Funktion, in der die zur Größenänderung nötigen Befehle stehen. Das geschieht mit window.addEventListener(‚resize‘, resizeCanvas, false); D.h. bei einer Größenänderung (resize) wird immer die Callbackfunktion resizeCanvas() ausgeführt.

function resizeCanvas()
{
	canvas.width   = window.innerWidth;
	ctx.font       = "30px Arial";
	text_width     = ctx.measureText(ticker_content).width;
	x              = canvas.width;

}	

Die Canvas-Breite wird zunächst aus der neuen Fensterbreite ausgelesen. Da sich das Element dabei zurücksetzt, muss die Schrift wieder eingestellt und die Textbreite neu berechnet werden. Der Lauftext soll in diesem Fall nochmal von vorn anfangen, also setzen wir den Wert der Positionsvariable x wieder auf das Maximum.
Die Konstante und globalen Variablen sind wie in canvas_animation_2.html. Allerdings mit dem Unterschied, dass die Variablen in diesem Beispiel mit Defaultwerten initialisiert werden.

Das RSS abholen

Prinzipiell ist das Verfahren analog zu den CSS3-Animationen. Die Einsprungfunktion start() ist bis auf die zusätzliche Initialisierung des Canvas und dessen Kontext in initCanvas() gleich geblieben. Der grobe Programmablauf besitzt jetzt fünf Schritte:

  1. Den Canvas Initialisieren
  2. Den RSS-Feed als JSON-Text abholen
  3. Den JSON-Textin ein Javascriptobjekt konvertieren
  4. Aus dem Javascriptobjekt den Text für den Ticker extrahieren
  5. Den Tickertext ausgeben

Es gibt zwei konzeptionelle Unterschiede zu den CSS3-Animationen

1. Die gute Nachricht: Eine Animationsdauer zu berechnen, um die Lesegeschwindigkeit in Abhängigkeit zu der Textgröße zu steuern, ist nicht mehr nötig. Die Lesegeschwindigkeit lässt sich über die Verschiebung der Pixel steuern und bleibt unabhängig von der Länge des Textes konstant. D.h. diese Funktionalität wurde entfernt.

2. Die schlechte Nachricht: Wir müssen dafür ein anderes Problem lösen! Der Canvas kann nämlich nicht beliebig breit werden. Die Limits sind bei jedem Browser unterschiedlich und können sich zukünftig verändern. D.h. Wir müssen für das jewweilige Zielgerät die Werte durch ausprobieren herausfinden. Firefox limitiert aktuell zum Beispiel auf 22528 x 20992 Pixel. Beim Chromium/Chrome liegt das Limit hingegen bei 16384 x 16384 Pixel. Das entspricht in etwa je nach Länge 10 – 15 Schlagzeilen. Wenn das Limit überschritten wird, akzeptiert der Canvas-Kontext den per fillText() einzufügenden Inhalt nicht und bleibt leer. D.h wir sind gezwungen, Vorkehrungen zu treffen, um diese Problematik abzufangen.

Ansätze das Limit-Problem zu lösen

Es gibt mehrere Möglichkeiten an dieses Problem heranzugehen. Wir könnten z.B. den Text von 30px auf 20px verkleinern. Das kann bei Feeds, die knapp das Limit überschreiten noch funktionieren. Allerdings ist das keine wirkliche Lösung, sondern nur ein schlechter Workaround. Der Feed kann trotzdem noch zu lang werden.
Eine weitaus nachhaltigere Lösung wäre den sichtbaren Bereich des Canvas zirkulär zu befüllen. D.h. ein Zeichen, welches links im Off verschwindet aus dem Canvas-Kontext zu löschen und auf der rechten Seite ein neues Zeichen aus dem Inhalt nachzuschieben. Der Algorithmus dazu ist allerdings komplex und würde den Rahmen dieses Beitrages sprengen. Falls Sie eine solche Lösung umgesetzt haben möchten, können Sie sich gerne an uns wenden.

Wir haben uns für eine, aus unserer Sicht pragmatischere dritte Variante entschieden. Wir schneiden den Text einfach ab, bevor das Limit erreicht wird. Bei den meisten News-Feeds ist es in der Regel so, dass die neuesten Nachrichten am Anfang stehen, während ältere nach unten rutschen. Wenn wir davon ausgehen, dass 10 – 15 Schlagzeilen aus einem Feed angezeigt werden können, sollte das für einen Newsticker in der Regel ausreichend aktuellen Inhalt darstellen. Wer will schon veraltete Nachrichten lesen?

Text vor dem Limit abschneiden

Das „Schneiden“ und Prüfen erfolgt optimalerweise in der Funktion, die den Tickerinhalt zusammenfügt:

function createTickerOutput(feed_obj)
{
	var ticker_text = " +++ ";
	var tmp_text    = "";
	for (var i = 0; i < feed_obj.query.count; i++)
	{
		tmp_text =  feed_obj.query.results.item[i].title+ " +++ ";
		if (isNewContentSizeValid(ticker_text + tmp_text))
			ticker_text += tmp_text;
		else
			break;
	
	}
	return ticker_text;
}

Bei jedem Feed-Item wird erst geprüft, ob sein Hinzufügen für ein Überschreiten des Schwellenwert von 16384 Pixel sorgt. Das geschieht in der Funktion isNewContentSizeValid():

function isNewContentSizeValid(txt)
{
	return (ctx.measureText(txt).width < _max_canvas_width);
}

Dort wird die Breite des neuen Textes untersucht. Wenn diese kleiner ist als 16384 Pixel (_max_canvas_width), liefert die Funktion ein true zurück. Somit bekommt die Variable ticker_text die zusätzliche Schlagzeile zugewiesen. Falls isNewContentSizeValid() false ausgibt, bedeutet das ein weiteres Hinzufügen würde das Limit überschreiten. In dem Fall wird die Bedingung übersprungen, ticker_text bleibt unverändert und mit break verlassen wir vorzeitig die for-Schleife. Das können Sie prüfen, indem Sie den NTV-Feed (https://www.n-tv.de/rss) anstelle von https://smil-control.de/blog/feed verwenden. Der NTV-Feed hat üblicherweise 20-22 Einträge und überschreitet damit definitiv das Limit.

Canvas Lauftexte mit RSS – Der Lohn der Mühe

function displayTicker(ticker_text)
{
	ticker_content = ticker_text;
	resizeCanvas();
	window.requestAnimationFrame(moveTicker);
}

Die globalen Variable ticker_content erhält den auszugebenden Text und das Canvas wird mit den entsprechenden Werten erstellt. Dafür nutzen wir die bereits für das Resizing erstellte Funktion resizeCanvas(). Am Ende starten wir die rekursiv arbeitende Animationsfunktion moveTicker(),

function moveTicker()
{
	ctx.clearRect(0,0, canvas.width, 80);
	if (x > -text_width)
		x = x - move_pixel;
	else
	x = canvas.width;
	ctx.fillText(ticker_content, x, 50);	
	window.requestAnimationFrame(moveTicker);
}

welche sich gegenüber dem letzten Beitrag nicht wesentlich geändert hat. Lediglich der Breitenwert wird jetzt anstelle des festen Wert 500 variabel durch die aktuelle Canvas-Breite bestimmt.
Rufen Sie der Datei canvas_animations_rss.html auf, um eine Live-Ansicht zu bekommen. Es kann je nach Verbindungsgeschwindigkeit 1-4 Sekunden dauern, bis der Feed gelesen und verarbeitet wird.

Wie geht es weiter?

Im nächsten Artikel, werden wir noch zwei weitere Techniken für horizontale Lauftexte vorstellen. Darunter ein Element, welches zum Urgestein des Internets gehört. Danach werden wir alle Konzepte miteinander vergleichen und die einzelnen Vor- und Nachteile zusammenfassen.
Wenn Sie Fragen oder Anmerkungen haben, können Sie uns gerne kontaktieren.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.