HTML5 Custom Controls und Sencha Touch

Das gewisse App-was
Kommentare

HTML5 erobert die unterschiedlichsten Plattformen, und es besteht zunehmend Bedarf an Custom Controls, also Komponenten, die sich entweder im Design oder ihren Eigenschaften von den Standard-Controls unterscheiden. Nach der Erstellung eines solchen Controls steht der Entwickler immer vor dem Problem, diese neue Komponente in ein bestehendes Framework einbinden zu müssen. Wir erklären kurz den Aufbau und die Entwicklung eines HTML5-basierten Custom Controls und zeigen, wie man dieses in Sencha Touch einbindet.

Apple hat bereits bei der Einführung des ersten iPhones versucht, die Anwendungsentwicklung weg von nativen und hin zu webbasierten Anwendungen zu forcieren, was allerdings nicht richtig gelang. Zu dieser Zeit war der Großteil der Entwicklergemeinde der Meinung, Anwendungen, die in JavaScript und HTML geschrieben sind, könnten nativen Anwendungen nicht das Wasser reichen. Das hat sich dank des Siegeszugs von HTML5 gewaltig verändert: Heute sind HTML-basierte Anwendungen aus dem mobilen Sektor nicht mehr wegzudenken. Sie bieten dem Entwickler die Möglichkeit, relativ plattformunabhängig eine mobile Anwendung zu erstellen, die auf einer Vielzahl mobiler Endgeräte lauffähig ist. Mit ihrem Framework Sencha Touch hat die Firma Sencha ein Produkt geschaffen, das sich in diesem Bereich einen Namen gemacht hat. In Sencha Touch wird im Prinzip die komplette Anwendung inklusive User Interface in JavaScript programmiert. Dadurch war und ist es sehr weit verbreitet.

HTML5 Custom Control

Als Entwickler steht man oft vor dem Problem, dass die Anforderungen im Projekt die Entwicklung eigener Komponenten nötig machen. In webbasierten Anwendungen bestehen diese so genannten Custom Controls in der Regel aus einer Mischung aus HTML, JavaScript und CSS. Das HTML5-<canvas>-Tag ist dabei für den grafischen Teil, JavaScript für die Logik und CSS für Styling und Animation zuständig. Als Beispiel wollen wir in diesem Artikel eine einfache LED als Komponente erstellen. Für eine reale Anwendung würde man wahrscheinlich einen anderen Ansatz wählen, da eine LED durch ihre beiden Zustände (AN und AUS) auch einfach durch zwei Bilder zu realisieren ist. Aber gerade wegen ihrer Einfachheit ist sie als Beispiel hervorragend geeignet. Zunächst haben wir als Vorlage eine Zeichnung der Komponente erstellt (Abb. 1).

Abb. 1: Grafische Vorlage der LED (Adobe Fireworks)

Um diese Zeichnung nun in Code zu überführen, müssen wir die einzelnen Bestandteile der Zeichnung mithilfe der zur Verfügung stehenden grafischen Elemente des HTML5 Canvas API umsetzen. Damit man eine skalierbare Komponente erhält, muss man im Prinzip die XY-Koordinaten aller grafischen Elemente in Relation zu der Größe der Zeichnung angeben. Nehmen wir zum Beispiel den Kreis, der die rote LED selbst darstellt (Abb. 2). Wie man erkennen kann, hat der Kreis folgende Abmessungen:

Abb. 2: Kreis mit Gradient, der die LED darstellt (Adobe Fireworks)

  • X = 100
  • Y = 100
  • W = 200
  • H = 200

Es ist natürlich möglich, einen Kreis mit exakt diesen Werten zu zeichnen, doch das würde nur bei einer Größe der Komponente von W=400 und H=400 funktionieren. Wollte man die gleiche Komponente in der halben Größe erstellen, so müsste man alle Werte anpassen. Damit die Komponente automatisch skaliert, beziehen wir die einzelnen Koordinaten einfach auf die Ausgangsgröße:

  • X / ORIGINAL_BREITE * AKTUELLE_BREITE
  • Y / ORIGINAL_HÖHE * AKTUELLE_HÖHE

