make hook for renderer to store
This commit is contained in:
parent
c6dabe8a3e
commit
cfbae41817
|
@ -41,7 +41,7 @@ import onKeyPress from './ui/keypress';
|
||||||
|
|
||||||
import App from './components/App';
|
import App from './components/App';
|
||||||
|
|
||||||
import Renderer from './ui/Renderer';
|
import renderer from './ui/Renderer';
|
||||||
import ProtocolClient from './socket/ProtocolClient';
|
import ProtocolClient from './socket/ProtocolClient';
|
||||||
|
|
||||||
window.addEventListener('keydown', onKeyPress, false);
|
window.addEventListener('keydown', onKeyPress, false);
|
||||||
|
@ -190,8 +190,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const viewport = initViewport();
|
const viewport = initViewport();
|
||||||
const renderer = new Renderer();
|
renderer.setViewport(viewport, store);
|
||||||
renderer.setViewport(viewport);
|
|
||||||
|
|
||||||
ProtocolClient.on('pixelUpdate', ({
|
ProtocolClient.on('pixelUpdate', ({
|
||||||
i, j, offset, color,
|
i, j, offset, color,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { persistStore } from 'redux-persist';
|
||||||
import audio from './audio';
|
import audio from './audio';
|
||||||
import swal from './sweetAlert';
|
import swal from './sweetAlert';
|
||||||
import protocolClientHook from './protocolClientHook';
|
import protocolClientHook from './protocolClientHook';
|
||||||
|
import rendererHook from './rendererHook';
|
||||||
// import ads from './ads';
|
// import ads from './ads';
|
||||||
// import analytics from './analytics';
|
// import analytics from './analytics';
|
||||||
import array from './array';
|
import array from './array';
|
||||||
|
@ -38,6 +39,7 @@ const store = createStore(
|
||||||
notifications,
|
notifications,
|
||||||
title,
|
title,
|
||||||
protocolClientHook,
|
protocolClientHook,
|
||||||
|
rendererHook,
|
||||||
// ads,
|
// ads,
|
||||||
// analytics,
|
// analytics,
|
||||||
logger,
|
logger,
|
||||||
|
|
53
src/store/rendererHook.js
Normal file
53
src/store/rendererHook.js
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Hooks for renderer
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import renderer from '../ui/Renderer';
|
||||||
|
|
||||||
|
export default (store) => (next) => (action) => {
|
||||||
|
// executed after reducers
|
||||||
|
const ret = next(action);
|
||||||
|
|
||||||
|
const state = store.getState();
|
||||||
|
|
||||||
|
switch (action.type) {
|
||||||
|
case 'RELOAD_URL':
|
||||||
|
case 'SELECT_CANVAS':
|
||||||
|
case 'RECEIVE_ME': {
|
||||||
|
renderer.updateCanvasData(state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'REQUEST_BIG_CHUNK':
|
||||||
|
case 'RECEIVE_BIG_CHUNK':
|
||||||
|
case 'RECEIVE_BIG_CHUNK_FAILURE':
|
||||||
|
case 'RECEIVE_IMAGE_TILE': {
|
||||||
|
renderer.forceNextRender = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'SET_SCALE': {
|
||||||
|
const {
|
||||||
|
viewscale,
|
||||||
|
canvasMaxTiledZoom,
|
||||||
|
view,
|
||||||
|
canvasSize,
|
||||||
|
} = state.canvas;
|
||||||
|
renderer.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'SET_VIEW_COORDINATES': {
|
||||||
|
const { view, canvasSize } = state.canvas;
|
||||||
|
renderer.updateView(view, canvasSize);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
|
@ -11,7 +11,6 @@ import {
|
||||||
getTileOfPixel,
|
getTileOfPixel,
|
||||||
getPixelFromChunkOffset,
|
getPixelFromChunkOffset,
|
||||||
} from '../core/utils';
|
} from '../core/utils';
|
||||||
import store from './store';
|
|
||||||
import { fetchChunk, fetchTile } from '../actions';
|
import { fetchChunk, fetchTile } from '../actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -35,31 +34,25 @@ const SCALE_THREASHOLD = Math.min(
|
||||||
|
|
||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
lastFetchs: number;
|
|
||||||
centerChunk: Cell;
|
centerChunk: Cell;
|
||||||
view: Cell;
|
|
||||||
tiledScale: number;
|
tiledScale: number;
|
||||||
tiledZoom: number;
|
tiledZoom: number;
|
||||||
hover: boolean;
|
hover: boolean;
|
||||||
canvasId: number;
|
|
||||||
//--
|
//--
|
||||||
viewport: HTMLCanvasElement = null;
|
viewport: HTMLCanvasElement = null;
|
||||||
|
store;
|
||||||
//--
|
//--
|
||||||
scale: number;
|
|
||||||
forceNextRender: boolean;
|
forceNextRender: boolean;
|
||||||
forceNextSubrender: boolean;
|
forceNextSubrender: boolean;
|
||||||
canvas: HTMLCanvasElement;
|
canvas: HTMLCanvasElement;
|
||||||
lastFetch: number;
|
lastFetch: number;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.lastFetchs = 0;
|
|
||||||
this.centerChunk = [null, null];
|
this.centerChunk = [null, null];
|
||||||
this.tiledScale = 0;
|
this.tiledScale = 0;
|
||||||
this.tiledZoom = 4;
|
this.tiledZoom = 4;
|
||||||
this.hover = false;
|
this.hover = false;
|
||||||
this.canvasId = null;
|
|
||||||
//--
|
//--
|
||||||
this.scale = 0;
|
|
||||||
this.forceNextRender = true;
|
this.forceNextRender = true;
|
||||||
this.forceNextSubrender = true;
|
this.forceNextSubrender = true;
|
||||||
this.lastFetch = 0;
|
this.lastFetch = 0;
|
||||||
|
@ -76,33 +69,95 @@ class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HAS to be set before any rendering can happen
|
// HAS to be set before any rendering can happen
|
||||||
setViewport(viewport: HTMLCanvasElement) {
|
setViewport(viewport: HTMLCanvasElement, store) {
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
|
this.store = store;
|
||||||
|
const state = store.getState();
|
||||||
|
const {
|
||||||
|
canvasMaxTiledZoom,
|
||||||
|
viewscale,
|
||||||
|
view,
|
||||||
|
canvasSize,
|
||||||
|
} = state.canvas;
|
||||||
|
this.updateCanvasData(state);
|
||||||
|
this.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize);
|
||||||
|
this.forceNextRender = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateCanvasData(state: State) {
|
||||||
|
const {
|
||||||
|
canvasMaxTiledZoom,
|
||||||
|
viewscale,
|
||||||
|
view,
|
||||||
|
canvasSize,
|
||||||
|
} = state.canvas;
|
||||||
|
this.tiledZoom = canvasMaxTiledZoom + Math.log2(this.tiledScale) / 2;
|
||||||
|
this.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScale(
|
||||||
|
viewscale: number,
|
||||||
|
canvasMaxTiledZoom: number,
|
||||||
|
view,
|
||||||
|
canvasSize,
|
||||||
|
) {
|
||||||
|
pixelNotify.updateScale(viewscale);
|
||||||
|
let tiledScale = (viewscale > 0.5) ? 0 : Math.round(Math.log2(viewscale) / 2);
|
||||||
|
tiledScale = 4 ** tiledScale;
|
||||||
|
const tiledZoom = canvasMaxTiledZoom + Math.log2(tiledScale) / 2;
|
||||||
|
const relScale = viewscale / tiledScale;
|
||||||
|
|
||||||
|
this.tiledScale = tiledScale;
|
||||||
|
this.tiledZoom = tiledZoom;
|
||||||
|
this.relScale = relScale;
|
||||||
|
this.updateView(view, canvasSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateView(view, canvasSize) {
|
||||||
|
const [x, y] = view;
|
||||||
|
let [cx, cy] = this.centerChunk;
|
||||||
|
const [curcx, curcy] = getTileOfPixel(this.tiledScale, [x, y], canvasSize);
|
||||||
|
if (cx !== curcx || cy !== curcy) {
|
||||||
|
cx = curcx;
|
||||||
|
cy = curcy;
|
||||||
|
this.centerChunk = [cx, cy];
|
||||||
|
this.forceNextRender = true;
|
||||||
|
} else {
|
||||||
|
this.forceNextSubrender = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
renderPixel(
|
renderPixel(
|
||||||
i: number,
|
i: number,
|
||||||
j: number,
|
j: number,
|
||||||
offset: number,
|
offset: number,
|
||||||
color: ColorIndex,
|
color: ColorIndex,
|
||||||
) {
|
) {
|
||||||
if (this.scale < 0.8) return;
|
const state: State = this.store.getState();
|
||||||
const scale = (this.scale > SCALE_THREASHOLD) ? 1 : this.scale;
|
const {
|
||||||
|
canvasSize,
|
||||||
|
palette,
|
||||||
|
scale,
|
||||||
|
} = state.canvas;
|
||||||
|
|
||||||
|
if (scale < 0.8) return;
|
||||||
|
const scaleM = (scale > SCALE_THREASHOLD) ? 1 : scale;
|
||||||
|
|
||||||
const context = this.canvas.getContext('2d');
|
const context = this.canvas.getContext('2d');
|
||||||
if (!context) return;
|
if (!context) return;
|
||||||
|
|
||||||
const [x, y] = getPixelFromChunkOffset(i, j, offset, this.canvasSize);
|
const [x, y] = getPixelFromChunkOffset(i, j, offset, canvasSize);
|
||||||
|
|
||||||
const [canX, canY] = this.centerChunk
|
const [canX, canY] = this.centerChunk
|
||||||
.map((z) => (z + 0.5) * TILE_SIZE - this.canvasSize / 2);
|
.map((z) => (z + 0.5) * TILE_SIZE - canvasSize / 2);
|
||||||
const px = ((x - canX) * scale) + (CANVAS_WIDTH / 2);
|
const px = ((x - canX) * scaleM) + (CANVAS_WIDTH / 2);
|
||||||
const py = ((y - canY) * scale) + (CANVAS_HEIGHT / 2);
|
const py = ((y - canY) * scaleM) + (CANVAS_HEIGHT / 2);
|
||||||
// if not part of our current canvas, do not render
|
// if not part of our current canvas, do not render
|
||||||
if (px < 0 || py >= CANVAS_WIDTH || py < 0 || py >= CANVAS_HEIGHT) return;
|
if (px < 0 || py >= CANVAS_WIDTH || py < 0 || py >= CANVAS_HEIGHT) return;
|
||||||
|
|
||||||
context.fillStyle = this.palette.colors[color];
|
context.fillStyle = palette.colors[color];
|
||||||
context.fillRect(px, py, scale, scale);
|
context.fillRect(px, py, scaleM, scaleM);
|
||||||
pixelNotify.addPixel(x, y);
|
pixelNotify.addPixel(x, y);
|
||||||
|
|
||||||
this.forceNextSubrender = true;
|
this.forceNextSubrender = true;
|
||||||
|
@ -132,16 +187,25 @@ class Renderer {
|
||||||
|
|
||||||
|
|
||||||
renderChunks(
|
renderChunks(
|
||||||
chunks,
|
state: State,
|
||||||
view: Cell,
|
|
||||||
canvasId: number,
|
|
||||||
) {
|
) {
|
||||||
const context = this.canvas.getContext('2d');
|
const context = this.canvas.getContext('2d');
|
||||||
if (!context) return;
|
if (!context) return;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
centerChunk: chunkPosition, scale, tiledScale, tiledZoom,
|
centerChunk: chunkPosition,
|
||||||
|
tiledScale,
|
||||||
|
tiledZoom,
|
||||||
|
viewport,
|
||||||
} = this;
|
} = this;
|
||||||
|
const {
|
||||||
|
viewscale: scale,
|
||||||
|
canvasId,
|
||||||
|
canvasSize,
|
||||||
|
canvasMaxTiledZoom,
|
||||||
|
chunks,
|
||||||
|
} = state.canvas;
|
||||||
|
|
||||||
let { relScale } = this;
|
let { relScale } = this;
|
||||||
// clear rect is just needed for Google Chrome, else it would flash regularly
|
// clear rect is just needed for Google Chrome, else it would flash regularly
|
||||||
context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
|
context.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
|
||||||
|
@ -159,7 +223,7 @@ class Renderer {
|
||||||
}
|
}
|
||||||
// define how many chunks we will render
|
// define how many chunks we will render
|
||||||
// don't render chunks outside of viewport
|
// don't render chunks outside of viewport
|
||||||
const { width, height } = this.viewport;
|
const { width, height } = viewport;
|
||||||
const CHUNK_RENDER_RADIUS_X = Math.ceil(width / TILE_SIZE / 2 / relScale);
|
const CHUNK_RENDER_RADIUS_X = Math.ceil(width / TILE_SIZE / 2 / relScale);
|
||||||
const CHUNK_RENDER_RADIUS_Y = Math.ceil(height / TILE_SIZE / 2 / relScale);
|
const CHUNK_RENDER_RADIUS_Y = Math.ceil(height / TILE_SIZE / 2 / relScale);
|
||||||
// If scale is so large that neighbouring chunks wouldn't fit in canvas,
|
// If scale is so large that neighbouring chunks wouldn't fit in canvas,
|
||||||
|
@ -194,7 +258,7 @@ class Renderer {
|
||||||
const x = xOffset + dx * TILE_SIZE;
|
const x = xOffset + dx * TILE_SIZE;
|
||||||
const y = yOffset + dy * TILE_SIZE;
|
const y = yOffset + dy * TILE_SIZE;
|
||||||
|
|
||||||
const chunkMaxXY = this.canvasSize / TILE_SIZE;
|
const chunkMaxXY = canvasSize / TILE_SIZE;
|
||||||
if (cx < 0 || cx >= chunkMaxXY * tiledScale || cy < 0 || cy >= chunkMaxXY * tiledScale) {
|
if (cx < 0 || cx >= chunkMaxXY * tiledScale || cy < 0 || cy >= chunkMaxXY * tiledScale) {
|
||||||
// if out of bounds
|
// if out of bounds
|
||||||
context.fillRect(x, y, TILE_SIZE, TILE_SIZE);
|
context.fillRect(x, y, TILE_SIZE, TILE_SIZE);
|
||||||
|
@ -214,10 +278,10 @@ class Renderer {
|
||||||
} else {
|
} else {
|
||||||
// we don't have that chunk
|
// we don't have that chunk
|
||||||
if (fetch) {
|
if (fetch) {
|
||||||
if (tiledZoom === this.canvasMaxTiledZoom) {
|
if (tiledZoom === canvasMaxTiledZoom) {
|
||||||
store.dispatch(fetchChunk(canvasId, [tiledZoom, cx, cy]));
|
this.store.dispatch(fetchChunk(canvasId, [tiledZoom, cx, cy]));
|
||||||
} else {
|
} else {
|
||||||
store.dispatch(fetchTile(canvasId, [tiledZoom, cx, cy]));
|
this.store.dispatch(fetchTile(canvasId, [tiledZoom, cx, cy]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (loadingTiles.hasTiles) {
|
if (loadingTiles.hasTiles) {
|
||||||
|
@ -233,9 +297,14 @@ class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// keep in mind that everything we got here gets executed 60 times per second
|
||||||
|
// avoiding unneccessary stuff is important
|
||||||
render() {
|
render() {
|
||||||
const { viewport } = this;
|
try{
|
||||||
const state: State = store.getState();
|
const {
|
||||||
|
viewport,
|
||||||
|
} = this;
|
||||||
|
const state: State = this.store.getState();
|
||||||
const {
|
const {
|
||||||
showGrid,
|
showGrid,
|
||||||
showPixelNotify,
|
showPixelNotify,
|
||||||
|
@ -247,57 +316,28 @@ class Renderer {
|
||||||
placeAllowed,
|
placeAllowed,
|
||||||
} = state.user;
|
} = state.user;
|
||||||
const {
|
const {
|
||||||
chunks,
|
|
||||||
view,
|
view,
|
||||||
fetchs,
|
|
||||||
viewscale,
|
viewscale,
|
||||||
|
canvasSize,
|
||||||
canvasId,
|
canvasId,
|
||||||
} = state.canvas;
|
} = state.canvas;
|
||||||
|
|
||||||
if (!view || canvasId === null) return;
|
if (!view || canvasId === null) return;
|
||||||
|
|
||||||
if (this.canvasId !== canvasId) {
|
|
||||||
const { canvasSize, palette, canvasMaxTiledZoom } = state.canvas;
|
|
||||||
this.canvasSize = canvasSize;
|
|
||||||
this.palette = palette;
|
|
||||||
this.canvasMaxTiledZoom = canvasMaxTiledZoom;
|
|
||||||
this.tiledZoom = this.canvasMaxTiledZoom + Math.log2(this.tiledScale) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [x, y] = view;
|
const [x, y] = view;
|
||||||
let [cx, cy] = this.centerChunk;
|
const [cx, cy] = this.centerChunk;
|
||||||
|
|
||||||
// if we have to render pixelnotify
|
// if we have to render pixelnotify
|
||||||
const doRenderPixelnotify = (viewscale >= 0.5 && showPixelNotify && pixelNotify.doRender());
|
const doRenderPixelnotify = (viewscale >= 0.5 && showPixelNotify && pixelNotify.doRender());
|
||||||
// if we have to render offscreen canvas
|
|
||||||
let doRenderCanvas = (this.lastFetchs !== fetchs || this.forceNextRender);
|
|
||||||
if (viewscale !== this.scale) {
|
|
||||||
this.scale = viewscale;
|
|
||||||
pixelNotify.updateScale(viewscale);
|
|
||||||
this.tiledScale = (viewscale > 0.5) ? 0 : Math.round(Math.log2(viewscale) / 2);
|
|
||||||
this.tiledScale = 4 ** this.tiledScale;
|
|
||||||
this.tiledZoom = this.canvasMaxTiledZoom + Math.log2(this.tiledScale) / 2;
|
|
||||||
this.relScale = viewscale / this.tiledScale;
|
|
||||||
doRenderCanvas = true;
|
|
||||||
}
|
|
||||||
// if we have to render placeholder
|
// if we have to render placeholder
|
||||||
const doRenderPlaceholder = (viewscale >= 3 && placeAllowed && (hover || this.hover) && !isPotato);
|
const doRenderPlaceholder = (viewscale >= 3 && placeAllowed && (hover || this.hover) && !isPotato);
|
||||||
const doRenderPotatoPlaceholder = (viewscale >= 3 && placeAllowed && (hover !== this.hover || doRenderCanvas || this.forceNextSubrender || doRenderPixelnotify) && isPotato);
|
const doRenderPotatoPlaceholder = (viewscale >= 3 && placeAllowed && (hover !== this.hover || this.forceNextRender || this.forceNextSubrender || doRenderPixelnotify) && isPotato);
|
||||||
//--
|
//--
|
||||||
if (view !== this.view) {
|
|
||||||
const [curcx, curcy] = getTileOfPixel(this.tiledScale, [x, y], this.canvasSize);
|
|
||||||
if (cx !== curcx || cy !== curcy) {
|
|
||||||
cx = curcx;
|
|
||||||
cy = curcy;
|
|
||||||
this.centerChunk = [cx, cy];
|
|
||||||
doRenderCanvas = true;
|
|
||||||
}
|
|
||||||
this.view = view;
|
|
||||||
// if we have nothing to render, return
|
// if we have nothing to render, return
|
||||||
// note: this.hover is used to, to render without the placeholder one last time when cursor leaves window
|
// note: this.hover is used to, to render without the placeholder one last time when cursor leaves window
|
||||||
} else if (
|
if (
|
||||||
// no full rerender
|
// no full rerender
|
||||||
!doRenderCanvas
|
!this.forceNextRender
|
||||||
// no render placeholder under cursor
|
// no render placeholder under cursor
|
||||||
&& !doRenderPlaceholder
|
&& !doRenderPlaceholder
|
||||||
&& !doRenderPotatoPlaceholder
|
&& !doRenderPotatoPlaceholder
|
||||||
|
@ -310,9 +350,8 @@ class Renderer {
|
||||||
}
|
}
|
||||||
this.hover = hover;
|
this.hover = hover;
|
||||||
|
|
||||||
if (doRenderCanvas) {
|
if (this.forceNextRender) {
|
||||||
this.renderChunks(chunks, view, canvasId);
|
this.renderChunks(state);
|
||||||
this.lastFetchs = fetchs;
|
|
||||||
}
|
}
|
||||||
this.forceNextRender = false;
|
this.forceNextRender = false;
|
||||||
this.forceNextSubrender = false;
|
this.forceNextSubrender = false;
|
||||||
|
@ -327,7 +366,8 @@ class Renderer {
|
||||||
viewportCtx.imageSmoothingEnabled = false;
|
viewportCtx.imageSmoothingEnabled = false;
|
||||||
// If scale is so large that neighbouring chunks wouldn't fit in offscreen canvas,
|
// If scale is so large that neighbouring chunks wouldn't fit in offscreen canvas,
|
||||||
// do scale = 1 in renderChunks and scale in render()
|
// do scale = 1 in renderChunks and scale in render()
|
||||||
const canvasCenter = this.canvasSize / 2;
|
const canvasCenter = canvasSize / 2;
|
||||||
|
console.log("do render")
|
||||||
if (viewscale > SCALE_THREASHOLD) {
|
if (viewscale > SCALE_THREASHOLD) {
|
||||||
viewportCtx.save();
|
viewportCtx.save();
|
||||||
viewportCtx.scale(viewscale, viewscale);
|
viewportCtx.scale(viewscale, viewscale);
|
||||||
|
@ -347,8 +387,12 @@ class Renderer {
|
||||||
|
|
||||||
if (hover && doRenderPlaceholder) renderPlaceholder(state, viewport, viewscale);
|
if (hover && doRenderPlaceholder) renderPlaceholder(state, viewport, viewscale);
|
||||||
if (hover && doRenderPotatoPlaceholder) renderPotatoPlaceholder(state, viewport, viewscale);
|
if (hover && doRenderPotatoPlaceholder) renderPotatoPlaceholder(state, viewport, viewscale);
|
||||||
|
} catch {
|
||||||
|
console.log("error");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default Renderer;
|
const renderer = new Renderer();
|
||||||
|
export default renderer;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user