
Podsumowanie
Jak użyliśmy Polymeru do stworzenia wydajnej mobilnej technologii WebGL Miecz świetlny, który jest modułowy i można skonfigurować. Sprawdzamy kilka kluczowych szczegółów naszego projektu: https://lightsaber.withgoogle.com/ pomoże Ci zaoszczędzić czas, gdy następnym razem natrafisz na wściekłych Szturmowców.
Omówienie
Jeśli zastanawia się Pan/Pani, czym są Polymer lub WebKomponent, najlepiej jest zacząć od udostępnienia fragmentu z faktycznego działającego projektu. Oto przykład wzięty ze strony docelowej naszego projektu https://lightsaber.withgoogle.com. Jest to zwykły plik HTML, który ma w środku trochę magii:
<!-- Element-->
<dom-module id="sw-page-landing">
<!-- Template-->
<template>
<style>
<!-- include elements/sw/pages/sw-page-landing/styles/sw-page-landing.css-->
</style>
<div class="centered content">
<sw-ui-logo></sw-ui-logo>
<div class="connection-url-wrapper">
<sw-t key="landing.type" class="type"></sw-t>
<div id="url" class="connection-url">.</div>
<sw-ui-toast></sw-ui-toast>
</div>
</div>
<div class="disclaimer epilepsy">
<sw-t key="disclaimer.epilepsy" class="type"></sw-t>
</div>
<sw-ui-footer state="extended"></sw-ui-footer>
</template>
<!-- Polymer element script-->
<script src="scripts/sw-page-landing.js"></script>
</dom-module>
Obecnie istnieje wiele możliwości tworzenia aplikacji opartej na HTML5. Interfejsy API, platformy, biblioteki, silniki gier itp. Konfiguracja jest trudna do skonfigurowania, mimo że jest tu wiele możliwości między kontrolą nad wysoką wydajnością grafiki a przejrzystą modułową do ich struktury i skalowalności. Odkryliśmy, że technologia Polymer może pomóc nam utrzymać projekt, który jest zorganizowany przy jednoczesnej pracy na niskim poziomie. i staraliśmy się opracować sposób, w jaki zrealizowaliśmy nasz projekt, aby jak najlepiej wykorzystać możliwości platformy Polymer.
Modułowość z polimerem
Polymer to biblioteka, która umożliwia daje większą kontrolę nad sposobem tworzenia projektu z wykorzystaniem elementów niestandardowych wielokrotnego użytku. Umożliwia korzystanie z samodzielnych, w pełni funkcjonalnych modułów znajdujących się w jednym pliku HTML. Mają nie tylko strukturę (znaczniki HTML), ale też style wbudowane i logika.
Spójrz na ten przykład:
<link rel="import" href="bower_components/polymer/polymer.html">
<dom-module id="picture-frame">
<template>
<!-- scoped CSS for this element -->
<style>
div {
display: inline-block;
background-color: #ccc;
border-radius: 8px;
padding: 4px;
}
</style>
<div>
<!-- any children are rendered here -->
<content></content>
</div>
</template>
<script>
Polymer({
is: "picture-frame",
});
</script>
</dom-module>
Jednak w większym projekcie pomocne może być oddzielenie tych 3 elementów logicznych: (HTML, CSS, JS) i scalać tylko podczas kompilacji. Jedna rzecz każdy element projektu ma oddzielny folder:
src/elements/
|-- elements.jade
`-- sw
|-- debug
| |-- sw-debug
| |-- sw-debug-performance
| |-- sw-debug-version
| `-- sw-debug-webgl
|-- experience
| |-- effects
| |-- sw-experience
| |-- sw-experience-controller
| |-- sw-experience-engine
| |-- sw-experience-input
| |-- sw-experience-model
| |-- sw-experience-postprocessor
| |-- sw-experience-renderer
| |-- sw-experience-state
| `-- sw-timer
|-- input
| |-- sw-input-keyboard
| `-- sw-input-remote
|-- pages
| |-- sw-page-calibration
| |-- sw-page-connection
| |-- sw-page-connection-error
| |-- sw-page-error
| |-- sw-page-experience
| `-- sw-page-landing
|-- sw-app
| |-- bower.json
| |-- scripts
| |-- styles
| `-- sw-app.jade
|-- system
| |-- sw-routing
| |-- sw-system
| |-- sw-system-audio
| |-- sw-system-config
| |-- sw-system-environment
| |-- sw-system-events
| |-- sw-system-remote
| |-- sw-system-social
| |-- sw-system-tracking
| |-- sw-system-version
| |-- sw-system-webrtc
| `-- sw-system-websocket
|-- ui
| |-- experience
| |-- sw-preloader
| |-- sw-sound
| |-- sw-ui-button
| |-- sw-ui-calibration
| |-- sw-ui-disconnected
| |-- sw-ui-final
| |-- sw-ui-footer
| |-- sw-ui-help
| |-- sw-ui-language
| |-- sw-ui-logo
| |-- sw-ui-mask
| |-- sw-ui-menu
| |-- sw-ui-overlay
| |-- sw-ui-quality
| |-- sw-ui-select
| |-- sw-ui-toast
| |-- sw-ui-toggle-screen
| `-- sw-ui-volume
`-- utils
`-- sw-t
Foldery każdego elementu mają taką samą strukturę wewnętrzną z osobnymi katalogi i pliki logiczne (pliki kawa), style (pliki CSS) oraz (plik jade).
Oto przykładowy element sw-ui-logo
:
sw-ui-logo/
|-- bower.json
|-- scripts
| `-- sw-ui-logo.coffee
|-- styles
| `-- sw-ui-logo.scss
`-- sw-ui-logo.jade
Jeśli spojrzysz na plik .jade
:
// Element
dom-module(id='sw-ui-logo')
// Template
template
style
include elements/sw/ui/sw-ui-logo/styles/sw-ui-logo.css
img(src='[[url]]')
// Polymer element script
script(src='scripts/sw-ui-logo.js')
Możesz zobaczyć, jak wszystko jest zorganizowane w przejrzysty sposób, dodając style
i logikę w osobnych plikach. Aby włączyć nasze style do
korzystamy z instrukcji include
Jade, więc mamy wbudowany kod CSS
zawartości pliku po skompilowaniu. Element skryptu sw-ui-logo.js
będzie
uruchamianych w czasie działania.
Zależności modułowe w Bower
Zwykle zapisujemy biblioteki i inne zależności na poziomie projektu.
Jednak w powyższej konfiguracji zobaczysz element bower.json
, który znajduje się
folder elementów: zależności na poziomie elementu. Idea tego podejścia
jest to sytuacja, w której mamy wiele elementów o różnych
możemy upewnić się, że ładują się tylko te zależności, które są
w rzeczywistości. Jeśli usuniesz jakiś element, nie musisz pamiętać,
usuń zależność, ponieważ usuniesz też plik bower.json
deklarującej te zależności. Każdy element niezależnie wczytuje
zależności między nimi.
Aby jednak uniknąć powielania zależności, dołączamy plik .bowerrc
w folderze poszczególnych elementów. Informuje, gdzie przechowywać dane
aby mieć pewność, że w kodzie w hierarchii jest tylko jedna zależności
katalogu:
{
"directory" : "../../../../../bower_components"
}
Dzięki temu, jeśli wiele elementów deklaruje THREE.js
jako zależność, raz
Bower instaluje go dla pierwszego elementu i zaczyna analizować drugi,
zauważy, że ta zależność jest już zainstalowana i nie będzie
pobierz go jeszcze raz lub zduplikuj. Zachowa ona również tę zależność
plików, o ile jest co najmniej jeden element, który nadal je definiuje
to bower.json
.
Skrypt bash znajduje wszystkie pliki bower.json
w strukturze elementów zagnieżdżonych.
Następnie kolejno wchodzi do tych katalogów i wykonuje funkcję bower install
każdy z nich:
echo installing bower components...
modules=$(find /vagrant/app -type f -name "bower.json" -not -path "*node_modules*" -not -path "*bower_components*")
for module in $modules; do
pushd $(dirname $module)
bower install --allow-root -q
popd
done
Szybki nowy szablon elementu
Za każdym razem, gdy chcesz utworzyć nowy element, może minąć trochę czasu: generowanie z podstawową strukturą plików i właściwymi nazwami. Wykorzystujemy więc Slush, aby napisać prosty generator elementów.
Możesz wywołać ten skrypt z poziomu wiersza poleceń:
$ slush element path/to/your/element-name
Zostanie utworzony nowy element z całą strukturą i zawartością pliku.
Zdefiniowaliśmy szablony plików elementów, np. szablon pliku .jade
wygląda tak:
// Element
dom-module(id='<%= name %>')
// Template
template
style
include elements/<%= path %>/styles/<%= name %>.css
span This is a '<%= name %>' element.
// Polymer element script
script(src='scripts/<%= name %>.js')
Generator Slush zastępuje zmienne rzeczywistymi ścieżkami i nazwami elementów.
Używanie Gulp do tworzenia elementów
Gulp pozwala kontrolować proces kompilacji. W naszej strukturze, elementy, które Gulp musi wykonać, aby wykonać te czynności:
- Kompiluj elementy
.coffee
plików do folderu.js
- Kompiluj elementy
.scss
plików do folderu.css
- Kompiluj elementy
.jade
do.html
z umieszczonymi plikami.css
.
Więcej informacji:
Kompilowanie elementów .coffee
plików do folderu .js
gulp.task('elements-coffee', function () {
return gulp.src(abs(config.paths.app + '/elements/**/*.coffee'))
.pipe($.replaceTask({
patterns: [{json: getVersionData()}]
}))
.pipe($.changed(abs(config.paths.static + '/elements'), {extension: '.js'}))
.pipe($.coffeelint())
.pipe($.coffeelint.reporter())
.pipe($.sourcemaps.init())
.pipe($.coffee({
}))
.on('error', gutil.log)
.pipe($.sourcemaps.write())
.pipe(gulp.dest(abs(config.paths.static + '/elements')));
});
W krokach 2 i 3 używamy interfejsu gulp i wtyczki kompasu, by skompilować scss
na
.css
i .jade
do .html
, w podobny sposób jak do punktu 2 powyżej.
W tym elementy polimerowe
Aby uwzględnić elementy Polymer, używamy importu HTML.
<link rel="import" href="elements.html">
<!-- Polymer -->
<link rel="import" href="../bower_components/polymer/polymer.html">
<!-- Custom elements -->
<link rel="import" href="sw/sw-app/sw-app.html">
<link rel="import" href="sw/system/sw-system/sw-system.html">
<link rel="import" href="sw/system/sw-routing/sw-routing.html">
<link rel="import" href="sw/system/sw-system-version/sw-system-version.html">
<link rel="import" href="sw/system/sw-system-environment/sw-system-environment.html">
<link rel="import" href="sw/pages/sw-page-landing/sw-page-landing.html">
<link rel="import" href="sw/pages/sw-page-connection/sw-page-connection.html">
<link rel="import" href="sw/pages/sw-page-calibration/sw-page-calibration.html">
<link rel="import" href="sw/pages/sw-page-experience/sw-page-experience.html">
<link rel="import" href="sw/ui/sw-preloader/sw-preloader.html">
<link rel="import" href="sw/ui/sw-ui-overlay/sw-ui-overlay.html">
<link rel="import" href="sw/ui/sw-ui-button/sw-ui-button.html">
<link rel="import" href="sw/ui/sw-ui-menu/sw-ui-menu.html">
Optymalizacja pierwiastków polimerowych do produkcji
Duży projekt może zawierać dużo elementów polimerowych. W naszym
tych projektów jest ponad pięćdziesiąt. Jeśli okaże się, że każdy element ma
oddzielny plik .js
i niektóre z bibliotekami, do których odwołują się biblioteki, staje się on większy niż
100 oddzielnych plików. Oznacza to, że przeglądarka musi wysyłać wiele żądań,
z utratą skuteczności. Podobnie jak w procesie konkatenacji i minifikacji
w przypadku kompilacji Angular, „wulkanizujemy” projekt Polymer
do produkcji.
Vulcanize to narzędzie polimerowe, które spłaszcza drzewo zależności do pojedynczego pliku HTML, zmniejszając liczby żądań. Jest to szczególnie przydatne w przeglądarkach, które nie natywną obsługę komponentów sieciowych.
CSP (Content Security Policy) i polimer
Podczas tworzenia bezpiecznych aplikacji internetowych należy wdrożyć CSP. CSP to zestaw reguł, które zapobiegają atakom typu cross-site scripting (XSS): uruchamianie skryptów z niebezpiecznych źródeł lub wykonywanie wbudowanych skryptów z plików HTML.
Wygenerowano ten zoptymalizowany, połączony i zminifikowany plik .html
.
firmy Vulcanize zawiera kod JavaScript w źródle niezgodnym z CSP.
. Aby go rozwiązać, używamy narzędzia
Wyraźniejszy.
Crisper rozdziela wbudowane skrypty z pliku HTML i umieszcza je w jednym,
zewnętrznego pliku JavaScript pod kątem zgodności z CSP. W tym samym czasie mijamy
pliki HTML za pomocą Crisper. Zostaną utworzone 2 pliki: elements.html
oraz
elements.js
W aplikacji elements.html
zajmuje się również wczytywaniem
wygenerował(a) elements.js
.
Struktura logiczna aplikacji
W Polymer pierwiastki mogą być wszystko, od niewizualnej użyteczności po małe, samodzielnych i wielokrotnego użytku elementów interfejsu (np. przycisków) i większych modułów, takich jak „strony” a nawet tworzyć pełne aplikacje.

Przetwarzanie końcowe z użyciem architektury Polymer i architektura nadrzędny-podrzędny
W każdym potoku graficznym 3D zawsze jest ostatni etap, w którym efekty są dodawane do całego zdjęcia jako nakładki. To jest i obejmuje takie efekty, jak poświaty, promienie boskie, głębi ostrości, efektu bokeh, rozmycia itp. Efekty są łączone i stosowane do różne elementy w zależności od sposobu budowania sceny. W THREE.js może utworzyć niestandardowy program do cieniowania do przetwarzania końcowego w JavaScripcie lub jest to możliwe dzięki strukturze Polymer.
Jeśli spojrzymy na kod HTML elementu pośredniczącego:
<dom-module id="sw-experience-postprocessor">
<!-- Template-->
<template>
<sw-experience-effect-bloom class="effect"></sw-experience-effect-bloom>
<sw-experience-effect-dof class="effect"></sw-experience-effect-dof>
<sw-experience-effect-vignette class="effect"></sw-experience-effect-vignette>
</template>
<!-- Polymer element script-->
<script src="scripts/sw-experience-postprocessor.js"></script>
</dom-module>
Efekty określamy jako zagnieżdżone elementy Polymer w ramach wspólnej klasy. Następnie:
w sw-experience-postprocessor.js
robimy to:
effects = @querySelectorAll '.effect'
@composer.addPass effect.getPass() for effect in effects
Używamy funkcji HTML i funkcji querySelectorAll
JavaScriptu, aby znaleźć wszystkie
efektów zagnieżdżonych jako elementy HTML w procesorze postów w kolejności,
w których zostały określone. Następnie przeprowadzamy je iterację i dodajemy do narzędzia kompozycyjnego.
Teraz załóżmy, że chcemy usunąć efekt głębi ostrości (DOF). zmienić kolejność kwitnienia kwiatów i efekty winietowe. Wystarczy, że zmodyfikujemy może to być np. definicja podmiotu przetwarzającego dane osobowe, np.:
<dom-module id="sw-experience-postprocessor">
<!-- Template-->
<template>
<sw-experience-effect-vignette class="effect"></sw-experience-effect-vignette>
<sw-experience-effect-bloom class="effect"></sw-experience-effect-bloom>
</template>
<!-- Polymer element script-->
<script src="scripts/sw-experience-postprocessor.js"></script>
</dom-module>
a scena po prostu się uruchomi, nie zmieniając ani jednego wiersza rzeczywistego kodu.
pętla renderowania i pętli aktualizacji w narzędziu Polymer;
Dzięki Polymer można także elegancko podejść do renderowania i aktualizacji silnika.
Utworzyliśmy element timer
, który korzysta z metody requestAnimationFrame
i oblicza
takie jak bieżący czas (t
) i czas delta, czyli czas, który upłynął od
ostatnia klatka (dt
):
Polymer
is: 'sw-timer'
properties:
t:
type: Number
value: 0
readOnly: true
notify: true
dt:
type: Number
value: 0
readOnly: true
notify: true
_isRunning: false
_lastFrameTime: 0
ready: ->
@_isRunning = true
@_update()
_update: ->
if !@_isRunning then return
requestAnimationFrame => @_update()
currentTime = @_getCurrentTime()
@_setT currentTime
@_setDt currentTime - @_lastFrameTime
@_lastFrameTime = @_getCurrentTime()
_getCurrentTime: ->
if window.performance then performance.now() else new Date().getTime()
Następnie używamy wiązania danych, by powiązać właściwości t
i dt
z
wyszukiwarka (experience.jade
):
sw-timer(
t='{ % templatetag openvariable % }t}}',
dt='{ % templatetag openvariable % }dt}}'
)
sw-experience-engine(
t='[t]',
dt='[dt]'
)
Słuchamy zmian t
i dt
w wyszukiwarce i za każdym razem,
wartości zmienią się, funkcja _update
zostanie wywołana:
Polymer
is: 'sw-experience-engine'
properties:
t:
type: Number
dt:
type: Number
observers: [
'_update(t)'
]
_update: (t) ->
dt = @dt
@_physics.update dt, t
@_renderer.render dt, t
Jeśli jednak zależy Ci na FPS, warto usunąć dane Polymer powiązanie w pętli renderowania w celu zaoszczędzenia kilku milisekund wymaganych do powiadomienia o zmianach. Wprowadziliśmy takie obserwacje:
sw-timer.coffee
:
addUpdateListener: (listener) ->
if @_updateListeners.indexOf(listener) == -1
@_updateListeners.push listener
return
removeUpdateListener: (listener) ->
index = @_updateListeners.indexOf listener
if index != -1
@_updateListeners.splice index, 1
return
_update: ->
# ...
for listener in @_updateListeners
listener @dt, @t
# ...
Funkcja addUpdateListener
przyjmuje wywołanie zwrotne i zapisuje je w swoim
tablicy wywołań zwrotnych. Następnie w pętli aktualizacji powtarzamy każde wywołanie zwrotne.
uruchamiamy je bezpośrednio za pomocą argumentów dt
i t
, omijając wiązanie danych lub
uruchamiania zdarzeń. Gdy wywołanie zwrotne nie jest już aktywne, dodaliśmy
Funkcja removeUpdateListener
, która umożliwia usunięcie wcześniej dodanego wywołania zwrotnego.
Miecz świetlny w THREE.js
THREE.js ogranicza szczegółowość technologii WebGL i pozwala nam skupić się dotyczącą problemu. Naszym problemem jest walka z szturmowcami i potrzebujemy broni. Zbudujmy więc miecz świetlny.
Świecące ostrze odróżnia miecz świetlny od dwuręczna broń. Składa się głównie z dwóch części: belki i szlaku co jest widoczne przy jej przesuwaniu. Zbudowaliśmy go w jasnym kształcie oraz dynamiczny ślad, który podąża za nim w miarę ruchu gracza.
Ostrze
Ostrze składa się z 2 podłoży. Wewnętrzna i zewnętrzna. Obie sieci są siatkami THREE.js zawierającymi odpowiednie materiały.
Wewnętrzne ostrze
Wewnętrzną osłonę użyliśmy niestandardowego materiału z dostosowanym cieniowaniem. Śr wybierz linię utworzoną przez dwa punkty i rzutuj linię między tymi dwoma punktami punkty w samolocie. Ten samolot to Ty sterujesz, gdy i grają na komórce. Daje to poczucie głębi i orientacji do miecza.
Aby stworzyć wrażenie świecącego się przedmiotu, patrzymy na ortogonalna odległość dowolnego punktu na płaszczyźnie od głównego łączący dwa punkty A i B, jak poniżej. Im bliżej punktu tym jaśniejszy jest obraz.

Poniższe źródło pokazuje, jak obliczamy vFactor
, aby kontrolować intensywność
w cieniowaniu wierzchołków, aby zmieszać je ze sceną
program do cieniowania fragmentów
THREE.LaserShader = {
uniforms: {
"uPointA": {type: "v3", value: new THREE.Vector3(0, -1, 0)},
"uPointB": {type: "v3", value: new THREE.Vector3(0, 1, 0)},
"uColor": {type: "c", value: new THREE.Color(1, 0, 0)},
"uMultiplier": {type: "f", value: 3.0},
"uCoreColor": {type: "c", value: new THREE.Color(1, 1, 1)},
"uCoreOpacity": {type: "f", value: 0.8},
"uLowerBound": {type: "f", value: 0.4},
"uUpperBound": {type: "f", value: 0.8},
"uTransitionPower": {type: "f", value: 2},
"uNearPlaneValue": {type: "f", value: -0.01}
},
vertexShader: [
"uniform vec3 uPointA;",
"uniform vec3 uPointB;",
"uniform float uMultiplier;",
"uniform float uNearPlaneValue;",
"varying float vFactor;",
"float getDistanceFromAB(vec2 a, vec2 b, vec2 p) {",
"vec2 l = b - a;",
"float l2 = dot( l, l );",
"float t = dot( p - a, l ) / l2;",
"if( t < 0.0 ) return distance( p, a );",
"if( t > 1.0 ) return distance( p, b );",
"vec2 projection = a + (l * t);",
"return distance( p, projection );",
"}",
"vec3 getIntersection(vec4 a, vec4 b) {",
"vec3 p = a.xyz;",
"vec3 q = b.xyz;",
"vec3 v = normalize( q - p );",
"float t = ( uNearPlaneValue - p.z ) / v.z;",
"return p + (v * t);",
"}",
"void main() {",
"vec4 a = modelViewMatrix * vec4(uPointA, 1.0);",
"vec4 b = modelViewMatrix * vec4(uPointB, 1.0);",
"if(a.z > uNearPlaneValue) a.xyz = getIntersection(a, b);",
"if(b.z > uNearPlaneValue) b.xyz = getIntersection(a, b);",
"a = projectionMatrix * a; a /= a.w;",
"b = projectionMatrix * b; b /= b.w;",
"vec4 p = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
"gl_Position = p;",
"p /= p.w;",
"float d = getDistanceFromAB(a.xy, b.xy, p.xy) * gl_Position.z;",
"vFactor = 1.0 - clamp(uMultiplier * d, 0.0, 1.0);",
"}"
].join( "\n" ),
fragmentShader: [
"uniform vec3 uColor;",
"uniform vec3 uCoreColor;",
"uniform float uCoreOpacity;",
"uniform float uLowerBound;",
"uniform float uUpperBound;",
"uniform float uTransitionPower;",
"varying float vFactor;",
"void main() {",
"vec4 col = vec4(uColor, vFactor);",
"float factor = smoothstep(uLowerBound, uUpperBound, vFactor);",
"factor = pow(factor, uTransitionPower);",
"vec4 coreCol = vec4(uCoreColor, uCoreOpacity);",
"vec4 finalCol = mix(col, coreCol, factor);",
"gl_FragColor = finalCol;",
"}"
].join( "\n" )
};
Poświata zewnętrznego ostrza
Aby uzyskać zewnętrzną poświatę, renderujemy w osobnym buforze renderowania i używamy funkcji z efektem kwitnienia kwiatu i zmieszać go z końcowym zdjęciem, aby uzyskać pożądany blask. Na grafice poniżej widać 3 różne regiony, potrzebne, by mieć porządną sztylet. Biały, środkowy rdzeń niebieska poświata z zewnętrzną poświatą.

Szlak mieczy świetlnych
Ślad miecza świetlnego jest kluczem do uzyskania pełnego efektu w takiej postaci, w jakiej widać oryginał. z serii Gwiezdne Wojny. Wyruszyliśmy na szlak z wielbiciele stworzonych trójkątów dynamicznie w zależności od ruchu miecza świetlnego. Ci fani są są przekazywane do późniejszego ulepszenia wizualnego. Aby utworzyć mamy geometrię wentylatora. i bieżące przekształcenie generujemy w siatce nowy trójkąt, z ogona po określonej długości.


Gdy mamy już siatkę, przypisujemy do niej prosty materiał i przekazujemy postprocesorem w celu uzyskania płynnego efektu. Używamy tego samego efektu kwitnienia na zewnątrz łopatki poświatę i powstaliśmy gładki ślad, jak widać:

Świecące wokół szlaku
Aby dokończyć ostatni element, musieliśmy zmierzyć się ze światłem które można stworzyć na wiele sposobów. Nasze rozwiązanie nie podawać tutaj szczegółów. Ze względu na wydajność trzeba było utworzyć niestandardowy program do cieniowania dla tego bufora, który tworzy gładką krawędź wokół bufor renderowania. Następnie łączymy dane wyjściowe w ostatecznym renderowaniu, zobacz poświatę, która otacza szlak:

Podsumowanie
Polymer to zaawansowana biblioteka i koncepcja (tak samo jak WebKomponent ogólne). To, co na nim zrobisz, zależy tylko od Ciebie. Może to być wszystko: z prostego przycisku do pełnowymiarowej aplikacji WebGL. W poprzednich rozdziałach omówiliśmy kilka porad i wskazówek, jak efektywnie korzystać z technologii Polymer, w środowisku produkcyjnym oraz jak tworzyć bardziej złożone moduły, które również zapewniają cóż. Pokazaliśmy również, jak stworzyć ładny miecz świetlny w WebGL. Więc jeśli to wszystko połączysz, pamiętaj o wulkanizacji pierwiastków polimerowych przed wdrożeniem na serwerze produkcyjnym oraz Jeśli chcesz zachować zgodność z zasadami CSP, możesz liczyć na siebie.