Die Werte für ORIGINAL_BREITE und ORIGINAL_HÖHE sind dabei die Größen der Originalzeichnung. Die Werte für AKTUELLE_BREITE und AKTUELLE_HÖHE entsprechen der gewünschten Größe der Komponente. Das führt zu folgenden Werten:

  • X = 0.25 * AKTUELLE_BREITE
  • Y = 0.25 * AKTUELLE_HÖHE
  • W = 0.5 * AKTUELLE_BREITE
  • H = 0.5 * AKTUELLE_HÖHE

Da die LED quadratisch ist, muss man nur dafür sorgen, dass die Breite immer der Höhe entspricht, damit man eine runde LED erhält. Es gibt inzwischen auch Zeichenprogramme, die direkt HTML5 exportieren können, sodass man keine umständliche manuelle Konvertierung vornehmen muss. Damit aus dem reinen Zeichencode eine funktionsfähige Komponente wird, muss ein wenig Logik dazuprogrammiert werden. Man benötigt dabei Methoden zur Erzeugung des Canvas-Elements im HTML DOM zum Initialisieren der so genannte Buffer, Methoden zur Visualisierung der Grafik und letztendlich noch Methoden zur Änderung des Zustandes (AN oder AUS, Abb. 3).

Abb. 3: Zustand AUS und AN der LED

Da bei einem Zustandswechsel nicht immer der gesamte Zeichencode ausgeführt werden muss (es ändert sich ja im Prinzip nur der Farbgradient eines einzelnen Kreises), speichert man die verschiedenen Ebenen der Komponente in Buffern. Diese Buffer sind ebenfalls Canvas-Elemente, die jedoch nicht in den DOM „eingehängt“ werden und somit nur im Speicher zur Verfügung stehen. Vorteil dieser Methode ist, dass man die verschiedenen Bilder im Voraus in den Speicher „zeichnen“ kann und sie später lediglich abzurufen braucht. Einzig bei einer Größenänderung der Komponente muss die gesamte Visualisierung neu durchgeführt werden. In Listing 1 wurde aus Platzgründen auf den Zeichencode verzichtet. Wie man sehen kann, kann die LED-Komponente mit einigen Parametern initialisiert werden. Werden diese nicht angegeben, nimmt man die Default-Parameter, beispielsweise die Breite und Höhe der Komponente.

