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 полностью согласен с Антоном!