// Vyžaduje kompilaci pomocí: npm run build
import React, { Suspense, useMemo, useRef } from 'react';
import ReactDOM from 'react-dom';
import * as THREE from 'three';
import { Canvas, useLoader, useUpdate, useThree, useRender, extend } from 'react-three-fiber';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// Bezstavový komponent pro 3D objekt textu
const Text = ({ children, vAlign = 'center', hAlign = 'center', size = 1, diffuseColor = '#dd3300', emissiveColor = '#ee9966', ...props }) => {
const font = useLoader(THREE.FontLoader, '../../../libs/threejs/three.helvetiker.json');
const config = useMemo(
() => ({ font, size: 20, height: 4, curveSegments: 32 }),
[font]
);
const mesh = useUpdate(
self => {
const size = new THREE.Vector3();
self.geometry.computeBoundingBox();
self.geometry.boundingBox.getSize(size);
self.position.x = hAlign === 'center' ? -size.x / 2 : hAlign === 'right' ? 0 : -size.x;
self.position.y = vAlign === 'center' ? -size.y / 2 : vAlign === 'top' ? 0 : -size.y;
self.material.color.setStyle(diffuseColor);
self.material.emissive.setStyle(emissiveColor);
},
[children]
);
return (
<group {...props} scale={[0.1 * size, 0.1 * size, 0.1]}>
<mesh ref={mesh}>
<textGeometry attach="geometry" args={[children, config]} />
<meshStandardMaterial attach="material" color={diffuseColor} emissive={emissiveColor} emissiveIntensity={0.4} />
</mesh>
</group>
)
}
// Bezstavový komponent ovládání uvnitř scény
extend({ OrbitControls });
const Controls = (props) => {
const { camera, gl } = useThree();
const controls = useRef();
useRender(({ camera }) => {
controls.current && controls.current.update();
camera.updateMatrixWorld();
});
return <orbitControls ref={controls} args={[camera, gl.domElement]} {...props} />;
};
// Hlavní komponent aplikace
class ExampleApp1 extends React.Component {
// konstruktor komponentu
constructor(props) {
super(props);
this.state = {
text: 'Hello world!',
diffuseColor: '#dd3300',
emissiveColor: '#ee9966'
};
}
// metoda pro vykreslení scény
renderScene() {
return (
<div className="fixed-ratio">
<Canvas camera={{ position: [0, 0, 5] }}>
<Controls />
<pointLight position={[2, 2, 5]} />
<Suspense fallback={null}>
<Text hAlign="center" position={[0, 0, 0]} children={this.state.text} diffuseColor={this.state.diffuseColor} emissiveColor={this.state.emissiveColor} />
</Suspense>
</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>Vyzařovaná barva</label>
<div>
<input
type="color"
value={this.state.emissiveColor}
onChange={(e) => this.setState({ emissiveColor: e.target.value })}
/>
<span style={{ color: this.state.emissiveColor }}>{this.state.emissiveColor}</span>
</div>
</div>
</div>
</form>
);
}
// hlavní metoda pro vykreslování
render() {
return (
<div>
{this.renderScene()}
{this.renderControls()}
</div>
);
}
}
// vykreslení aplikace do HTML elementu
ReactDOM.render(<ExampleApp1 />, document.getElementById('app1'));
npm install
npm install react
npm install react-dom
npm install react-scripts
npm install three
npm install react-three-fiber
npm run start
// Vyžaduje kompilaci pomocí: npm run build
// importy
import React, { useRef } from 'react';
import ReactDOM from 'react-dom';
import * as THREE from 'three';
import { Canvas, useThree, useRender, extend } from 'react-three-fiber';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
// Bezstavový komponent ovládání uvnitř scény
extend({ OrbitControls });
const Controls = (props) => {
const { camera, gl } = useThree();
const controls = useRef();
useRender(({ camera }) => {
controls.current && controls.current.update();
camera.updateMatrixWorld();
})
return <orbitControls ref={controls} args={[camera, gl.domElement]} {...props} />;
}
// Bezstavový komponent kostky
const BoxThree = ({ instance, index }) => {
const { position, size, color } = instance;
return (
<group position={new THREE.Vector3(index * 2, 0, 0)}>
<mesh position={new THREE.Vector3(position.x, position.y, position.z)}>
<boxBufferGeometry attach="geometry" args={[size.x, size.y, size.z]} />
<meshStandardMaterial attach="material" color={new THREE.Color(color)} />
</mesh>
</group>
);
}
// Bezstavový komponent 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);
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 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;
});
};
// metoda volaná při připojení komponentu
componentDidMount() {
this.add();
}
// metoda pro vykreslení
render() {
return (
<div>
<div className="fixed-ratio">
<Canvas camera={{ position: [0, 0, 5] }}>
<Controls />
<pointLight position={[2, 2, 5]} />
<group position={new THREE.Vector3((- this.state.boxes.length + 1), 0, 0)}>
{this.state.boxes.map((box, index) => (
<BoxThree instance={box} index={index} key={index} />
))}
</group>
</Canvas>
</div>
<formgt;
<ul>
{this.state.boxes.map((box, index) => (
<BoxControl instance={box} index={index} key={index} onChange={this.changeState(index)} remove={this.remove(index)} />
))}
</ul>
<div onClick={(e) => this.add()}>Přidat nový objekt</div>
</form>
</div>
);
}
}
// vykreslení aplikace do HTML elementu
ReactDOM.render(<ExampleApp2 />, document.getElementById('app2'));
npm install
npm install react
npm install react-dom
npm install react-scripts
npm install three
npm install react-three-fiber
npm run start