render: map composing (working)

This commit is contained in:
muerwre 2018-11-27 17:18:15 +07:00
parent 2eb398ef6b
commit 4747c918e2
9 changed files with 122 additions and 7 deletions

View file

@ -9,7 +9,7 @@ import { EditorDialog } from '$components/panels/EditorDialog';
import { LogoPreview } from '$components/logo/LogoPreview'; import { LogoPreview } from '$components/logo/LogoPreview';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { setMode, startEditing, stopEditing, setLogo } from '$redux/user/actions'; import { setMode, startEditing, stopEditing, setLogo, showRenderer } from '$redux/user/actions';
import type { UserType } from '$constants/types'; import type { UserType } from '$constants/types';
import { editor } from '$modules/Editor'; import { editor } from '$modules/Editor';
import { getTilePlacement } from '$utils/renderer'; import { getTilePlacement } from '$utils/renderer';
@ -31,6 +31,7 @@ type Props = {
startEditing: Function, startEditing: Function,
stopEditing: Function, stopEditing: Function,
setLogo: Function, setLogo: Function,
showRenderer: Function,
} }
class Component extends React.PureComponent<Props, void> { class Component extends React.PureComponent<Props, void> {
@ -108,8 +109,8 @@ class Component extends React.PureComponent<Props, void> {
<button <button
className={classnames('disabled', { active: mode === MODES.SHOTTER })} className={classnames('disabled', { active: mode === MODES.SHOTTER })}
// onClick={this.startShotterMode} onClick={this.props.showRenderer}
onClick={getTilePlacement} // onClick={getTilePlacement}
> >
<Icon icon="icon-shot-3" /> <Icon icon="icon-shot-3" />
</button> </button>
@ -220,6 +221,7 @@ const mapDispatchToProps = dispatch => bindActionCreators({
setLogo, setLogo,
startEditing, startEditing,
stopEditing, stopEditing,
showRenderer,
}, dispatch); }, dispatch);
export const EditorPanel = connect( export const EditorPanel = connect(

View file

@ -0,0 +1,71 @@
import React from 'react';
import { getTilePlacement } from '$utils/renderer';
export class Renderer extends React.Component {
componentDidMount() {
if (this.canvas) this.init();
}
init() {
const ctx = this.canvas.getContext('2d');
const geometry = getTilePlacement();
this.fetchImages(ctx, geometry)
.then(images => this.composeImages({ geometry, images, ctx }));
// myimage = new Image();
// myimage.onload = function() {
// ctx.drawImage(myimage, x, y);
// }
// myimage.src = 'http://myserver/nextimage.cgi';
}
fetchImages = (ctx, geometry) => {
const {
minX, maxX, minY, maxY, zoom
} = geometry;
const images = [];
for (let x = minX; x <= maxX; x += 1) {
for (let y = minY; y <= maxY; y += 1) {
images.push({ x, y, source: this.getImageSource({ x, y, zoom }) });
}
}
return Promise.all(images.map(({ x, y, source }) => (
this.imageFetcher(source).then(image => ({ x, y, image }))
)));
};
imageFetcher = source => new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(img);
img.src = source;
});
getImageSource = ({ x, y, zoom }) => (`http://b.basemaps.cartocdn.com/light_all/${zoom}/${x}/${y}.png`);
composeImages = ({ images, geometry, ctx }) => {
const {
minX, minY, shiftX, shiftY, size
} = geometry;
images.map(({ x, y, image }) => {
const posX = ((x - minX) * size) + shiftX;
const posY = ((y - minY) * size) + (window.innerHeight - shiftY - size);
return ctx.drawImage(image, posX, posY, 256, 256);
});
};
render() {
return (
<div className="renderer-shade" onClick={this.props.onClick}>
<canvas width={window.innerWidth} height={window.innerWidth} ref={el => { this.canvas = el; }} />
</div>
);
}
}

View file

@ -9,17 +9,30 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import { Renderer } from '$components/renderer/Renderer';
import { hideRenderer } from '$redux/user/actions';
const Component = () => ( type Props = {
renderer_active: Boolean,
hideRenderer: Function,
}
const Component = (props: Props) => (
<div> <div>
<Fills /> <Fills />
<UserLocation /> <UserLocation />
<UserPanel /> <UserPanel />
<EditorPanel /> <EditorPanel />
{
props.renderer_active &&
<Renderer onClick={props.hideRenderer} />
}
</div> </div>
); );
const mapStateToProps = () => ({}); const mapStateToProps = ({ user }) => ({
const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch); renderer_active: user.renderer_active
});
const mapDispatchToProps = dispatch => bindActionCreators({ hideRenderer }, dispatch);
export const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component)); export const App = connect(mapStateToProps, mapDispatchToProps)(hot(module)(Component));

