Matcap menu using React
Однажды я закинул ссылку на свой блог в одну из групп на Facebook, посвященной Three.js. Один из комментаторов сказал мне, что было бы круто, если бы я добавил в редактор Matcap. И я решил – а почему бы и нет. Но данный пост посвящен больше использованию React нежели созданию Matcap, так как материал Matcab реализован в Three.js и вся задача сводится к тому, чтобы применить этот материал и назначить изображение.
Прежде я старался использовать только Vanilla JS, так как проект учебный. Но и по этой же причине я решил попробовать применить React для части интерфейса, так как изучение React (или какого-то другого из трех китов фронтенда) является неотъемлемой частью обучения web-программиста. Мне было интересно именно внедрить React в существующий проект, самому настроить Webpack, что вполне у меня получилось, конечно, не без преодоления трудностей — при установке React из npm почему-то удалились все модули, которые у меня были установлены до этого, Webpack начал ругаться и проект перестал собираться.
Для связи событий между меню React и редактором мне понадобился state-менеджер. Я умышленно пока не использую готовые решения типа Redux, так как хочу написать сам и понять, как это работает. Нашел урок в интернете и написал свой EventEmitter:
export default class EventEmitter {
constructor(){
// singleton
if (EventEmitter.exist){
return EventEmitter.instance;
}
EventEmitter.instance = this;
EventEmitter.exist = true;
//---
this._events = {};
}
onEvent(name, listener) {
if (!this._events[name]) {
this._events[name] = [];
}
this._events[name].push(listener);
}
removelistener(name, listenerToRemove) {
if (!this._events[name]) {
throw new Error(`Can't remove a listener. Event "${name} doesn't exist`);
}
this._events[name] = this._events[name].filter( listener => listener != listenerToRemove)
}
emitEvent(name, data) {
if (!this._events[name]) {
throw new Error(`Can't emit an event. Event ${name} doesn't exist`);
}
this._events[name].forEach( callback => callback(data));
}
}
Затем создал класс State, тоже singleton, и реализовал в нем метод
changeAppState( mode, state ) {
this.appState = state;
this.eventEmitter.emitEvent( mode, state )
}
А в коде сцены слушаю это событие и меняю изображение
this.state.eventEmitter.onEvent('matcapChanged', matcapChanging );
function matcapChanging(img) {
if ( !scope.currentSelection.object ) return;
scope.currentSelection.object.material.dispose();
scope.currentSelection.object.material = new MeshMatcapMaterial();
let texture = new TextureLoader().load( "_Resources/Matcabs/Test/" + img )
scope.currentSelection.object.material.matcap = texture;
}
Событие установки изображения я добавил в useEffect, то есть когда происходит выбор изображения, происходит рендер компонента ReactPanel, и после рендера, если изображение изменилось, генерируется событие
useEffect( () => {appState.changeAppState( 'matcapChanged', selectedCard.src )}, [selectedCard] );

Спасибо за пост, только почему не пишите последние пару дней? Мы же ждем продолжения 🙂 +1 полностью согласен с Антоном!