Marzec już za pasem a mój projekt w dalszym ciągu nie ma solidnych fundamentów. W tym wpisie ostatecznie rozprawię się z kwestią frameworków aby wreszcie móc pobawić się technologią VR.
W pierwszej części postu zakończyłem wstępną konfigurację webpacka i udało mi się doprowadzić do sytuacji w której mój kod pisany w reakcie i ES6, jest transformowany do VanillaJS w standardzie ES5. Może to nie dużo, ale dla laika brzmi kosmicznie nieprawdaż? Pora sprawdzić jak React poradzi sobie z A-Frame’m.
Pierwszy komponent
Nie wiem jak Wy, ale ja najlepiej wiedzę przyswajam poprzez praktykę, dlatego też bez zbędnego wstępu rozpoczynam zabawę z pierwszym komponentem, który będzie sceną. Na początek utworzę folder components
w katalogu js
, który będzie przechowywał wszystkie komponenty aplikacji. Pozbywam się też testowego pliku modules.js
, który miał na celu sprawdzenie czy wynikowa paczka jest poprawnie transformowana. Poprawiam także webpack.config.js
, ponieważ nie uwzględniłem w nim reguły do parsowania plików z rozszerzeniem .jsx
, a będę je stosował do plików komponentów. Fragment rules
będzie wyglądał następująco:
rules: [{ test: /\.(js|jsx)$/, exclude: [/node_modules/], use: [{ loader: "babel-loader", options: { presets: [ ["env", { "targets": { "browsers": ["last 2 versions", "ie >= 11", "Android >= 5"] } }], ["react"] ] } }] }]
Czas na komponent sceny. Nazwę go scene.component.jsx
i podobny schemat zachowam dla przyszłych komponentów. O to jego zawartość:
import AFrame from "aframe"; import React from "react"; const SceneComponent = () => ( <a-scene> </a-scene> ); export default SceneComponent;
Wykorzystałem tzw. stateless function, czyli funkcję bezstanową do zwrócenia pustego komponentu sceny. Na uwagę zasługuje linijka import AFrame from "aframe";
, która jest wymagana, ponieważ A-Frame musi zostać wczytany przed użyciem komponentu (zaleceniem jest umieszczenie skryptu w sekcji <head>, ale to na razie pomijam). Jeszcze tylko drobna modyfikacja pliku app.js
pod kątem nazewnictwa i jesteśmy w domu:
import React from "react"; import ReactDOM from "react-dom"; import SceneComponent from "./components/scene.component.jsx"; ReactDOM.render(<SceneComponent />, document.getElementById("vrath-scene"));
A-Frame „kompiluje” znacznik <a-scene>
do canvasu, a więc do uruchomienia przykładu będę potrzebował serwera. Na szczęście także i w tym przypadku mogę liczyć na webpacka. Twórcy tego narzędzia przygotowali pakiet webpack-dev-server
, który uruchamia serwer deweloperski i jednocześnie „śledzi” zmiany w projekcie, dzięki czemu jest w stanie pokazać zmiany bez odświeżania strony. Dodam więc go do projektu:
yarn add webpack-dev-server --dev
Następnie muszę zmodyfikować package.json
o wpis, który pozwoli mi uruchomić serwer. W sekcji scripts
uzupełniam o skrypt server:
"scripts": { "dev": "webpack --config webpack.config.js", "production": "webpack -p --config webpack.config.js", "server": "webpack-dev-server --config webpack.config.js --open" }
Teraz wystarczy uruchomić npm run server
aby cieszyć się serwerem webpacka. Po wpisaniu w przeglądarce adresu http://localhost:8080
widać, że element sceny został dołączony do drzewa DOM.
Więcej komponentów!
Zdecydowanie za mało atrakcyjny to przykład, dlatego uzupełnię go o elementy z wpisu wstępnego o A-Frame, czyli o kamerę i obiekt kuli. I będzie to pełnopoprawny komponent React’a:
import AFrame from "aframe"; import React from "react"; const SceneComponent = () => ( <a-scene> <a-sphere radius="100" color="#258bd6"></a-sphere> <a-camera position="0 0 500"></a-camera> </a-scene> ); export default SceneComponent;
Niebieska kula jak się patrzy! Nie chciałbym jednak aby cały projekt był w jednym pliku, więc pora na mały refaktor. Wydzielę komponent kuli i kamery do osobnych plików:
sphere.component.jsx
import React from "react"; export default () => <a-sphere radius="100" color="#258bd6"></a-sphere>;
camera.component.jsx
import React from "react"; export default () => <a-camera position="0 0 500"></a-camera>;
Z komponentu sceny wynoszę też importowanie A-Frame do app.js, ponieważ nie ma takiej potrzeby aby robić inicjalizację tego frameworka w każdym komponencie (na pewno zbyt mądre by to nie było). Natomiast sama plik sceny wygląda teraz tak:
import React from "react"; import SphereComponent from "./sphere.component.jsx"; import CameraComponent from "./camera.component.jsx"; const SceneComponent = () => ( <a-scene> <SphereComponent /> <CameraComponent /> </a-scene> ); export default SceneComponent;
Zarówno kula jak i kamera mają zaszyte właściwości (ang. properties) bezpośrednio w komponentach, co nie jest zbyt elastycznym rozwiązaniem, ponieważ nie jesteśmy w stanie tym sterować. Trzeba się tym zająć!
CDN…