View file

@ -32,3 +32,5 @@ export const setSaveSuccess = payload => ({ type: ACTIONS.SET_SAVE_SUCCESS, ...p
export const setSaveError = save_error => ({ type: ACTIONS.SET_SAVE_ERROR, save_error }); export const setSaveError = save_error => ({ type: ACTIONS.SET_SAVE_ERROR, save_error });
export const setSaveOverwrite = () => ({ type: ACTIONS.SET_SAVE_OVERWRITE }); export const setSaveOverwrite = () => ({ type: ACTIONS.SET_SAVE_OVERWRITE });
export const showRenderer = () => ({ type: ACTIONS.SHOW_RENDERER });
export const hideRenderer = () => ({ type: ACTIONS.HIDE_RENDERER });

View file

@ -30,4 +30,7 @@ export const ACTIONS = {
SET_SAVE_SUCCESS: 'SET_SAVE_SUCCESS', SET_SAVE_SUCCESS: 'SET_SAVE_SUCCESS',
SET_SAVE_ERROR: 'SET_SAVE_ERROR', SET_SAVE_ERROR: 'SET_SAVE_ERROR',
SET_SAVE_OVERWRITE: 'SET_SAVE_OVERWRITE', SET_SAVE_OVERWRITE: 'SET_SAVE_OVERWRITE',
SHOW_RENDERER: 'SHOW_RENDERER',
HIDE_RENDERER: 'HIDE_RENDERER',
}; };

View file

@ -51,6 +51,9 @@ const resetSaveDialog = state => ({
...state, save_overwriting: false, save_finished: false, save_processing: false, save_error: '', ...state, save_overwriting: false, save_finished: false, save_processing: false, save_error: '',
}); });
const showRenderer = state => ({ ...state, renderer_active: true });
const hideRenderer = state => ({ ...state, renderer_active: false });
const HANDLERS = { const HANDLERS = {
[ACTIONS.SET_USER]: setUser, [ACTIONS.SET_USER]: setUser,
[ACTIONS.SET_EDITING]: setEditing, [ACTIONS.SET_EDITING]: setEditing,
@ -68,6 +71,9 @@ const HANDLERS = {
[ACTIONS.SET_SAVE_SUCCESS]: setSaveSuccess, [ACTIONS.SET_SAVE_SUCCESS]: setSaveSuccess,
[ACTIONS.SEND_SAVE_REQUEST]: sendSaveRequest, [ACTIONS.SEND_SAVE_REQUEST]: sendSaveRequest,
[ACTIONS.RESET_SAVE_DIALOG]: resetSaveDialog, [ACTIONS.RESET_SAVE_DIALOG]: resetSaveDialog,
[ACTIONS.SHOW_RENDERER]: showRenderer,
[ACTIONS.HIDE_RENDERER]: hideRenderer,
}; };
export const INITIAL_STATE = { export const INITIAL_STATE = {
@ -87,6 +93,8 @@ export const INITIAL_STATE = {
save_finished: false, save_finished: false,
save_overwriting: false, save_overwriting: false,
save_processing: false, save_processing: false,
renderer_active: false
}; };
export const userReducer = createReducer(INITIAL_STATE, HANDLERS); export const userReducer = createReducer(INITIAL_STATE, HANDLERS);

View file

@ -8,6 +8,7 @@
@import 'logo.less'; @import 'logo.less';
@import 'user-button.less'; @import 'user-button.less';
@import 'save.less'; @import 'save.less';
@import 'renderer.less';
body { body {
font-family: 'Rubik', sans-serif; font-family: 'Rubik', sans-serif;

15
src/styles/renderer.less Normal file
View file

@ -0,0 +1,15 @@
.renderer-shade {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
background: rgba(0, 0, 0, 0.5);
//canvas {
// width: 50vw;
// height: 50vh;
//}
}

View file

@ -55,7 +55,7 @@ export const getTilePlacement = () => {
maxY, maxY,
shiftX: (rsw.x - msw.x), shiftX: (rsw.x - msw.x),
shiftY: (height + (rsw.y - msw.y)), shiftY: (height + (rsw.y - msw.y)) + 3,
size: 256, size: 256,
width, width,