Naučte HTML novým trikům (II)

Pavel, 03.02.2015 08:00, Internet, 7 odpovědí (7781 zobrazení)

Dnes si ukážeme, jak vytvořit nový HTML tag, reprezentující SVG prvek, který se zobrazí jako sedmisegmentovka (jedna číslice). Použijeme ho např. takhle: <svg-segment x=“20“ y=“30“ value=“6“ color=“blue“></svg-segment>

Článek volně navazuje na předchozí: Naučte HTML novým trikům.

Pozn.: Je mi jasné, že daný úkol lze realizovat mnoha způsoby, jistě jsou i elegantnější než moje řešení :-)

Sedmisegmentovka

Jedná se hojně rozšířený způsob zobrazení čílic 0 – 9 (popřípadě 0 – F v šestnáctkové soustavě). Může vypadat např. takhle:
http://pc.poradna.net/file/view/21476-7segmentovka            -popis-png
Každý segment má svoje označení (a – g), číslice zobrazíme tak, že příslušný segment buď rozsvítíme, nebo zhasneme.
Pro zobrazení číslice 3 rozsvítíme segmenty: a, b, c, d, g.
http://pc.poradna.net/file/view/21477-7segmentovka            -cislice-3-png

Direktiva v Angularu

Pomocí javascriptové knihovny AngularJS můžeme definovat nový tag, který potom využijeme v HTML kódu webové stránky. Naše direktiva se jmenuje svgSegment a v HTML kódu ji použijeme jako <svg-segment></svg-segment>.
Může vypadat takto:

app.directive('svgSegment', function(){
    return {
    restrict: 'E',
    replace: true,
    scope: {
        x:'@',
        y: '@',
        min: '@',
        max: '@',
        value: '=value',
        error: '@',
        errorColor: '@',
        color: '@'
    },
    templateNamespace: "svg",
    template: '<g transform="translate({{x}}, {{y}})">\n\
                    <polyline id="segm_a" stroke-width="1" points="11, 14 17, 8 47, 8 53, 14 47, 20 17, 20 11, 14 17, 8" stroke-linejoin="miter" ng-attr-fill="{{a}}" />\n\
                    <polyline id="segm_g" stroke-width="1" points="11, 65 17, 59 47, 59 53, 65 47, 71 17, 71 10, 65 17, 59" stroke-linejoin="miter"  ng-attr-fill="{{g}}" />\n\
                    <polyline id="segm_d" stroke-width="1" points="11, 114 17, 108 47, 108 53, 114 47, 120 17, 120 11, 114 17, 108" stroke-linejoin="miter"  ng-attr-fill="{{d}}" />\n\
                    <polyline id="segm_f" stroke-width="1" points="8, 19 14, 25 14, 55 8, 61 2, 55 2, 25 8, 19 14, 25" stroke-linejoin="miter" ng-attr-fill="{{f}}" />\n\
                    <polyline id="segm_b" stroke-width="1" points="56, 19 62, 25 62, 55 56, 61 50, 55 50, 25 56, 19 62, 25" stroke-linejoin="miter"  ng-attr-fill="{{b}}" />\n\
                    <polyline id="segm_c" stroke-width="1" points="56, 69 62, 75 62, 105 56, 111 50, 105 50, 75 56, 69 62, 75" stroke-linejoin="miter" ng-attr-fill="{{c}}" />\n\
                    <polyline id="segm_e" stroke-width="1" points="8, 69 14, 75 14, 105 8, 111 2, 105 2, 75 8, 69 14, 75" stroke-linejoin="miter" ng-attr-fill="{{e}}" />\n\
                    \n\</g>',

controller: function ($scope){
         
        $scope.getColors = function(){
            var val = $scope.value;
            if (!$scope.isError) {
                $scope.a = (val === 0 || val === 2 || val === 3 || val === 5 || val === 6 || val === 7 || val === 8 || val === 9) ? $scope.color : 'none';
                $scope.b = (val === 0 || val === 1 || val === 2 || val === 3 || val === 4 || val === 7 || val === 8 || val === 9) ? $scope.color : 'none';
                $scope.c = (val === 0 || val === 1 || val === 3 || val === 4 || val === 5 || val === 6|| val === 7 || val === 8 || val === 9) ? $scope.color : 'none';
                $scope.d = (val === 0 || val === 2 || val === 3 || val === 5 || val === 6 || val === 8) ? $scope.color : 'none';
                $scope.e = (val === 0 || val === 2 || val === 6 || val === 8 ) ? $scope.color : 'none';
                $scope.f = (val === 0 || val === 4 || val === 5 || val === 6 || val === 8 || val === 9) ? $scope.color : 'none';
                $scope.g = (val === 2 || val === 3 || val === 4 || val === 5 || val === 6 || val === 8 || val === 9) ? $scope.color : 'none';                
            }
            else {
                if ($scope.error === "E") {
                    $scope.a = $scope.f = $scope.g = $scope.e = $scope.d = $scope.errorColor;
                    $scope.b = $scope.c = 'none';
                }
                else {
                    $scope.g = $scope.errorColor;
                    $scope.a = $scope.b = $scope.c = $scope.d = $scope.e = $scope.f = 'none';
                }
            }             
        };
        
        $scope.checkError = function(){
            $scope.isError = false;
            $scope.isError = !( ($scope.value === parseInt($scope.value)) && ($scope.value >= $scope.min) && ($scope.value <= $scope.max) );
        };
         
         
        $scope.min = ($scope.min === parseInt($scope.min)) ? $scope.min : 0;
        $scope.max = ($scope.max === parseInt($scope.max)) ? $scope.max : 9;
         
        $scope.color = $scope.color || 'black';
        $scope.errorColor = $scope.errorColor || 'red';
        $scope.error = ( ($scope.error === '-') || ($scope.error === 'E') ) ? $scope.error : 'E';

$scope.$watch('value', function(newVal){
            $scope.value = newVal;
            $scope.checkError();
            $scope.getColors();
        });
    }
    }; 
});

