Matcap menu using React

0

I once posted a link to my blog in one of the Three.js Facebook groups. One of the commenters told me that it would be cool if I added Matcap to the editor. And I decided – why not. But this post is more about using React than creating Matcap, since the Matcab material is implemented in Three.js and the whole task comes down to applying this material and assigning an image.

Before I tried to use only Vanilla JS, as the project is educational. But for the same reason, I decided to try using React for part of the interface, since learning React (or some other of the three frontend whales) is an integral part of training a web programmer. It was interesting for me to implement React into an existing project, configure Webpack myself, which I did quite well, of course, not without overcoming difficulties – when installing React from npm, for some reason, all the modules that I had installed before were deleted, Webpack began to swear and the project stopped being assembled.

I needed a state manager to communicate events between the React menu and the editor. I deliberately don’t use ready-made solutions like Redux yet, as I want to write myself and understand how it works. Found a lesson on the Internet and wrote my own 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));
    }
}

Then I created the State class, also a singleton, and implemented the method in it

    changeAppState( mode, state ) {
        this.appState = state;
        this.eventEmitter.emitEvent( mode, state )
    }

And in the scene code, I listen to this event and change the image

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;
        }

I added the image setting event to useEffect, that is, when an image is selected, the ReactPanel component is rendered, and after rendering, if the image has changed, an event is generated

useEffect( () => {appState.changeAppState( 'matcapChanged', selectedCard.src )}, [selectedCard] );
0