don't use outdated hammerjs in pixelpaintercontrols
This commit is contained in:
parent
709fd3af9f
commit
87629bf1b4
|
@ -4,7 +4,6 @@
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Hammer from 'hammerjs';
|
|
||||||
import keycode from 'keycode';
|
import keycode from 'keycode';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -28,100 +27,108 @@ import {
|
||||||
getOffsetOfPixel,
|
getOffsetOfPixel,
|
||||||
} from '../core/utils';
|
} from '../core/utils';
|
||||||
|
|
||||||
let store = null;
|
class PixelPlainterControls {
|
||||||
|
constructor(renderer, viewport: HTMLCanvasElement, curStore) {
|
||||||
|
this.store = curStore;
|
||||||
|
this.renderer = renderer;
|
||||||
|
this.viewport = viewport;
|
||||||
|
|
||||||
function onKeyPress(event: KeyboardEvent) {
|
this.onMouseDown = this.onMouseDown.bind(this);
|
||||||
// ignore key presses if modal is open or chat is used
|
this.onKeyPress = this.onKeyPress.bind(this);
|
||||||
if (event.target.nodeName === 'INPUT'
|
this.onAuxClick = this.onAuxClick.bind(this);
|
||||||
|| event.target.nodeName === 'TEXTAREA'
|
this.onMouseOut = this.onMouseOut.bind(this);
|
||||||
) {
|
this.onMouseMove = this.onMouseMove.bind(this);
|
||||||
return;
|
this.onWheel = this.onWheel.bind(this);
|
||||||
|
this.onMouseUp = this.onMouseUp.bind(this);
|
||||||
|
this.onTouchStart = this.onTouchStart.bind(this);
|
||||||
|
this.onTouchEnd = this.onTouchEnd.bind(this);
|
||||||
|
this.onTouchMove = this.onTouchMove.bind(this);
|
||||||
|
|
||||||
|
this.clickTabStartView = [0, 0];
|
||||||
|
this.clickTabStartTime = 0;
|
||||||
|
this.clickTabStartCoords = [0, 0];
|
||||||
|
this.startTabDist = 50;
|
||||||
|
this.startTabScale = this.store.getState().scale;
|
||||||
|
this.isMultiTab = false;
|
||||||
|
this.isMouseDown = false;
|
||||||
|
|
||||||
|
document.addEventListener('keydown', this.onKeyPress, false);
|
||||||
|
viewport.addEventListener('auxclick', this.onAuxClick, false);
|
||||||
|
viewport.addEventListener('mousedown', this.onMouseDown, false);
|
||||||
|
viewport.addEventListener('mousemove', this.onMouseMove, false);
|
||||||
|
viewport.addEventListener('mouseup', this.onMouseUp, false);
|
||||||
|
viewport.addEventListener('wheel', this.onWheel, false);
|
||||||
|
viewport.addEventListener('touchstart', this.onTouchStart, false);
|
||||||
|
viewport.addEventListener('touchend', this.onTouchEnd, false);
|
||||||
|
viewport.addEventListener('touchmove', this.onTouchMove, false);
|
||||||
|
viewport.addEventListener('mouseout', this.onMouseOut, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (keycode(event)) {
|
dispose() {
|
||||||
case 'up':
|
document.removeEventListener('keydown', this.onKeyPress, false);
|
||||||
case 'w':
|
|
||||||
store.dispatch(moveNorth());
|
|
||||||
break;
|
|
||||||
case 'left':
|
|
||||||
case 'a':
|
|
||||||
store.dispatch(moveWest());
|
|
||||||
break;
|
|
||||||
case 'down':
|
|
||||||
case 's':
|
|
||||||
store.dispatch(moveSouth());
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
case 'd':
|
|
||||||
store.dispatch(moveEast());
|
|
||||||
break;
|
|
||||||
/*
|
|
||||||
case 'space':
|
|
||||||
if ($viewport) $viewport.click();
|
|
||||||
return;
|
|
||||||
*/
|
|
||||||
case '+':
|
|
||||||
case 'e':
|
|
||||||
store.dispatch(zoomIn());
|
|
||||||
break;
|
|
||||||
case '-':
|
|
||||||
case 'q':
|
|
||||||
store.dispatch(zoomOut());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function initControls(renderer, viewport: HTMLCanvasElement, curStore) {
|
onMouseDown(event: MouseEvent) {
|
||||||
store = curStore;
|
event.preventDefault();
|
||||||
viewport.onmousemove = ({ clientX, clientY }: MouseEvent) => {
|
|
||||||
const state = store.getState();
|
|
||||||
const screenCoor = screenToWorld(state, viewport, [clientX, clientY]);
|
|
||||||
store.dispatch(setHover(screenCoor));
|
|
||||||
};
|
|
||||||
viewport.onmouseout = () => {
|
|
||||||
store.dispatch(unsetHover());
|
|
||||||
};
|
|
||||||
viewport.onwheel = ({ deltaY }: WheelEvent) => {
|
|
||||||
const state = store.getState();
|
|
||||||
const { hover } = state.gui;
|
|
||||||
let zoompoint = null;
|
|
||||||
if (hover) {
|
|
||||||
zoompoint = hover;
|
|
||||||
}
|
|
||||||
if (deltaY < 0) {
|
|
||||||
store.dispatch(zoomIn(zoompoint));
|
|
||||||
}
|
|
||||||
if (deltaY > 0) {
|
|
||||||
store.dispatch(zoomOut(zoompoint));
|
|
||||||
}
|
|
||||||
store.dispatch(onViewFinishChange());
|
|
||||||
};
|
|
||||||
viewport.onauxclick = ({ which, clientX, clientY }: MouseEvent) => {
|
|
||||||
// middle mouse button
|
|
||||||
if (which !== 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const state = store.getState();
|
|
||||||
if (state.canvas.scale < 3) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const coords = screenToWorld(state, viewport, [clientX, clientY]);
|
|
||||||
const clrIndex = renderer.getColorIndexOfPixel(...coords);
|
|
||||||
if (clrIndex === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
store.dispatch(selectColor(clrIndex));
|
|
||||||
};
|
|
||||||
|
|
||||||
// fingers controls on touch
|
if (event.button === 0) {
|
||||||
const hammertime = new Hammer(viewport);
|
this.isMouseDown = true;
|
||||||
hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL });
|
const { clientX, clientY } = event;
|
||||||
hammertime.get('swipe').set({ direction: Hammer.DIRECTION_ALL });
|
this.clickTabStartTime = Date.now();
|
||||||
// Zoom-in Zoom-out in touch devices
|
this.clickTabStartCoords = [clientX, clientY];
|
||||||
hammertime.get('pinch').set({ enable: true });
|
this.clickTabStartView = this.store.getState().canvas.view;
|
||||||
|
const { viewport } = this;
|
||||||
|
setTimeout(() => {
|
||||||
|
viewport.style.cursor = 'move';
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hammertime.on('tap', ({ center }) => {
|
onMouseUp(event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (event.button === 0) {
|
||||||
|
this.isMouseDown = false;
|
||||||
|
const { clientX, clientY } = event;
|
||||||
|
const { clickTabStartCoords, clickTabStartTime } = this;
|
||||||
|
const coordsDiff = [
|
||||||
|
clickTabStartCoords[0] - clientX,
|
||||||
|
clickTabStartCoords[1] - clientY,
|
||||||
|
];
|
||||||
|
// thresholds for single click / holding
|
||||||
|
if (clickTabStartTime > Date.now() - 300
|
||||||
|
&& coordsDiff[0] < 10 && coordsDiff[1] < 10) {
|
||||||
|
PixelPlainterControls.placePixel(
|
||||||
|
this.store,
|
||||||
|
this.viewport,
|
||||||
|
this.renderer,
|
||||||
|
[clientX, clientY],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.viewport.style.cursor = 'auto';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getTouchCenter(event: TouchEvent) {
|
||||||
|
switch (event.touches.length) {
|
||||||
|
case 1: {
|
||||||
|
const { pageX, pageY } = event.touches[0];
|
||||||
|
return [pageX, pageY];
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
const pageX = Math.floor(0.5
|
||||||
|
* (event.touches[0].pageX + event.touches[1].pageX));
|
||||||
|
const pageY = Math.floor(0.5
|
||||||
|
* (event.touches[0].pageY + event.touches[1].pageY));
|
||||||
|
return [pageX, pageY];
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static placePixel(store, viewport, renderer, center) {
|
||||||
const state = store.getState();
|
const state = store.getState();
|
||||||
const { autoZoomIn } = state.gui;
|
const { autoZoomIn } = state.gui;
|
||||||
const { placeAllowed } = state.user;
|
const { placeAllowed } = state.user;
|
||||||
|
@ -133,8 +140,7 @@ export function initControls(renderer, viewport: HTMLCanvasElement, curStore) {
|
||||||
|
|
||||||
if (isHistoricalView) return;
|
if (isHistoricalView) return;
|
||||||
|
|
||||||
const { x, y } = center;
|
const cell = screenToWorld(state, viewport, center);
|
||||||
const cell = screenToWorld(state, viewport, [x, y]);
|
|
||||||
|
|
||||||
if (autoZoomIn && scale < 8) {
|
if (autoZoomIn && scale < 8) {
|
||||||
store.dispatch(setViewCoordinates(cell));
|
store.dispatch(setViewCoordinates(cell));
|
||||||
|
@ -142,7 +148,7 @@ export function initControls(renderer, viewport: HTMLCanvasElement, curStore) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't allow placing of pixel just on low zoomlevels
|
// allow placing of pixel just on low zoomlevels
|
||||||
if (scale < 3) return;
|
if (scale < 3) return;
|
||||||
|
|
||||||
if (!placeAllowed) return;
|
if (!placeAllowed) return;
|
||||||
|
@ -156,61 +162,218 @@ export function initControls(renderer, viewport: HTMLCanvasElement, curStore) {
|
||||||
selectedColor,
|
selectedColor,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
const initialState: State = store.getState();
|
static getMultiTouchDistance(event: TouchEvent) {
|
||||||
[window.lastPosX, window.lastPosY] = initialState.canvas.view;
|
if (event.touches.length < 2) {
|
||||||
let lastScale = initialState.canvas.scale;
|
return 1;
|
||||||
hammertime.on(
|
}
|
||||||
'panstart pinchstart pan pinch panend pinchend',
|
const a = event.touches[0];
|
||||||
({
|
const b = event.touches[1];
|
||||||
type, deltaX, deltaY, scale,
|
return Math.sqrt(
|
||||||
}) => {
|
(b.pageX - a.pageX) ** 2 + (b.pageY - a.pageY) ** 2,
|
||||||
viewport.style.cursor = 'move'; // like google maps
|
);
|
||||||
const { scale: viewportScale } = store.getState().canvas;
|
}
|
||||||
|
|
||||||
// pinch start
|
onTouchStart(event: TouchEvent) {
|
||||||
if (type === 'pinchstart') {
|
event.preventDefault();
|
||||||
store.dispatch(unsetHover());
|
|
||||||
lastScale = viewportScale;
|
this.clickTabStartTime = Date.now();
|
||||||
|
this.clickTabStartCoords = PixelPlainterControls.getTouchCenter(event);
|
||||||
|
const state = this.store.getState();
|
||||||
|
this.clickTabStartView = state.canvas.view;
|
||||||
|
|
||||||
|
if (event.touches.length > 1) {
|
||||||
|
this.startTabScale = state.canvas.scale;
|
||||||
|
this.startTabDist = PixelPlainterControls.getMultiTouchDistance(event);
|
||||||
|
this.isMultiTab = true;
|
||||||
|
} else {
|
||||||
|
this.isMultiTab = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchEnd(event: TouchEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (event.changedTouches.length < 2) {
|
||||||
|
const { pageX, pageY } = event.changedTouches[0];
|
||||||
|
const { clickTabStartCoords, clickTabStartTime } = this;
|
||||||
|
const coordsDiff = [
|
||||||
|
clickTabStartCoords[0] - pageX,
|
||||||
|
clickTabStartCoords[1] - pageY,
|
||||||
|
];
|
||||||
|
// thresholds for single click / holding
|
||||||
|
if (clickTabStartTime > Date.now() - 300
|
||||||
|
&& coordsDiff[0] < 10 && coordsDiff[1] < 10) {
|
||||||
|
const { store, viewport } = this;
|
||||||
|
PixelPlainterControls.placePixel(
|
||||||
|
store,
|
||||||
|
viewport,
|
||||||
|
this.renderer,
|
||||||
|
[pageX, pageY],
|
||||||
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
store.dispatch(unsetHover());
|
||||||
|
}, 500);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// panstart
|
onTouchMove(event: TouchEvent) {
|
||||||
if (type === 'panstart') {
|
event.preventDefault();
|
||||||
store.dispatch(unsetHover());
|
|
||||||
const { view: initView } = store.getState().canvas;
|
|
||||||
[window.lastPosX, window.lastPosY] = initView;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pinch
|
const multiTouch = (event.touches.length > 1);
|
||||||
if (type === 'pinch') {
|
|
||||||
store.dispatch(setScale(lastScale * scale));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// pan
|
||||||
|
const [clientX, clientY] = PixelPlainterControls.getTouchCenter(event);
|
||||||
|
const { store } = this;
|
||||||
|
const state = store.getState();
|
||||||
|
if (this.isMultiTab !== multiTouch) {
|
||||||
|
// if one finger got lifted or added, reset clickTabStart
|
||||||
|
this.isMultiTab = multiTouch;
|
||||||
|
this.clickTabStartCoords = [clientX, clientY];
|
||||||
|
this.clickTabStartView = state.canvas.view;
|
||||||
|
this.startTabDist = PixelPlainterControls.getMultiTouchDistance(event);
|
||||||
|
this.startTabScale = state.canvas.scale;
|
||||||
|
} else {
|
||||||
// pan
|
// pan
|
||||||
|
const { clickTabStartView, clickTabStartCoords } = this;
|
||||||
|
const [lastPosX, lastPosY] = clickTabStartView;
|
||||||
|
|
||||||
|
const deltaX = clientX - clickTabStartCoords[0];
|
||||||
|
const deltaY = clientY - clickTabStartCoords[1];
|
||||||
|
const { scale } = state.canvas;
|
||||||
store.dispatch(setViewCoordinates([
|
store.dispatch(setViewCoordinates([
|
||||||
window.lastPosX - (deltaX / viewportScale),
|
lastPosX - (deltaX / scale),
|
||||||
window.lastPosY - (deltaY / viewportScale),
|
lastPosY - (deltaY / scale),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// pinch end
|
// pinch
|
||||||
if (type === 'pinchend') {
|
if (multiTouch) {
|
||||||
lastScale = viewportScale;
|
const a = event.touches[0];
|
||||||
|
const b = event.touches[1];
|
||||||
|
const { startTabDist, startTabScale } = this;
|
||||||
|
const dist = Math.sqrt(
|
||||||
|
(b.pageX - a.pageX) ** 2 + (b.pageY - a.pageY) ** 2,
|
||||||
|
);
|
||||||
|
const pinchScale = dist / startTabDist;
|
||||||
|
store.dispatch(setScale(startTabScale * pinchScale));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// panend
|
onWheel(event: MouseEvent) {
|
||||||
if (type === 'panend') {
|
const { deltaY } = event;
|
||||||
store.dispatch(onViewFinishChange());
|
const { store } = this;
|
||||||
const { view } = store.getState().canvas;
|
const state = store.getState();
|
||||||
[window.lastPosX, window.lastPosY] = view;
|
const { hover } = state.gui;
|
||||||
viewport.style.cursor = 'auto';
|
let zoompoint = null;
|
||||||
}
|
if (hover) {
|
||||||
},
|
zoompoint = hover;
|
||||||
);
|
}
|
||||||
|
if (deltaY < 0) {
|
||||||
|
store.dispatch(zoomIn(zoompoint));
|
||||||
|
}
|
||||||
|
if (deltaY > 0) {
|
||||||
|
store.dispatch(zoomOut(zoompoint));
|
||||||
|
}
|
||||||
|
store.dispatch(onViewFinishChange());
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('keydown', onKeyPress, false);
|
onMouseMove(event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const { clientX, clientY } = event;
|
||||||
|
const { store, isMouseDown } = this;
|
||||||
|
const state = store.getState();
|
||||||
|
if (isMouseDown) {
|
||||||
|
const { clickTabStartView, clickTabStartCoords } = this;
|
||||||
|
const [lastPosX, lastPosY] = clickTabStartView;
|
||||||
|
const deltaX = clientX - clickTabStartCoords[0];
|
||||||
|
const deltaY = clientY - clickTabStartCoords[1];
|
||||||
|
|
||||||
|
const { scale } = state.canvas;
|
||||||
|
store.dispatch(setViewCoordinates([
|
||||||
|
lastPosX - (deltaX / scale),
|
||||||
|
lastPosY - (deltaY / scale),
|
||||||
|
]));
|
||||||
|
} else {
|
||||||
|
const screenCoor = screenToWorld(
|
||||||
|
state,
|
||||||
|
this.viewport,
|
||||||
|
[clientX, clientY],
|
||||||
|
);
|
||||||
|
store.dispatch(setHover(screenCoor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseOut() {
|
||||||
|
const { store } = this;
|
||||||
|
store.dispatch(unsetHover());
|
||||||
|
}
|
||||||
|
|
||||||
|
onAuxClick(event: MouseEvent) {
|
||||||
|
const { which, clientX, clientY } = event;
|
||||||
|
const { store } = this;
|
||||||
|
// middle mouse button
|
||||||
|
if (which !== 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
const state = store.getState();
|
||||||
|
if (state.canvas.scale < 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const coords = screenToWorld(state, this.viewport, [clientX, clientY]);
|
||||||
|
const clrIndex = this.renderer.getColorIndexOfPixel(...coords);
|
||||||
|
if (clrIndex === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
store.dispatch(selectColor(clrIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyPress(event: KeyboardEvent) {
|
||||||
|
// ignore key presses if modal is open or chat is used
|
||||||
|
if (event.target.nodeName === 'INPUT'
|
||||||
|
|| event.target.nodeName === 'TEXTAREA'
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { store } = this;
|
||||||
|
|
||||||
|
switch (keycode(event)) {
|
||||||
|
case 'up':
|
||||||
|
case 'w':
|
||||||
|
store.dispatch(moveNorth());
|
||||||
|
break;
|
||||||
|
case 'left':
|
||||||
|
case 'a':
|
||||||
|
store.dispatch(moveWest());
|
||||||
|
break;
|
||||||
|
case 'down':
|
||||||
|
case 's':
|
||||||
|
store.dispatch(moveSouth());
|
||||||
|
break;
|
||||||
|
case 'right':
|
||||||
|
case 'd':
|
||||||
|
store.dispatch(moveEast());
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
case 'space':
|
||||||
|
if ($viewport) $viewport.click();
|
||||||
|
return;
|
||||||
|
*/
|
||||||
|
case '+':
|
||||||
|
case 'e':
|
||||||
|
store.dispatch(zoomIn());
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
case 'q':
|
||||||
|
store.dispatch(zoomOut());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeControls() {
|
export default PixelPlainterControls;
|
||||||
document.removeEventListener('keydown', onKeyPress, false);
|
|
||||||
}
|
|
||||||
|
|
|
@ -18,10 +18,7 @@ import {
|
||||||
renderPlaceholder,
|
renderPlaceholder,
|
||||||
renderPotatoPlaceholder,
|
renderPotatoPlaceholder,
|
||||||
} from './render2Delements';
|
} from './render2Delements';
|
||||||
import {
|
import PixelPainterControls from '../controls/PixelPainterControls';
|
||||||
initControls,
|
|
||||||
removeControls,
|
|
||||||
} from '../controls/PixelPainterControls';
|
|
||||||
|
|
||||||
|
|
||||||
import ChunkLoader from './ChunkLoader2D';
|
import ChunkLoader from './ChunkLoader2D';
|
||||||
|
@ -90,7 +87,7 @@ class Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
destructor() {
|
destructor() {
|
||||||
removeControls(this.viewport);
|
this.controls.dispose();
|
||||||
window.removeEventListener('resize', this.resizeHandle);
|
window.removeEventListener('resize', this.resizeHandle);
|
||||||
this.viewport.remove();
|
this.viewport.remove();
|
||||||
}
|
}
|
||||||
|
@ -120,8 +117,8 @@ class Renderer {
|
||||||
canvasSize,
|
canvasSize,
|
||||||
} = state.canvas;
|
} = state.canvas;
|
||||||
this.updateCanvasData(state);
|
this.updateCanvasData(state);
|
||||||
initControls(this, this.viewport, store);
|
|
||||||
this.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize);
|
this.updateScale(viewscale, canvasMaxTiledZoom, view, canvasSize);
|
||||||
|
this.controls = new PixelPainterControls(this, this.viewport, store);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCanvasData(state: State) {
|
updateCanvasData(state: State) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user