Direktiva vrací objekt, jehož vlastnost:
restrict: 'E' říká, že vytvoříme nový tag = element
replace: true značí, že (níže uvedená) vlastnost template nahradí náš tag <svg-element></svg-element> zápisem uvedeným v template
scope je další objekt, pomocí kterého ovlivníme chování tagu <svg-element></svg-element> z HTML kódu použitím atributů
x, y jsou souřadnice, kde se nám sedmisegmentovka vykreslí
min, max umožní definovat minimální a maximální číslici, kterou sedmisegmentovka zobrazí
value je hodnota, kterou chceme zobrazit
error je znak, který se má zobrazit v případě chyby (např. číslo mimo rozsah min-max), může být buď „-„ nebo „E“
errorColor je barva, kterou se zobrazí chyba
color je barva rozsvíceného segmentu

templateNamespace: "svg" je tu proto, aby direktiva fungovala uvnitř SVG
template obsahuje kód, který se vloží místo tagu <svg-element></svg-element>
na prvním řádku je případné posunutí segmentovky o x pixelů doprava a o y pixelů dolů (klasický způsob v SVG)
na dalších řádcích jsou pak definovány jednotlivé segmenty ag pomocí tagu <polyline />, je to běžný zápis v SVG až na poslední atribut ng-attr-fill, který určuje barvu každého segmentu (podle toho, zda je rozsvícený nebo zhasnutý)

controller popisuje chování direktivy a provádí některé manipulace
funkce $scope.getColors() na základě hodnoty value nastaví barvu těm segmentům, které se mají rozsvítit, zhasnuté segmenty se vůbec nevykreslí; v případě chyby se zobrazí buď znak „-„ nebo znak „E“

funkce $scope.checkError() ověří, zda je zadána celočíselná hodnota value a zda je v intervalu min-max, v opačném případě nastaví proměnnou $scope.isError na true

poslední fukce $scope.$watch() by tu být nemusela v případě, že nepotřebujeme update sedmisegmentovky a stačilo by nám jen jednou nastavit její číselnou hodnotu, která se má zobrazit; pokud ale chceme, aby segmentovka reagovala na změnu atributu value, hodí se nám tato funkce

Použití tagu <svg-segment></svg-segment> v HTML

Direktiva definuje několik atributů pro tag (element) <svg-segment></svg-segment> zapsaný v HTML. Atributy jsou:
x, y, min, max, value, error, errorColor, color

Příklad použití:
<svg> <svg-segment x=“20“ y=“30“ value=“6“ color=“blue“></svg-segment> </svg>

Co se stane v případě, že některý z atributů vynecháme? Každý atribut má svoji implicitní hodnotu (která se použije). Souřadnice x, y ji sice nemají implicitně uvedenu, ale pokud je vynecháme, zobrazí se sedmisegmentovka na pozici 0, 0. Ostatní implicitní hodnoty jsou definovány v controlleru, např. minimum:
$scope.min = ($scope.min === parseInt($scope.min)) ? $scope.min : 0;
Zdálo by se, že vynecháním nejpodstatnějšího atributu value nebude zřejmé, co se vlastně má zobrazit, nicméně zobrazí se chyba (znak „-„ nebo „E“), to zařídí funkce $scope.checkError(), v jejíž první části podmínky se ověří, zda platí $scope.value === parseInt($scope.value), tj. zda je atribut value celé číslo. Tím, že atribut value úplně vynecháme, podmínka splněna není a je nastaven příznak chyby a proměnná $scope.isError má hodnotu true.

Na závěr

Jsem si vědom toho, že daný úkol se dá řešit mnoha způsoby, z nichž některé jsou jistě elegantnější než moje řešení. Pro experty: Také vím, že v direktivách existují funkce link a compile…

Direktiva by šla ještě rozšířit o atribut type s hodnotami „bin“, „dec“, „hex“ a s příslušně upraveným controllorem by se pak dala použít na zobrazování dvojkových, desítkových a šestnáctkových číslic.

Pokud někomu nevyhovuje velikost sedmisegmentovky, nemusí nic upravovat v kódu, ale stačí obalit segmentovku tagy <g> a </g> a nastavit příslušnou hodnotu scale, např:
<svg> <g transform=“scale(0.5)“> <svg-segment x=“20“ y=“30“ value=“6“ color=“blue“></svg-segment> </g> </svg>

Odpovědět


PředmětAutorDatum
Re: Naučte HTML novým trikům (II) LeguanOS03.02.2015 10:09
Re: Naučte HTML novým trikům (II) MaSo03.02.2015 10:41
Re: Naučte HTML novým trikům (II) Kráťa03.02.2015 14:12
Re: Naučte HTML novým trikům (II) CoCoChanel03.02.2015 14:41
Re: Naučte HTML novým trikům (II) Kráťa03.02.2015 21:57
Re: Naučte HTML novým trikům (II) Rce05.02.2015 03:22
Re: Naučte HTML novým trikům (II) posledníKráťa05.02.2015 10:34

TOPlist