Naučte HTML novým trikům (II)
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:
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.
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 a až g 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>
Tak proč pises članek?
Článek o HTML bez řadku 'html' dobry vtip, Chtělo by to víc praxe a techniky před napsaním članku (malé A4), nebo upozornit ze se jen učíš
Jinak hezké
Na takové hračičky je Angular pěkný, ale na větším komerčním projektu už bych ho nenasazoval, použil něco jiného (React) nebo počkal bych na verzi 2, která bude podle všeho postavená úplně jinak a dořeší neduhy jedičky...
Pěkné. Když jsem ale testoval HTML5 inputy, některé prohlížeže to nezvládaly a tak dělám stále postaru.
Teď jsem se tam podíval
input-date
a Firefox to stále neumí.
Kráťa
• Proc nenapises o tech "HTML5 input" naké clanek? (pripadne mi to zajimavejsi s rozsahlejsim vyuzitim)
• Jako clanek o <HTML> se mi to zda jako zajimavejsi koncept nez Angular .
Já testoval pouze tento input "date". Na článek by to jednak nebylo a druhak se necítím být natolik odborně, abych o tom sepsal pojednání.
Nicméně díky za inspiraci, možná testnu i ostatní a napíšu pojednání z pohledu webotvůrce a uživatele.
V roce 2011 jsem na to i založil vlákno
http://pc.poradna.net/q/view/666610-ma-smysl-delat -input-type-date
V Konqueroru je to ještě vtipnější. Je tam sice cvakátko-klikátko na vytažení roletky, leč nejde zmačknout
To nemá chybu!