var Led = function(param) {
  para        = para || {};
  this.id     = (undefined === param.id ? 'control' : param.id);
  this.parId  = (undefined === param.parId ? 'body' : param.parId);
  this.width  = (undefined === param.width ? 400 : param.width);
  this.height = (undefined === param.height ? 400 : param.height);
  var on      = (undefined === param.on ? false : param.on);

  // Create the <canvas> element
  var canvas    = document.createElement('canvas');
  canvas.id     = this.id;
  canvas.width  = this.width;
  canvas.height = this.height;
  if (this.parId === 'body') {
    document.body.appendChild(canvas);
  } else {
    document.getElementById(this.parId).appendChild(canvas);
  }

  // Get the canvas context and clear it
  var mainCtx = document.getElementById(this.id).getContext('2d');
  mainCtx.save();
  mainCtx.clearRect(0, 0, this.width, this.height);
  mainCtx.canvas.width  = this.width;
  mainCtx.canvas.height = this.height;

  var imageWidth  = this.width;
  var imageHeight = this.height;
  var initialized = false;

  // Initialize buffers
  var bkgBuffer    = document.createElement('canvas');
  bkgBuffer.width  = imageWidth;
  bkgBuffer.height = imageHeight;
  var bkgCtx       = bkgBuffer.getContext('2d');

  var offBuffer    = document.createElement('canvas');
  offBuffer.width  = imageWidth;
  offBuffer.height = imageHeight;
  var offCtx       = offBuffer.getContext('2d');

  var onBuffer     = document.createElement('canvas');
  onBuffer.width   = imageWidth;
  onBuffer.height  = imageHeight;
  var onCtx        = onBuffer.getContext('2d');

  var fgdBuffer    = document.createElement('canvas');
  fgdBuffer.width  = imageWidth;
  fgdBuffer.height = imageHeight;
  var fgdCtx       = fgdBuffer.getContext('2d');

  // Drawing methods
  var drawBkg = function(ctx) {...}

  var drawLed = function(ctx, isOn) {...}

  var drawFgd = function(ctx) {...}

  var init = function() {
    initialized = true;
    drawBkg(bkgCtx);
    drawLed(offCtx, false);
   drawLed(onCtx, true);
   drawFgd(fgdCtx);
  }

  // Public methods
  this.paintComponent = function() {
    if (!initialized) {
      init();
    }
    mainCtx.save();
    mainCtx.clearRect(0, 0, this.width, this.height);
    mainCtx.drawImage(bkgBuffer, 0, 0);
    if (on) {
      mainCtx.drawImage(onBuffer, 0, 0);
   } else {
      mainCtx.drawImage(offBuffer, 0, 0);
   }
   mainCtx.drawImage(fgdBuffer, 0, 0);
    mainCtx.restore();
  }

  this.setOn = function(newState) {
    on = newState;
   this.paintComponent();
  }

  this.isOn  = function() {
    return on;
  }
  this.paintComponent();
}
...
this.width = (undefined === param.width ? 400 : param.width);
this.height = (undefined === param.height ? 400 : param.height);
...

Wird also keine Breite (width) mit übergeben, so erhält die Breite automatisch den Wert 400, was der Breite des Originalbildes entsprach. Diese Technik entspricht einem Konstruktor, wie man ihn aus anderen Programmiersprachen wie etwa Java kennt. Im folgenden Schritt wird ein Canvas-Element erzeugt und in den HTML DOM eingefügt. Zudem holen wir uns von diesem Canvas-Element den so genannten Canvas Context (im Listing abgekürzt durch ctx), löschen seinen Inhalt und setzen seine Größe. Dieser Canvas Context stellt auch das 2-D-API zur Verfügung, mit dessen Hilfe wir die Komponente letztendlich „zeichnen“ können.

...
  // Create the <canvas> element
  var canvas    = document.createElement('canvas');
  canvas.id     = this.id;
  canvas.width  = this.width;
  canvas.height = this.height;
  if (this.parentId === 'body') {
    document.body.appendChild(canvas);
  } else {
    document.getElementById(this.parentId).appendChild(canvas);
  }
  
  // Get the canvas context and clear it
  var mainCtx = document.getElementById(this.id).getContext('2d');
  mainCtx.save();
  mainCtx.clearRect(0, 0, this.width, this.height);

  // Set the canvas size
  mainCtx.canvas.width  = this.width;
  mainCtx.canvas.height = this.height;
...

Da wir unser Canvas-Element angelegt und eingerichtet haben, müssen wir noch die Buffer der verschiedenen Ebenen unserer Komponente anlegen und einrichten. Für die LED-Komponente habe ich vier verschiedene Ebenen/Buffer angelegt: Background, Off, On und Foreground. Jeder dieser Buffer wird wie in Listing 4 am Beispiel des Background Buffers erzeugt und initialisiert.

...
  var bkgBuffer    = document.createElement('canvas');
  bkgBuffer.width  = imageWidth;
  bkgBuffer.height = imageHeight;
  var bkgCtx       = bkgBuffer.getContext('2d');
...

