Získej svůj iPhone v nové soutěži! Získej svůj iPhone v nové soutěži!
Nová překladatelská soutěž ITnetwork.cz o telefon iPhone, sluchátka Beats a další věcné ceny za 4 hodiny práce.
Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

6. díl - Uživatelské rozhraní

JavaScript 3D webová hra Uživatelské rozhraní

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulém díle jsem ukazoval, jaké výhody má TypeScript oproti JavaScriptu. V tomto díle ukáži, jak vytvořím uživatelské rozhraní pomocí Reactu a Material UI.

Nebudu vytvářet nic složitého. V rámci uživatelského rozhraní chci zobrazit výběr barvy bloku a zobrazený text s počtem kostiček. To vše v levé vysouvací liště vedle scény.

Webová hra

Stav

Abychom mohli všechny nové funkčnosti implementovat, musíme změnit strukturu stavu hry. Kromě bloků a poslední akce musíme držet stav uživatelského rozhraní. To budou dvě hodnoty navíc:

  • ui.drawer říká, zda je levá lišta vysunutá.
  • ui.color je barva, kterou máme aktuálně vybranou.

Takto např. bude vypadat stav hry:

{
    "blocks": [
        {
            "id": "00dc4850-98cc-43ed-9965-1f6b08cb93da",
            "position": {
                "x": 1,
                "y": 1,
                "z": 0
            },
            "color": "#d74040"
        },
        {
            "id": "d5a0191d-1c7f-495d-9aa0-65236441f513",
            "position": {
                "x": 0,
                "y": 1,
                "z": 0
            },
            "color": "#d74040"
        }
    ],
    "ui": {
        "drawer": true,
        "color": "#d74040"
    },
    "lastAction": {
        "type": "BLOCK_ADD",
        "newBlock": {
            "id": "d5a0191d-1c7f-495d-9aa0-65236441f513",
            "position": {
                "x": 0,
                "y": 1,
                "z": 0
            },
            "color": "#d74040"
        }
    }
}

K akcím, co máme, přibudou ještě:

  • UI_DRAWER_TOGGLE zobrazí či skryje levou lištu.
  • UI_COLOR_SET nastaví barvu.

Spravovat takto složitý stav pouze pomocí jednoho reduceru je velmi nepřehledné. Proto využiji funkci Reduxu combineReducers.

index.ts

import { combineReducers } from 'redux'
import * as _ from "lodash";
import blocks from './blocks';
import ui from './ui';


function lastAction(previousAction,action){
    return action;
}


const stateReducerInner = combineReducers({
    blocks,
    ui,
    lastAction
});

export enum ActionTypes{
    CHANGE_STATE='CHANGE_STATE',
}

export const createAction = {
    CHANGE_STATE: (state)=>({type:ActionTypes.CHANGE_STATE,state}),
};

export function stateReducer(state, action) {
    if (action.type === ActionTypes.CHANGE_STATE) {
        return _.assign({},action.state, {lastAction:action});
    } else {
        return stateReducerInner(state, action);
    }
};

blocks.ts

const defaultBlocks = [
    {
        id:'My first block!!!',
        position:{x:0,y:0,z:0},
        color:'#cccccc'
    }
];

export default function blocks(blocks = defaultBlocks, action) {
    switch (action.type) {

        case 'BLOCK_ADD':
            return blocks.concat([action.newBlock]);


        case 'BLOCK_DELETE':
            return blocks.filter((block)=>block.id!==action.blockId);

        default:
            return blocks;
    }
}

ui.ts

const defaultUi = {
    drawer:false,
    color: '#cccccc',
};


export default function ui(ui=defaultUi, action) {
    return {
        drawer: action.type==='UI_DRAWER_TOGGLE'?(!ui.drawer):ui.drawer,
        color:  action.type==='UI_COLOR_SET'?action.value:ui.color,
    };
}

React

