// Hlavní komponent aplikace
class ExampleApp1 extends React.Component {
// konstruktor komponentu
constructor(props) {
super(props);
// stav komponentu
this.state = {
text: 'Hello world!',
diffuseColor: '#dd3300',
outlineColor: '#ee9966'
};
// objekt pro zprostředkování změn stavu
this.binder = OP({});
}
// metoda volaná při změně stavu nebo props
componentDidUpdate(prevProps, prevState, snapshot) {
// synchronizace se zprostředkujícím objektem
this.binder.set(this.state);
}
// metoda pro získání odkazu na funkci pro změnu stavu
getStateUpdater() {
var that = this;
return (updater) => {
return (value) => {
that.setState((state) => {
updater(value, state);
return state;
});
}
}
}
// metoda volaná při připojení komponentu
componentDidMount() {
// vytvoření scény
var scene = setupScene(this.canvas);
// načtení fontu
var font = new pc.Asset('Arial.json', "font", { url: "../../../libs/playcanvas/Arial.json" });
var that = this;
scene.assets.on('load', function () {
// entita pro text
var text = new pc.Entity("text", scene);
text.addComponent("element", {
type: "text",
anchor: [0, 0, 0, 0],
pivot: [0.5, 0.5],
fontSize: 16,
fontAsset: font,
opacity: 1,
color: [0, 0, 1],
shadowColor: [0, 0, 1],
shadowOffset: [0.2, -0.2],
text: that.state.text
});
text.setLocalScale(1 / 32, 1 / 32, 1 / 32);
scene.root.addChild(text);
// propojení scény se stavem React komponentu pomocí mapy
var state = that.getStateUpdater();
that.binder.map(MAP.TEXT.to3DModel, text).set(that.state);
OP(text).map({
text: state((v, s) => s.text = v),
color: state((v, s) => s.diffuseColor = v),
shadowColor: state((v, s) => s.outlineColor = v)
}, {});
});
scene.assets.add(font);
scene.assets.load(font);
}
// metoda pro vykreslení plátna
renderScene() {
return (
<div className="fixed-ratio">
<canvas ref={ref => (this.canvas = ref)}></canvas>
</div>
);
}
// metoda pro vykreslení ovládacích prvků
renderControls() {
return (
<form>
<div>
<label>Text</label>
<!-- data-binding textu do vstupu, listener pro změnu -->
<input type="text"
value={this.state.text}
onChange={(e) => this.setState({ text: e.target.value })}
/>
</div>
<div className="grid">
<div>
<label>Základní barva</label>
<div>
<input
type="color"
value={this.state.diffuseColor}
onChange={(e) => this.setState({ diffuseColor: e.target.value })}
/>
<span style={{ color: this.state.diffuseColor }}>{this.state.diffuseColor}</span>
</div>
</div>
<div>
<label>Barva okraje</label>
<div>
<input
type="color"
value={this.state.outlineColor}
onChange={(e) => this.setState({ outlineColor: e.target.value })}
/>
<span style={{ color: this.state.outlineColor }}>{this.state.outlineColor}</span>
</div>
</div>
</div>
</form>
);
}
// hlavní vykreslovací metoda
render() {
return (
<div>
{this.renderScene()}
{this.renderControls()}
</div>
);
}
}
// vykreslení aplikace do HTML elementu
ReactDOM.render(<ExampleApp1 />, document.getElementById('app1'));
// React JSX zkompilovaný pomocí Babel.js
function () {
var ExampleApp1 = function (_React$Component) {
_inherits(ExampleApp1, _React$Component);
function ExampleApp1(props) {
_classCallCheck(this, ExampleApp1);
var _this = _possibleConstructorReturn(this, (ExampleApp1.__proto__ || Object.getPrototypeOf(ExampleApp1)).call(this, props));
_this.state = {
text: 'Hello world!',
diffuseColor: '#dd3300',
outlineColor: '#ee9966'
};
_this.binder = OP({});
return _this;
}
_createClass(ExampleApp1, [{
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps, prevState, snapshot) {
this.binder.set(this.state);
}
}, {
key: 'getStateUpdater',
value: function getStateUpdater() {
var that = this;
return function (updater) {
return function (value) {
that.setState(function (state) {
updater(value, state);
return state;
});
};
};
}
}, {
key: 'componentDidMount',
value: function componentDidMount() {
var scene = setupScene(this.canvas);
var font = new pc.Asset('Arial.json', "font", { url: "../../../libs/playcanvas/Arial.json" });
var that = this;
scene.assets.on('load', function () {
var text = new pc.Entity("text", scene);
text.addComponent("element", {
type: "text",
anchor: [0, 0, 0, 0],
pivot: [0.5, 0.5],
fontSize: 16,
fontAsset: font,
opacity: 1,
color: [0, 0, 1],
shadowColor: [0, 0, 1],
shadowOffset: [0.2, -0.2],
text: that.state.text
});
text.setLocalScale(1 / 32, 1 / 32, 1 / 32);
scene.root.addChild(text);
window.text = text;
var state = that.getStateUpdater();
that.binder.map(MAP.TEXT.to3DModel, text).set(that.state);
OP(text).map({
text: state(function (v, s) {
return s.text = v;
}),
color: state(function (v, s) {
return s.diffuseColor = v;
}),
shadowColor: state(function (v, s) {
return s.outlineColor = v;
})
}, {});
});
scene.assets.add(font);
scene.assets.load(font);
}
}, {
key: 'renderScene',
value: function renderScene() {
var _this2 = this;
return React.createElement(
'div',
{ className: 'fixed-ratio' },
React.createElement('canvas', { ref: function ref(_ref) {
return _this2.canvas = _ref;
} })
);
}
}, {
key: 'renderControls',
value: function renderControls() {
var _this3 = this;
return React.createElement(
'form',
{ className: 'uk-form-stacked' },
React.createElement(
'div',
{ className: 'uk-margin-small-top' },
React.createElement(
'label',
{ className: 'uk-form-label' },
'Text'
),
React.createElement(
'div',
{ className: 'uk-form-controls' },
React.createElement('input', { type: 'text', value: this.state.text, className: 'uk-input', onChange: function onChange(e) {
return _this3.setState({ text: e.target.value });
} })
)
),
React.createElement(
'div',
{ className: 'uk-margin-small-top' },
React.createElement(
'div',
{ className: 'uk-child-width-1-2 uk-grid-small', 'uk-grid': '' },
React.createElement(
'div',
null,
React.createElement(
'label',
{ className: 'uk-form-label' },
'Z\xE1kladn\xED barva'
),
React.createElement(
'div',
{ className: 'uk-form-controls' },
React.createElement('input', { type: 'color', value: this.state.diffuseColor, onChange: function onChange(e) {
return _this3.setState({ diffuseColor: e.target.value });
} }),
React.createElement(
'span',
{ style: { color: this.state.diffuseColor } },
this.state.diffuseColor
)
)
),
React.createElement(
'div',
null,
React.createElement(
'label',
{ className: 'uk-form-label' },
'Barva okraje'
),
React.createElement(
'div',
{ className: 'uk-form-controls' },
React.createElement('input', { type: 'color', value: this.state.outlineColor, onChange: function onChange(e) {
return _this3.setState({ outlineColor: e.target.value });
} }),
React.createElement(
'span',
{ style: { color: this.state.outlineColor } },
this.state.outlineColor
)
)
)
)
)
);
}
}, {
key: 'render',
value: function render() {
return React.createElement(
'div',
null,
this.renderScene(),
this.renderControls()
);
}
}]);
return ExampleApp1;
}(React.Component);
ReactDOM.render(React.createElement(ExampleApp1, null), document.getElementById('app1'));
}
// Hlavní komponent
class ExampleApp2 extends React.Component {
// stav komponentu
constructor(props) {
super(props);
this.state = { boxes: [] };
}
// metoda pro smazání kostky
remove(index) {
var that = this;
return () => {
that.group.setLocalPosition((-(that.state.boxes.length - 2) * 0.2), 0, 0);
return that.setState((state) => {
state.boxes = state.boxes.filter((v, i) => i != index);
return state;
});
}
}
// metoda pro přidání kostky
add() {
this.setState((state) => {
state.boxes.push({
position: { x: 0, y: 0, z: 0 },
size: { x: 1, y: 1, z: 1 },
color: getRandomColor(),
key: getUniqueHash()
});
return state;
});
};
// metoda volaná při připojení komponentu
componentDidMount() {
// inicializace scény
this.scene = setupScene(this.canvas);
this.group = new pc.Entity("group", this.scene);
this.group.setLocalScale(0.2, 0.2, 0.2);
this.scene.root.addChild(this.group);
this.add();
this.add();
}
// metoda pro získání funkce pro změnu stavu kostky
getBoxStateUpdater() {
var that = this;
return (indexReader) => {
return (updater) => {
return (value) => {
value = value.target && value.target.value ? value.target.value : value;
that.setState((state) => {
updater(value, state.boxes[indexReader()]);
return state;
});
}
}
}
}
// metoda pro vykreslování
render() {
return (
<div>
<div className="fixed-ratio">
<canvas ref={ref => (this.canvas = ref)}></canvas>
</div>
<form>
<ul>
<!-- cyklus pro ovládání jednotlivých kostek -->
{this.state.boxes.map((box, index) => (
<BoxControl instance={box} index={index} key={box.key} group={this.group} scene={this.scene} updater={this.getBoxStateUpdater()} remove={this.remove(index)} />
))}
</ul>
<a onClick={(e) => this.add()}> Přidat nový objekt</a>
</form>
</div>
);
}
}
// vykreslení aplikace do HTML elementu
ReactDOM.render(<ExampleApp2 />, document.getElementById('app2'));
// Komponent pro ovládání kostky
class BoxControl extends React.Component {
// konstruktor komponentu
constructor(props) {
super(props);
this.cube;
// objekt pro zprostředkování změn stavu
this.binder = OP({});
}
// metoda volaná při změně stavu nebo props
componentDidUpdate(prevProps, prevState, snapshot) {
const { index, instance } = this.props;
if (prevProps.index > index) {
// přepočet pozice a indexu
this.binder.set({ index });
var original = this.cube.getLocalPosition();
original.x = original.x - 2;
this.cube.setLocalPosition(original);
} else {
this.binder.set(instance);
}
}
// pomocná metoda pro získání indexu
getIndexReader() {
return () => this.props.index;
}
// metoda volaná při připojení komponentu
componentDidMount() {
const { index, instance, group, scene, updater } = this.props;
const { color } = instance;
var state = updater(this.getIndexReader());
// vytvoření kostky
var cube = new pc.Entity("box" + index, scene);
cube.addComponent("model", { type: "box" });
var material = new pc.StandardMaterial();
material.update();
cube.model.material = material;
group.addChild(cube);
// propojení 3D kostky s React komponentem pomocí mapy
this.binder.map(MAP.CUBE.to3DModel, cube).set({ index }).set(instance);
// relativní pozice kostky
group.setLocalPosition(-index * 0.2, 0, 0);
cube.setLocalPosition(index * 2, 0, 0);
this.cube = cube;
}
// metoda volaná při odpojení komponentu
componentWillUnmount() {
// odstranění 3D kostky
this.props.group.removeChild(this.cube);
this.cube.destroy();
}
// metoda pro vykreslování
render() {
const { index, instance, updater, remove } = this.props;
const { position, size, color } = instance;
const onChange = (handler) => updater(this.getIndexReader())((value, box) => { handler(box, value); });
return (
<li>
<div>
<div className="grid">
<div>
<label>Objekt {index + 1}</label>
<div>
<input type="color" value={color} onChange={onChange((box, value) => box.color = value)} />
<span style={{ color: color }}> { color } </span>
</div>
</div>
<div>
<div className="grid">
<div>
<label>↔ W { Math.floor(size.x * 100) }%</label>
<input type="range" value={size.x} min={0.1} max={2} step={0.1} onChange={onChange((box, value) => box.size.x = value)} />
</div>
<div>
<label>↕ H { Math.floor(size.y * 100) }%</label>
<input type="range" value={size.y} min={0.1} max={2} step={0.1} onChange={onChange((box, value) => box.size.y = value)} />
</div>
<div>
<label>⤢ L { Math.floor(size.z * 100) }%</label>
<input type="range" value={size.z} min={0.1} max={2} step={0.1} onChange={onChange((box, value) => box.size.z = value)} />
</div>
<div>
<label>→ X { position.x }</label>
<input type="range" value={position.x} min={-1} max={1} step={0.1} onChange={onChange((box, value) => box.position.x = value)} />
</div>
<div>
<label">↑ Y { position.y }</label>
<input type="range" value={position.y} min={-1} max={1} step={0.1} onChange={onChange((box, value) => box.position.y = value)} />
</div>
<div>
<label">↗ Z { position.z }</label>
<input type="range" value={position.z} min={-1} max={1} step={0.1} onChange={onChange((box, value) => box.position.z = value)} />
</div>
</div>
</div>
<div>
<a onClick={() => remove()}>Smazat</a>
</div>
</div>
</div>
</li>
);
}
}
// Předpoklad: Vytvořená základní scéna s kamerou a světelným zdrojem
function (scene) {
var rotationNode = new pc.Entity("rotated", scene);
var positionNode = new pc.Entity("positioned", scene);
// příprava materiálů
var seatFreeMaterial = new pc.StandardMaterial({});
seatFreeMaterial.diffuse = new pc.Color(0, 0.6, 1);
seatFreeMaterial.emissive = new pc.Color(0, 0.33, 0.9);
seatFreeMaterial.update();
var seatTakenMaterial = new pc.StandardMaterial({});
seatTakenMaterial.diffuse = new pc.Color(1, 0.6, 0);
seatTakenMaterial.emissive = new pc.Color(0.9, 0.33, 0);
seatTakenMaterial.update();
var backFreeMaterial = new pc.StandardMaterial({});
backFreeMaterial.diffuse = new pc.Color(0, 0.53, 1);
backFreeMaterial.emissive = new pc.Color(0, 0.33, 0.9);
backFreeMaterial.update();
var backTakenMaterial = new pc.StandardMaterial({});
backTakenMaterial.diffuse = new pc.Color(1, 0.53, 0);
backTakenMaterial.emissive = new pc.Color(0.9, 0.33, 0);
backTakenMaterial.update();
var holderFreeMaterial = new pc.StandardMaterial({});
holderFreeMaterial.diffuse = new pc.Color(0, 0.33, 0.9);
holderFreeMaterial.emissive = new pc.Color(0, 0.33, 0.9);
holderFreeMaterial.update();
var holderTakenMaterial = new pc.StandardMaterial({});
holderTakenMaterial.diffuse = new pc.Color(0.9, 0.33, 0);
holderTakenMaterial.emissive = new pc.Color(0.9, 0.33, 0);
holderTakenMaterial.update();
// Sedadlo
function Seat(row, column) {
var seat = new pc.Entity("seat" + row + "x" + column, scene);
seat.setLocalPosition(column * 1.4, row * -0.5, row * 3);
var bottom = new pc.Entity("bottom" + row + "x" + column, scene);
bottom.addComponent("model", { type: "box" });
bottom.setLocalScale(0.6, 0.5, 0.6);
var right = new pc.Entity("right" + row + "x" + column, scene);
right.addComponent("model", { type: "box" });
right.setLocalScale(0.2, 0.8, 0.6);
right.setLocalPosition(0.4, 0.15, 0);
var left = new pc.Entity("left" + row + "x" + column, scene);
left.addComponent("model", { type: "box" });
left.setLocalScale(0.2, 0.8, 0.6);
left.setLocalPosition(-0.4, 0.15, 0);
var back = new pc.Entity("back" + row + "x" + column, scene);
back.addComponent("model", { type: "box" });
back.setLocalScale(0.8, 1.2, 0.1);
back.setLocalPosition(0, 0.35, -0.25);
// předání akce pro výběr sedadla do Redux úložiště
function click() {
store.dispatch(pickSeatAction(row, column));
}
// připojení listeneru na kliknutí
bottom.onclick = click;
right.onclick = click;
left.onclick = click;
back.onclick = click;
// metoda pro přepnutí
this.switch = function (state) {
if (state) {
bottom.model.material = seatFreeMaterial;
right.model.material = holderFreeMaterial;
left.model.material = holderFreeMaterial;
back.model.material = backFreeMaterial;
} else {
bottom.model.material = seatTakenMaterial;
right.model.material = holderTakenMaterial;
left.model.material = holderTakenMaterial;
back.model.material = backTakenMaterial;
}
};
seat.addChild(bottom);
seat.addChild(right);
seat.addChild(left);
seat.addChild(back);
positionNode.addChild(seat);
}
// vytvoření sedadel na scéně
var state = store.getState();
var seats = [];
for (var r = 0; r < state.seats.length; r++) {
seats.push([]);
for (var c = 0; c < state.seats[r].length; c++) {
var seat = new Seat(r, c);
seat.switch(state.seats[r][c]);
seats[r].push(seat);
}
}
positionNode.setLocalPosition(-state.seats[0].length / 2 - 0.5, 0, -state.seats.length * 1.5);
rotationNode.addChild(positionNode);
rotationNode.setLocalScale(0.2, 0.2, 0.2);
rotationNode.rotateLocal(30, 0, 0);
scene.root.addChild(rotationNode);
// připojení na Redux úložiště
store.subscribe(function () {
state = store.getState();
for (var r = 0; r < state.seats.length; r++) {
for (var c = 0; c < state.seats[r].length; c++) {
seats[r][c].switch(state.seats[r][c]);
}
}
});
}
// mapování obdrženého stavu na props
const mapStateToProps = (state) => ({ seats: state.seats, free: state.free, taken: state.taken });
// mapování dispatch funkce na props
const mapDispatchToProps = (dispatch, ownProps) => ({
pickSeat: (row, column) => dispatch(pickSeatAction(row, column))
});
// Bezstavový komponent tlačítka pro výběr místa
const SeatButton = ({ row, column, enabled, picker }) => {
const click = () => picker(row, column);
return (
<input type='button' className={enabled ? 'seat-free' : ' seat-taken'} onClick={click} value={'S ' + (column + 1)} />
);
}
// Bezstavový hlavní komponent aplikace
const SeatApp = ({ seats, free, taken, pickSeat }) => {
return (
<form>
<h3>Vyberte sedadlo</h3>
<table>
<caption>Obsazeno je {taken}, zbývá {free} volných míst</caption>
<tbody>
{seats.map((row, rowIndex) => (
<tr key={rowIndex}>
<td>Řada {rowIndex + 1}</td>
{row.map((enabled, colIndex) => (
<td key={rowIndex + 'x' + colIndex}>
<SeatButton row={rowIndex} column={colIndex} enabled={enabled} picker={pickSeat} />
</td>
))}
</tr>
))}
</tbody>
</table>
</form>
);
}
// Hlavní komponent napojený na Redux úložiště
const ConnectedSeatApp = ReactRedux.connect(mapStateToProps, mapDispatchToProps)(SeatApp);
// vykreslení aplikace do HTML elementu s obalením do poskytovatele Redux úložiště
ReactDOM.render(<ReactRedux.Provider store={store}><ConnectedSeatApp /></ReactRedux.Provider>, document.getElementById('app3'));
// Akce pro výběr sedadla
function pickSeatAction(row, column) { return { type: 'PICK_SEAT', row, column }; }
// Reducer úložiště
function SeatReducer(state, action) {
// výchozí stav
if (typeof state === 'undefined') {
return {
seats: [
[true, true, true, true, true, true, true],
[true, true, true, true, true, true, true],
[true, true, true, true, true, true, true],
[true, true, true, true, true, true, true]
],
free: 28,
taken: 0
}
}
// zpracování akcí
switch (action.type) {
case 'PICK_SEAT':
const newState = Object.assign({}, state);
newState.seats[action.row][action.column] = !state.seats[action.row][action.column];
newState.seats[action.row][action.column] ? (newState.free++ , newState.taken--) : (newState.taken++ , newState.free--);
return newState;
default:
return state;
}
}
// Úložiště
var store = Redux.createStore(SeatReducer);