Es wird also ein Canvas-Element erzeugt (jedoch NICHT in den DOM „gehängt“), seine Größe gesetzt, das Canvas-Context-Objekt von diesem Canvas-Element geholt und in eine Variable gespeichert. Die verschiedenen Zeichenmethoden nehmen nun als Parameter jeweils ein Context-Element, auf dem dann die verschiedenen grafischen Elemente gezeichnet werden. Für jede Ebene wurde im Beispiel eine Zeichenmethode angelegt und nur für die LED selbst eine Methode erzeugt, da sich die beiden Zustände der LED nur durch unterschiedliche Füllungen eines Kreises unterscheiden (Listing 5).

...
  var drawLed = function(ctx, isOn) {
    ctx.save();
    ctx.beginPath();
    ctx.arc(0.5 * imageWidth, 0.5 * imageHeight, 
            0.25 * imageWidth, 0, 2 * Math.PI, false);
    var ledFill = 
      ctx.createLinearGradient(0.3275 * imageWidth, 
                               0.3275 * imageHeight, 
                               0.6686 * imageWidth, 
                               0.6686 * imageHeight);
    if(isOn) { // on
      ledFill.addColorStop(0.00, 'rgb(181, 0, 0)');
      ledFill.addColorStop(0.49, 'rgb(112, 0, 0)');
      ledFill.addColorStop(1.00, 'rgb(251, 0, 0)');
     ctx.fillStyle = ledFill;
     ctx.fill();
     // glow
      ctx.shadowOffsetX = 0.0 * imageWidth;
      ctx.shadowOffsetY = 0.0 * imageHeight;
      ctx.shadowColor   = 'rgba(255, 0, 0, 1)';
      ctx.shadowBlur    = 0.25 * imageWidth;
      ctx.fill();
    } else { // off
     ledFill.addColorStop(0.00, 'rgb(52, 15, 20)');
     ledFill.addColorStop(0.49, 'rgb(37, 0, 0)');
     ledFill.addColorStop(1.00, 'rgb(52, 15, 20)');
     ctx.fillStyle = ledFill;
     ctx.fill();    
   }
    ctx.restore();
  }
...

Damit wir die Buffer später verwenden können, müssen wir noch eine Methode zur Initialisierung hinzufügen, in der alle Buffer „befüllt“ werden. Dazu müssen die Buffer zunächst gelöscht werden, was mit der clearRect()-Methode erfolgt.

...
  var init = function() {
    initialized = true;
    bkgCtx.clearRect(0, 0, this.width, this.height);
   offCtx.clearRect(0, 0, this.width, this.height);
   onCtx.clearRect(0, 0, this.width, this.height);
   fgdCtx.clearRect(0, 0, this.width, this.height);
    
   drawBkg(bkgCtx);
    drawLed(offCtx, false);
    drawLed(onCtx, true);
   drawFgd(fgdCtx);
  }
...

Nachdem wir nun Methoden haben, um die Canvas-Context-Elemente anzulegen, zu initialisieren und mit grafischen Elementen zu füllen, benötigen wir noch eine Methode zur Visualisierung der Komponente, also eine Methode, in der all die verschiedenen Canvas-Context-Elemente, die wir jetzt mit Grafik gefüllt haben, übereinander gezeichnet werden. Das geschieht in der Methode paintComponent() (Listing 7). Es handelt sich hierbei um eine öffentliche Methode, da man sie von außen aufrufen können muss. Nachdem die Komponente initialisiert ist, wird der Canvas Context des sich im DOM befindlichen Canvas-Elements gelöscht. Daraufhin werden alle Buffer nacheinander übereinander gezeichnet. Hier findet auch die Unterscheidung nach dem jeweiligen Zustand statt (AN oder AUS). Letztendlich fehlt jetzt nur noch der HTML-Code, der benötigt wird, um die Komponente im Browser anzuzeigen. Abbildung 4 zeigt die fertige HTML5-Komponente der LED im Browser.