React je Javascriptový framework pro psaní uživatelských rozhraní. Dále v tomto článku budu předpokládat základní znalost Reactu, JSX syntaxe a react-redux. Pokud o tomto frameworku slyšíš poprvé, můžeš si přečíst např. tento článek. Také budu využívat Material UI , abych se nezdržoval stylováním komponent.

Naše React komponenty budou:

  • Root bude obsahovat levou lištu, kde budou komponenty Heading a BlockColor. Tato lišta bude vysouvací.
  • Heading bude informace o počtu kostiček.
  • BlockColor zobrazí aktuální barvu bloku a umožní její změnu.

root.tsx

import * as React from "react";
import {connect} from 'react-redux';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
import RaisedButton from 'material-ui/RaisedButton';
import Divider from 'material-ui/Divider';
import * as FontAwesome from 'react-fontawesome';
import BlockColor from './block-color';
import Heading from './heading';


function mapStateToProps(state){
    return {
        drawer: state.ui.drawer
    };
}

function mapDispatchToProps(dispatch){
    return {
        onMenu: ()=>dispatch({type:'UI_DRAWER_TOGGLE'}),
    }
}

function Root({drawer,onMenu}){
    return (
        <div>
            <RaisedButton
                onTouchTap={onMenu}
                style={{
                    position: 'fixed',
                    zIndex: 3,
                    top: 0,
                    left: 0,
                }}>
                <FontAwesome name="bars"/>
            </RaisedButton>


            <Drawer style={{
                zIndex: 5
            }} open={drawer}>


                <MenuItem onTouchTap={onMenu} leftIcon={<FontAwesome name="times"/>}>Close</MenuItem>

                <Heading/>
                <Divider />
                <BlockColor/>


            </Drawer>

        </div>
    )
}

export default connect(mapStateToProps, mapDispatchToProps)(Root);

heading.tsx

import * as React from "react";
import {connect} from 'react-redux';
import MenuItem from 'material-ui/MenuItem';

function mapStateToProps(state){
    return {
        size: state.blocks.length
    };
}

function Heading({size}){
    return (
        <div>
            <MenuItem>
                <h2>{size} blocks world</h2>
            </MenuItem>
        </div>
    )
}


export default connect(mapStateToProps)(Heading);

block-color.tsx

import * as React from "react";
import {connect} from 'react-redux';
import Subheader from 'material-ui/Subheader';
import MenuItem from 'material-ui/MenuItem';

function mapStateToProps(state){
    return {
        color: state.ui.color
    };
}

function mapDispatchToProps(dispatch){
    return {
        colorChange: (event)=>dispatch({type:'UI_COLOR_SET',value:event.target.value}),
    }
}

function BlockColor({color,colorChange}){
    return (
        <div>
            <Subheader>Block color</Subheader>
            <MenuItem>
                <input type="color" value={color} onChange={colorChange}/>
            </MenuItem>
        </div>
    )
}

export default connect(mapStateToProps, mapDispatchToProps)(BlockColor);

Nakonec Root komponentu vyrendrujeme a napojíme na store pomocí react-redux

ReactDOM.render(
    <Provider store={store}>
        <MuiThemeProvider>
            <Root />
        </MuiThemeProvider>
    </Provider>,
    root
);

Rozdělanou hru si můžeš stáhnout pod článkem, nebo jít do Git repozitáře, kde najdeš nejnovější verzi zdrojových kódů. Nebo si ji rovnou můžeš vyzkoušet na hejny.github.io/web-game. V dalším díle ukáži nasazovaní celého projektu na server.


 

Stáhnout

Staženo 39x (8.61 kB)

 

 

Článek pro vás napsal Pavol Hejný
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Autor se věnuje vývoji mnoha www aplikací - https://www.pavolhejny.com/
Miniatura
Předchozí článek
TypeScript
Miniatura
Všechny články v sekci
Vytvoř si vlastní 3D webovou hru
Miniatura
Následující článek
Nasazení na server
Aktivity (3)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!