Zpět

React & A-Frame

Ukázka #1 - Data-binding do 3D scény

  • // Hlavní komponent aplikace
    class ExampleApp1 extends React.Component {
    
        // konstruktor komponentu
    	constructor(props) {
    		super(props);
            // stav komponentu
    		this.state = {
    			text: 'Hello world!',
    			diffuseColor: '#dd3300'
    		};
    	}
    
        // metoda pro vykreslení scény
    	renderScene() {
    		return (
    			<div className="fixed-ratio">
    				<a-scene embedded="">
    					<a-entity position="0 0 0" camera="" look-controls=""></a-entity>
    					<!-- data-binding textu -->
    					<a-text
    						value={this.state.text} 
    						color={this.state.diffuseColor}
    						align="center"
    						position="0 0 -0.5">
    					</a-text>
    				</a-scene>
    			</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>
    					<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>
    			</form>
    		);
    	}
    
        // hlavní vykreslovací metoda
    	render() {
    		return (
    			<div>
    				{this.renderScene()}
    				{this.renderControls()}
    			</div>
    		);
    	}
    }
    
    // vykreslení aplikace do HTML elementu
    ReactDOM.render(<ExampleApp1 />, document.getElementById('app1'));
    
  • 
    

Ukázka #2 - Práce s polem

  • // Bezstavový komponent pro 3D kostku
    const BoxAFrame = ({ instance, index }) => {
    	const { position, size, color } = instance;
    	return (
    		<a-entity position={(index * 2) + ' 0 0'}>
    			<a-box
    				color={color}
    				position={position.x + ' ' + position.y + ' ' + position.z}
    				width={size.x}
    				height={size.y}
    				depth={size.z}>
    			</a-box>
    		</a-entity>
    	);
    }
    
    // Bezstavový komponent pro ovládání kostky
    const BoxControl = ({ instance, index, onChange, remove }) => {
    	const { position, size, color } = instance;
    	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>
    	);
    }
    
    // Hlavní komponent aplikace
    class ExampleApp2 extends React.Component {
    
    	// konstruktor komponentu
    	constructor(props) {
    		super(props);
    		// stav komponentu
    		this.state = {
    			boxes: []
    		};
    	}
    
    	// metoda pro získání funkce pro změnu stavu kostky
    	changeState(index) {
    		var that = this;
    		return (boxStateHandler) => {
    			return (e) => {
    				var value = e.target.value;
    				return that.setState((state) => {
    					boxStateHandler(state.boxes[index], value);
    					return state;
    				});
    			}
    		}
    	}
    
    	// metoda volaná při připojení komponentu
    	componentDidMount() {
    		this.add();
    		this.add();
    	}
    
    	// metoda pro smazání kostky
    	remove(index) {
    		var that = this;
    		return () => {
    			return that.setState((state) => {
    				state.boxes.splice(index, 1);
    				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: "#ff9900" // nebo jiná náhodná barva
    			});
    			return state;
    		});
    	};
    
    	// hlavní vykreslovací metoda
    	render() {
    		return (
    			<div>
    				<!-- 3D scéna -->
    				<div className="fixed-ratio">
    					<a-scene embedded="">
    						<a-entity position="0 0 0" camera="" look-controls=""></a-entity>
    						<a-entity position={(- this.state.boxes.length + 1) + ' 0 -5'}>
    						<!-- cyklus pro vykreslení 3D kostek -->
    							{this.state.boxes.map((box, index) => (
    								<BoxAFrame instance={box} index={index} key={index} />
    							))}
    						</a-entity>
    					</a-scene>
    				</div>
    				<!-- formulář pro ovládání -->
    				<form>
    					<ul>
    						<!-- cyklus pro ovládání jednotlivých kostek -->
    						{this.state.boxes.map((box, index) => (
    							<BoxControl instance={box} index={index} key={index} onChange={this.changeState(index)} 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'));
    

Ukázka #3 - Pokročilejší scéna

  • // Bezstavový komponent 3D sedadla
    const Seat = ({ row, column, enabled, picker }) => {
    	const click = () => picker(row, column);
    	return (
    		<a-entity position={(column * 1.4) + ' ' + (row * -0.5) + ' ' + (row * 2.5)}>
    			<a-box color={enabled ? '#0099ff' : '#ff9900'}
    				width="0.6"
    				height="0.5"
    				depth="0.5"
    				position="0 0 0"
    				data-clickable
    				onClick={click}>
    			</a-box>
    			<a-box color={enabled ? '#0055ee' : '#ee5500'}
    				width="0.2"
    				height="0.8"
    				depth="0.6"
    				position="0.4 0.15 0"
    				data-clickable
    				onClick={click}>
    			</a-box>
    			<a-box color={enabled ? '#0055ee' : '#ee5500'}
    				width="0.2"
    				height="0.8"
    				depth="0.6"
    				position="-0.4 0.15 0"
    				data-clickable
    				onClick={click}>
    			</a-box>
    			<a-box color={enabled ? '#0077ff' : '#ff7700'}
    				width="0.8"
    				height="1.2"
    				depth="0.1"
    				position="0 0.35 -0.25"
    				data-clickable
    				onClick={click}>
    			</a-box>
    		</a-entity>
    	);
    }
    
    // Bezstavový komponent tlačítka pro výběr sedadla
    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)} />
    	);
    }
    
    // Hlavní komponent aplikace
    class ExampleApp3 extends React.Component {
    
    	// konstruktor komponentu
    	constructor(props) {
    		super(props);
    		// stav komponentu
    		this.state = {
    			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
    		};
    	}
    
    	// metoda pro získání funkce na vybrání sedadla
    	getSeatPicker() {
    		var that = this;
    		return (row, column) => {
    			that.setState((state) => {
    				state.seats[row][column] = !state.seats[row][column];
    				state.seats[row][column] ? (state.free++ , state.taken--) : (state.taken++ , state.free--);
    				return state;
    			});
    		}
    	}
    		   
    	// metoda pro vykreslování
    	render() {
    		return (
    			<div>
    				<!-- 3D scéna -->
    				<div className="fixed-ratio">
    					<a-scene embedded="">
    						<a-entity position="0 0 0" camera="" look-controls=""></a-entity>
    						<a-entity light="type: directional; color: #FFF; intensity: 2; castShadow:true;" position="10 5 10"></a-entity>
    						<a-entity position={(-this.state.seats[0].length / 2) + ' 0 ' + (-this.state.seats.length * 3)}>
    							<!-- cyklus pro vytvoření 3D sedadel v řadách -->
    							{this.state.seats.map((row, rowIndex) => (
    								row.map((enabled, colIndex) => (
    									<Seat row={rowIndex} column={colIndex} enabled={enabled} picker={this.getSeatPicker()} key={rowIndex + 'x' + colIndex} />
    								))
    							))}
    						</a-entity>
    						<a-entity cursor="rayOrigin: mouse; fuse: false;" raycaster="objects: [data-clickable];"></a-entity>
    					</a-scene>
    				</div>
    				<!-- tabulka -->
    				<form>
    					<h3>Vyberte sedadlo</h3>
    					<table>
    						<caption>Obsazeno je {this.state.taken}, zbývá {this.state.free} volných míst</caption>
    						<tbody>
    							<!-- cyklus pro tabulku sedadel -->
    							{this.state.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={this.getSeatPicker()} />
    										</td>
    									))}
    								</tr>
    							))}
    						</tbody>
    					</table>
    				</form>
    			</div>
    		);
    	}
    }
    
    // vykreslení aplikace do HTML elementu
    ReactDOM.render(<ExampleApp3 />, document.getElementById('app3'));