...
  this.paintComponent = function() {
    if (!initialized) {
      init();
    }
    mainCtx.save();
    mainCtx.clearRect(0, 0, this.width, this.height);
    mainCtx.drawImage(bkgBuffer, 0, 0);
    if (on) {
     mainCtx.drawImage(onBuffer, 0, 0);
    } else {
     mainCtx.drawImage(offBuffer, 0, 0);
    }
   mainCtx.drawImage(fgdBuffer, 0, 0);
    mainCtx.restore();
  }
...
<!DOCTYPE html>
<html lang='en'>
  <head>
    <meta charset='utf-8' />
    <title>Led Komponente</title>
    <script type='text/javascript' src='Led.js'></script>
    <script type='application/javascript'>
      var control;
      function init() {
        control = new Led({id: 'control', 
                           parentId:'container', 
                           on: false, 
                 width: 400, 
                           height: 400});
      }    
     window.setInterval('control.setOn(!control.isOn())', 750);
    </script>
  </head>
  <body onload='init()'>
    <div id='container'></div>
  </body>
</html>

Abb. 4: HTML5-Led-Komponente im Browser

Aufmacherbild: 3d man pressing the green button in jump von Shutterstock / Urheberrecht: VERSUSstudio

[ header = Einbindung in Sencha Touch ]

Einbindung in Sencha Touch

Mit Sencha Touch steht dem Mobil-Entwickler eine Vielzahl von gelungenen Widgets für die grafische Gestaltung zur Verfügung. Doch um der Mobile-App noch das gewisse Etwas zu verleihen, lohnt es sich gelegentlich, eigene Komponenten zu gestalten. Für die Einbindung eines Custom Controls in Sencha Touch soll hier eine komplexere Control-Komponente verwendet werden. Vorweg ein Hinweis: Alle Codebeispiele im Folgenden beziehen sich auf Sencha Touch 2 mit überarbeiteter MVC-Architektur. Manch ein älterer Entwickler erinnert sich sicherlich an die mechanischen Splitflap-Anzeigen aus Flughäfen oder Bahnhöfen. Die Ziffern der Anzeige werden hier durch Umklappen einzelner Kärtchen geändert, was sich bei einer umfangreichen Aktualisierung einer größeren Anzeigetafel mit einem unverwechselbaren Sound vollzieht. Eine derartige Control-Komponente wie in Abbildung 5 ist wie dafür geschaffen, in einem mobilen Fußball-Tippspiel für die Wahl eines Tippergebnisses zu dienen.

Abb. 5: HTML5-Splitflap-Komponente im Browser

Mit den eingebauten Board-Mitteln von Sencha Touch würde man den Control für das Tippergebnis wohl mittels eines Spinners (Ext.field.Spinner) realisieren. Spinner werden typischerweise für die Manipulation von HTML5-Nummernfeldern verwendet. Mit etwas Geschick lassen sich aber auch zwei Picker-Instanzen (Ext.picker.Picker) so arrangieren, dass man eine brauchbare Komponente für die Auswahl der Tore beider Teams erhält. Mit dem gleichen Vorgehen wie bei der LED-Komponente kann man auch einen Splitflap Control realisieren. Neben der grafischen Gestaltung der Zifferkärtchen ist hier natürlich auch noch die richtige Animation ein entscheidendes Gestaltungselement. Letztere lässt sich relativ einfach und performant mittels CSS3 Keyframes umsetzen, doch das wäre Thema eines weiteren Artikels. Für die folgende Einbindung setzen wir den Splitflap Control als gegeben voraus. Damit sich der Control gut in beliebige Applikationen einbinden lässt, sollte er über einen Konstruktor parametrisierbar sein. Mittels Event Subscription sollen Änderungen am Tippergebnis nach außen zugänglich gemacht werden. Eine Umsetzung von beidem ist in Listing 9 veranschaulicht. Zu beachten ist, dass sich die Größe des Scoreboards automatisch an die des Parent-Elements anpasst.

