import * as React from 'react'
|
import * as PIXI from 'pixi.js';
|
import { FormattedMessage, useIntl, useModel } from '@umijs/max';
|
import { Layout, Button, Flex, Row, Col, FloatButton } from 'antd';
|
const { Header, Content } = Layout;
|
import {
|
AppstoreAddOutlined,
|
FileAddOutlined,
|
CompressOutlined,
|
SettingOutlined,
|
} from '@ant-design/icons';
|
import './index.css'
|
import { createStyles } from 'antd-style';
|
import Edit from './components/device'
|
import * as Utils from './utils'
|
import Player from './player';
|
|
const useStyles = createStyles(({ token }) => {
|
let dark = token.colorBgBase === '#000';
|
return {
|
dark: dark,
|
layout: {
|
overflow: 'hidden',
|
},
|
header: {
|
height: 64,
|
paddingInline: 48,
|
lineHeight: '64px',
|
padding: 0,
|
},
|
flex: {
|
width: '100%',
|
height: '100%',
|
padding: '0 30px',
|
},
|
content: {
|
backgroundColor: '#F8FAFB',
|
height: 'calc(100vh - 120px)'
|
},
|
};
|
});
|
|
let player;
|
|
const Map = () => {
|
const { initialState, setInitialState } = useModel('@@initialState');
|
const { styles } = useStyles();
|
const mapRef = React.useRef();
|
const contentRef = React.useRef();
|
|
const [deviceVisible, setDeviceVisible] = React.useState(false);
|
const [windowSize, setWindowSize] = React.useState({
|
width: window.innerWidth,
|
height: window.innerHeight,
|
});
|
const [app, setApp] = React.useState(null);
|
const [mapContainer, setMapContainer] = React.useState(null);
|
const [mapEditModel, setMapEditModel] = React.useState(false);
|
const [deviceSettingModel, setDeviceSettingModel] = React.useState(false);
|
const [didClickSprite, setDidClickSprite] = React.useState(false);
|
|
// init func
|
React.useEffect(() => {
|
player = new Player(mapRef.current, styles.dark, didClickSprite);
|
setApp(player.app);
|
setMapContainer(player.mapContainer);
|
Utils.syncApp(player.app);
|
Utils.syncMapContainer(player.mapContainer);
|
|
const handleResize = () => {
|
setWindowSize({
|
width: window.innerWidth,
|
height: window.innerHeight,
|
});
|
};
|
window.addEventListener('resize', handleResize);
|
}, []);
|
|
// resize
|
React.useEffect(() => {
|
if (!app) {
|
return;
|
}
|
const width = contentRef.current.offsetWidth;
|
const height = contentRef.current.offsetHeight;
|
app.renderer.resize(width, height);
|
}, [app, mapContainer, windowSize])
|
|
// Edit Model
|
React.useEffect(() => {
|
if (!mapContainer) {
|
return;
|
}
|
if (mapEditModel) {
|
player.showGridlines();
|
player.activateMapEvent(Utils.MapEvent.SELECTION_BOX);
|
mapContainer.children.forEach(child => {
|
Utils.beMovable(child, setDidClickSprite);
|
})
|
} else {
|
setDeviceSettingModel(false);
|
player.hideGridlines();
|
player.activateMapEvent(null);
|
mapContainer.children.forEach(child => {
|
child.off('pointerup');
|
child.off('pointerdown');
|
child.off('click');
|
})
|
}
|
}, [mapEditModel]);
|
|
// Settings Model
|
React.useEffect(() => {
|
if (!mapContainer) {
|
return;
|
}
|
if (deviceSettingModel) {
|
player.showStarryBackground();
|
} else {
|
player.hideStarryBackground();
|
}
|
}, [deviceSettingModel]);
|
|
// Add New Device
|
const onDrop = (sprite, x, y) => {
|
const { mapX, mapY } = Utils.getRealPosition(x, y, mapContainer);
|
sprite.x = mapX;
|
sprite.y = mapY;
|
|
Utils.initSprite(sprite);
|
mapContainer.addChild(sprite);
|
Utils.beMovable(sprite, setDidClickSprite);
|
};
|
|
// didClickSprite, stop triggers both sprite click and play's selection boxs
|
React.useEffect(() => {
|
player.updateDidClickSprite(didClickSprite);
|
}, [didClickSprite])
|
|
return (
|
<>
|
<Layout className={styles.layout}>
|
<Header className={styles.header}>
|
<Row style={{ height: '100%' }}>
|
<Col span={8} style={{ backgroundColor: '#3C40C6' }}></Col>
|
<Col span={16} style={{ backgroundColor: '#3C40C6' }}>
|
<Flex className={styles.flex} gap={'large'} justify={'flex-end'} align={'center'}>
|
<Button onClick={() => setMapEditModel(!mapEditModel)} size={'large'}>
|
{!mapEditModel
|
? <span style={{ fontWeight: 'bold' }}><FormattedMessage id='map.edit' defaultMessage='编辑地图' /></span>
|
: <span style={{ color: 'red', fontWeight: 'bold' }}><FormattedMessage id='map.edit.close' defaultMessage='退出编辑' /></span>
|
}
|
</Button>
|
</Flex>
|
</Col>
|
</Row>
|
</Header>
|
<Content ref={contentRef} className={styles.content}>
|
<div ref={mapRef} style={{ position: "relative" }} />
|
|
<FloatButton.Group
|
shape="square"
|
style={{
|
left: 35,
|
bottom: 35
|
}}
|
>
|
<FloatButton
|
icon={<CompressOutlined />}
|
/>
|
<FloatButton.BackTop visibilityHeight={0} />
|
</FloatButton.Group>
|
|
<FloatButton.Group
|
hidden={!mapEditModel}
|
trigger="hover"
|
style={{
|
right: 35,
|
bottom: 35
|
}}
|
icon={<AppstoreAddOutlined />}
|
>
|
<FloatButton
|
tooltip={<div><FormattedMessage id='map.device.add' defaultMessage='添加设备' /></div>}
|
icon={<FileAddOutlined />}
|
onClick={() => {
|
setDeviceVisible(true);
|
}}
|
/>
|
<FloatButton
|
tooltip={<div><FormattedMessage id='map.device.oper' defaultMessage='参数设置' /></div>}
|
icon={<SettingOutlined />}
|
onClick={() => {
|
setDeviceSettingModel(!deviceSettingModel);
|
}}
|
/>
|
</FloatButton.Group>
|
</Content>
|
</Layout>
|
|
<Edit
|
open={deviceVisible}
|
onCancel={() => {
|
setDeviceVisible(false);
|
}}
|
refCurr={mapRef.current}
|
onDrop={onDrop}
|
/>
|
</>
|
)
|
}
|
|
export default Map;
|