Wprowadzenie do lil-gui
W artykule omówię lil-gui - nowoczesny panel sterowania dla aplikacji JavaScript i Three.js. Podczas tworzenia aplikacji wykorzystujących Three.js, WebGL, Canvas, Web Audio API czy nawet zwykłe animacje JavaScript, bardzo szybko pojawia się potrzeba wygodnej zmiany parametrów programu bez ciągłego edytowania kodu i odświeżania strony. Do tego celu przez wiele lat wykorzystywano bibliotekę dat.GUI. Obecnie jej następcą jest lil-gui – lżejsza, szybsza i aktywnie rozwijana biblioteka oferująca praktycznie identyczny sposób użycia. W tym artykule poznamy podstawy pracy z lil-gui.
Czym jest lil-gui?
Czym jest lil-gui? lil-gui jest niewielką biblioteką JavaScript służącą do tworzenia paneli sterowania (GUI - Graphical User Interface). Pozwala w czasie działania programu:- zmieniać liczby,
- przełączać opcje logiczne,
- wybierać kolory,
- uruchamiać funkcje,
- rozwijać grupy ustawień,
- obserwować wartości zmiennych.
- Three.js
- Babylon.js
- PixiJS
- p5.js
- WebGL
- Shaderów GLSL
- Web Audio API
- symulacji fizycznych
- generative art
Instalacja
Najprościej wykorzystać CDN.<script type="module"> import GUI from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'; </script>lub w projekcie npm:
npm install lil-guiNastępnie:
import GUI from 'lil-gui';Najprostszy możliwy przykład:
import GUI from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'; const settings = { speed: 5 }; const gui = new GUI(); gui.add(settings, 'speed');Po uruchomieniu pojawi się panel zawierający jeden suwak.
Jak działa lil-gui?
Biblioteka działa na bardzo prostym mechanizmie. Tworzymy obiekt:
const settings = {
speed: 5
};
Następnie przekazujemy:
gui.add(settings, 'speed');Od tej chwili GUI modyfikuje właściwość obiektu.Jeżeli użytkownik przesunie suwak to automatycznie settings.speed otrzyma nową wartość.
Ustawienie zakresu
Domyślnie lil-gui nie zna zakresu liczb. Można go określić:gui.add(settings, 'speed', 0, 20);lub
gui.add(settings, 'speed')
.min(0)
.max(20);
Krok zmiany wartości
Przykład:gui.add(settings, 'speed', 0, 20, 0.5);lub
gui.add(settings, 'speed')
.min(0)
.max(20)
.step(0.5);
Użytkownik będzie zmieniał wartość co 0.5.
Nadanie etykiety
gui
.add(settings, 'speed')
.name('Prędkość');
Panel pokaże tekst Prędkość zamiast speed.
Wartości logiczne
Typ boolean automatycznie tworzy checkbox.
const settings = {
wireframe: false
};
gui.add(settings, 'wireframe');
Lista rozwijana
Jeżeli chcemy wybrać jedną z wielu wartości:
const settings = {
quality: 'Medium'
};
gui.add(settings, 'quality', [
'Low',
'Medium',
'High',
'Ultra'
]);
Powyższy kod tworzy listę rozwijaną. Można również użyć mapy:
gui.add(settings, 'quality', {
Niska: 1,
Średnia: 2,
Wysoka: 3
});
Wybór koloru
Jedna z najczęściej używanych funkcji. Po kliknięciu otworzy się klasyczny color picker.
const settings = {
color: '#00ffcc'
};
gui.addColor(settings, 'color');
Uruchamianie funkcji
GUI może zawierać przyciski. Kliknięcie przycisku uruchamia metodę.
const settings = {
reset() {
console.log("Reset");
}
};
gui.add(settings, 'reset');
Foldery
Przy większych projektach warto grupować ustawienia.
const appearance = gui.addFolder('Wygląd');
appearance.add(settings, 'wireframe');
appearance.addColor(settings, 'color');
Można tworzyć wiele folderów.
const animation = gui.addFolder('Animacja');
const camera = gui.addFolder('Kamera');
const lights = gui.addFolder('Światło');
Możesz określić czy folder ma być domyślnie otwarty folder.open() czy zamknięty folder.close().
Nasłuchiwanie zmian
Najczęściej wykorzystujemy:
gui.add(settings, 'speed')
.onChange(value => {
console.log(value);
});
Kod wykona się po każdej zmianie suwaka.
Zdarzenie zakończenia edycji
Jeżeli chcemy wykonać operację dopiero po puszczeniu suwaka:
gui.add(settings, 'speed')
.onFinishChange(value => {
console.log("Nowa wartość:", value);
});
Jest to przydatne przy kosztownych obliczeniach.
Obserwowanie zmiennej
Czasami wartość zmienia sam program.gui.add(player, 'fps').listen();GUI będzie automatycznie odświeżane.
Ukrywanie panelu
Można ukryć panel:gui.hide();i ponownie pokazać:
gui.show();Jeżeli panel nie będzie już potrzebny metoda gui.destroy() usunie cały panel z dokumentu.
Przykład z animacją
Zmieniając wartość suwaka natychmiast wpływamy na prędkość obrotu.import GUI from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'; const settings = { speed: 1, rotation: true }; const gui = new GUI(); gui.add(settings, 'speed', 0, 10); gui.add(settings, 'rotation'); function animate() { requestAnimationFrame(animate); if (settings.rotation) { cube.rotation.y += settings.speed * 0.01; } } animate();Poniżej znajduje się kompletny przykład wykorzystujący Three.js oraz lil-gui. Jest to prosta scena z obracającym się sześcianem, którego prędkość oraz możliwość obracania można kontrolować z poziomu panelu GUI.
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Three.js + lil-gui - Przykład animacji</title>
<style>
body{
margin:0;
overflow:hidden;
background:#111;
font-family:Arial,Helvetica,sans-serif;
}
canvas{
display:block;
}
</style>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"lil-gui": "https://cdn.jsdelivr.net/npm/[email protected]/+esm"
}
}
</script>
</head>
<body>
<script type="module">
import * as THREE from "three";
import GUI from "lil-gui";
// -------------------------------------------------
// Renderer
// -------------------------------------------------
const renderer = new THREE.WebGLRenderer({
antialias:true
});
renderer.setSize(window.innerWidth,window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.body.appendChild(renderer.domElement);
// -------------------------------------------------
// Scene
// -------------------------------------------------
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x202020);
// -------------------------------------------------
// Camera
// -------------------------------------------------
const camera = new THREE.PerspectiveCamera(
60,
window.innerWidth/window.innerHeight,
0.1,
100
);
camera.position.set(0, 0.5, 5);
// -------------------------------------------------
// Lights
// -------------------------------------------------
const ambientLight = new THREE.AmbientLight(0xffffff,1.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff,2);
directionalLight.position.set(5,5,5);
scene.add(directionalLight);
// -------------------------------------------------
// Cube
// -------------------------------------------------
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial({
color:0x00ccff,
roughness:0.35,
metalness:0.2
});
const cube = new THREE.Mesh(
geometry,
material
);
scene.add(cube);
// -------------------------------------------------
// Settings
// -------------------------------------------------
const settings = {
speed:1,
rotation:true
};
// -------------------------------------------------
// GUI
// -------------------------------------------------
const gui = new GUI({
title:"Animacja"
});
gui
.add(settings,"speed",0,10,0.1)
.name("Prędkość");
gui
.add(settings,"rotation")
.name("Obrót");
// -------------------------------------------------
// Animation
// -------------------------------------------------
function animate() {
requestAnimationFrame(animate);
if(settings.rotation){
cube.rotation.x += settings.speed*0.01;
cube.rotation.y += settings.speed*0.015;
}
renderer.render(scene,camera);
}
animate();
// -------------------------------------------------
// Resize
// -------------------------------------------------
window.addEventListener("resize", () => {
camera.aspect=window.innerWidth/window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(
window.innerWidth,
window.innerHeight
);
});
</script>
</body>
</html>
Kontrola obracającego sześcianu Three.js + lil.gui