// scoreboard constructor
var scoreBoard = new ScoreBoard(
  {
    parentId:'board',
    teamA: inTeamA,
    teamB: inTeamB,
    scoreA: inScoreA,
    scoreB: inScoreB,
    x:0,
    y:0
  }
);
// score event listener registration
scoreBoard.scoreEvent.subscribe(
  function (sender, eventArgs) { /* do something */ }
);

Die Einbindung in eine bestehende Sencha Touch App geschieht nun am besten über eine eigene View-Komponente. Hierzu erzeugen wir im view-Unterordner eine Komponente namens SplitFlap.js. Unsere SplitFlap View erbt sinnvollerweise von Ext.Component und hat ihren eigenen xtype splitflap. Um unsere View-Komponente von außen parametrisierbar zu machen, benötigen wir eine eigene Konstruktorimplementierung. Mittels config-Objekt können wir so alle notwendigen Parameter übergeben. Typischerweise werden Sencha Touch Views über das config-Objekt und dort über das items-Attribut statisch aufgebaut. Doch wie bindet man eine Custom-Komponente ein, die einen existierenden DOM Tree voraussetzt, um sich einfügen zu können? Die Lösung besteht in der dynamischen Instanziierung des config-Objekts im Konstruktor und der Registrierung eines Listeners auf das painted-Event. In der Dokumentation ist nachzulesen, dass nach dem painted-Event von einem fertig instanziierten DOM ausgegangen werden kann. Somit ist das der richtige Zeitpunkt für die Einbindung des Controls. Wichtig: im Konstruktor nicht den Parent-Aufruf am Schluss vergessen (this.callParent([cfg]);)! Listing 10 zeigt die vollständige Implementierung.

Ext.define('Cats.view.SplitFlap', {
    extend:'Ext.Component',
    xtype:'splitflap',

    scoreBoard: null,
    inTeamA: null,
    inTeamB: null,

    constructor: function (config) {
        var inTeamA = config.inTeamA;
        var inTeamB = config.inTeamB;
        var inScoreA = config.inScoreA || 0;
        var inScoreB = config.inScoreB || 0;
        var inRecordId = config.inRecordId;

        var me = this;
        var fireScoreChange = function(event) {
            var score = event.getScore();

            var scoreTeamA = score.getScoreTeamA();
            var scoreTeamB = score.getScoreTeamB();

            me.fireEvent('scorechange', me, { 
      recordId: inRecordId, 
      scoreTeamA: scoreTeamA, 
      scoreTeamB: scoreTeamB });
        };

        var cfg = {
            height:150,
            html:"<div style='text-align: center' id='board'></div>",

            listeners: {
                painted: function() {
                    scoreBoard = new ScoreBoard(
                            {
                                parentId:'board',
                                teamA: inTeamA,
                                teamB: inTeamB,
                                scoreA: inScoreA,
                                scoreB: inScoreB,
                                x:0,
                                y:0
                            }
                    );
                    scoreBoard.scoreEvent.subscribe(
        function (sender, eventArgs) {
                        fireScoreChange(eventArgs);
                    });
                }
            }
        };
        this.callParent([cfg]);
    }

});
});

Bei der Implementierung fallen einem noch ein paar Besonderheiten auf. Das für die Einbindung der Scoreboard-Komponenten nötige Parent-Div wird dynamisch mittels html-Schnipsel (id=board) im config-Objekt erledigt. Der Callback für das painted-Event geschieht innerhalb der „listeners“-Konfiguration. In der Implementierung des Callbacks wird hier nicht nur das Scoreboard erzeugt, sondern auch die View-Komponente als Listener auf ScoreEvents registriert. Letztendlich soll es nämlich möglich sein, sich auf der View-Komponente selbst als Listener für ScoreEvents zu registrieren. Um den Code übersichtlicher zu gestalten, wurde hier die fireScoreChange()-Methode extrahiert. Über den Aufruf von fireEvent() können wir in dieser nach außen einen beliebigen, eigens definierten EventTyp weiterleiten.

Die so entwickelte Sencha View kann nun nach Belieben im Controller verwendet werden. In unserer Tippspiel-App erscheint das Scoreboard beim Tippen auf einen Match-Eintrag, der unsere onMatchItemTap()-Funktion aufruft. Die Daten für das Scoreboard kommen übrigens aus einem „MatchStore“ Record, der auch über das Control aktualisiert wird. Sinnvollerweise lässt man das Scoreboard mittels ActionSheet (Ext.ActionSheet) von unten nach oben sliden (Listing 11).

...
onMatchItemTap:function (dataview, record, target, index, event, eOpts) {
  var picker = Ext.create('Ext.ActionSheet', {
    items:[{
      xtype:'splitflap',
      centered: true,

      inTeamA:record.get("homeTeam"),
      inTeamB:record.get("awayTeam"),
      inScoreA:parseInt(record.get("tipGoalsHomeTeam")),
      inScoreB:parseInt(record.get("tipGoalsAwayTeam")),
      inRecordId:record.get("id")
      },
      {
      xtype:'button',
      text:'Tippen',
      ui:'confirm',
      listeners:{
        tap:function () {
          picker.hide();
          Ext.Viewport.remove(picker);
          picker.destroy();

          // store result
          var store = Ext.getStore("MatchStore");
          store.sync();
        }
      }
      }]
            });

            Ext.Viewport.add(picker);
            picker.show();
    },

    onScoreChanged:function(splitflap, scores) {
        var store = Ext.getStore("MatchStore");
        var record = store.getById(scores.recordId);
        record.set('tipGoalsHomeTeam', scores.scoreTeamA);
        record.set('tipGoalsAwayTeam', scores.scoreTeamB);
    }

Unterhalb des Scoreboards wird hierbei noch ein Bestätigungsbutton für das Schließen eingefügt, der auch das Ausblenden des ActionSheets steuert und die Tippergebnisse im MatchStore persistiert. Damit die onScoreChanged()-Funktion – zuständig für das Aktualisieren der Tippergebnisse – aufgerufen wird, muss in der Konfiguration des Controllers noch ein spezieller Selector (refs-Objekt) und ein Delegation (control-Objekt) definiert werden (Listing 12).

// Controller implementation
...
config:{
  views:[
    ...
    'SplitFlap',
  ],
  refs:{
     ...
    splitflap:'splitflap'
  },
  control:{
    ...
    splitflap:{
      scorechange:"onScoreChanged"
    }
  }
}
...

Das fertige Ergebnis – mit angepasstem Farbschema – präsentiert sich dann wie in Abbildung 6. Durch Tippen auf die oberen/unteren Ziffernhälften wird der Punktestand jeweils inkrementiert/dekrementiert. Eine Livedemo ist hier verfügbar




(login:frei, passwort: frei).

Abb. 6: HTML5-Splitflap-Komponente in Sencha Touch

Fazit

Mit relativ geringem Aufwand ist es möglich, unabhängige HTML5-Komponenten in Sencha Touch einzubauen, wenn man die oben genannten Hinweise berücksichtigt. Handelt es sich um grafisch aufwändige Controls, ist es sinnvoll, den Prototypen in einem Zeichenprogramm zu entwerfen und diesen Prototypen dann mit geeigneten Tools in Code zu überführen. Für die im Artikel genannten Beispiele wurde hierzu Adobe Fireworks verwendet – und der FxgConverter, mit dem sich Files im Adobe-FXG-Format unter anderem auch in HTML5 Canvas konvertieren lassen. Möchte man sich als Unternehmen mit einer Anwendung von der breiten Masse absetzen, stellen Custom Controls auf der Basis von HTML5 eine hervorragende Möglichkeit dar, eine Anwendung plattformübergreifend zu gestalten.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -