viewport coordinate system
This commit is contained in:
parent
f396b4a16a
commit
45defd0a5b
|
@ -1,8 +1,12 @@
|
|||
import React, { createRef, useContext, useEffect } from "react";
|
||||
import React, { createRef, useCallback, useContext, useEffect } from "react";
|
||||
import { Canvas } from "../lib/canvas";
|
||||
import { useAppContext } from "../contexts/AppContext";
|
||||
import { PanZoomWrapper } from "@sc07-canvas/lib/src/renderer";
|
||||
import { RendererContext } from "@sc07-canvas/lib/src/renderer/RendererContext";
|
||||
import { ViewportMoveEvent } from "@sc07-canvas/lib/src/renderer/PanZoom";
|
||||
import throttle from "lodash.throttle";
|
||||
import { ICanvasPosition } from "../types";
|
||||
import { Routes } from "../lib/routes";
|
||||
|
||||
export const CanvasWrapper = () => {
|
||||
// to prevent safari from blurring things, use the zoom css property
|
||||
|
@ -17,30 +21,37 @@ export const CanvasWrapper = () => {
|
|||
|
||||
const CanvasInner = () => {
|
||||
const canvasRef = createRef<HTMLCanvasElement>();
|
||||
const { config } = useAppContext();
|
||||
const { config, setCanvasPosition } = useAppContext();
|
||||
const PanZoom = useContext(RendererContext);
|
||||
// const { centerView } = useControls();
|
||||
|
||||
// useTransformEffect(
|
||||
// throttle(({ state, instance }) => {
|
||||
// const params = new URLSearchParams();
|
||||
// params.set("x", state.positionX + "");
|
||||
// params.set("y", state.positionY + "");
|
||||
// params.set("zoom", state.scale + "");
|
||||
// window.location.hash = params.toString();
|
||||
// }, 1000)
|
||||
// );
|
||||
|
||||
useEffect(() => {
|
||||
if (!config.canvas || !canvasRef.current) return;
|
||||
const canvas = canvasRef.current!;
|
||||
const canvasInstance = new Canvas(config, canvas, PanZoom);
|
||||
// centerView();
|
||||
|
||||
const handleViewportMove = throttle((state: ViewportMoveEvent) => {
|
||||
const pos = canvasInstance.panZoomTransformToCanvas();
|
||||
|
||||
const canvasPosition: ICanvasPosition = {
|
||||
x: pos.canvasX,
|
||||
y: pos.canvasY,
|
||||
zoom: state.scale >> 0,
|
||||
};
|
||||
|
||||
setCanvasPosition(canvasPosition);
|
||||
|
||||
window.location.replace(Routes.canvas(canvasPosition));
|
||||
}, 1000);
|
||||
|
||||
PanZoom.addListener("viewportMove", handleViewportMove);
|
||||
|
||||
return () => {
|
||||
canvasInstance.destroy();
|
||||
PanZoom.removeListener("viewportMove", handleViewportMove);
|
||||
};
|
||||
}, [canvasRef, config]);
|
||||
}, [PanZoom, canvasRef, config, setCanvasPosition]);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
|
|
|
@ -68,8 +68,17 @@ export const Pallete = () => {
|
|||
};
|
||||
|
||||
export const CanvasMeta = () => {
|
||||
const { canvasPosition } = useAppContext();
|
||||
|
||||
return (
|
||||
<div id="canvas-meta">
|
||||
{canvasPosition && (
|
||||
<span>
|
||||
<button className="btn-link">
|
||||
({canvasPosition.x}, {canvasPosition.y})
|
||||
</button>
|
||||
</span>
|
||||
)}
|
||||
<span>
|
||||
Pixels: <span>123</span>
|
||||
</span>
|
||||
|
|
|
@ -6,7 +6,12 @@ import React, {
|
|||
useState,
|
||||
} from "react";
|
||||
import { Socket } from "socket.io-client";
|
||||
import { ClientConfig, IAppContext, IPalleteContext } from "../types";
|
||||
import {
|
||||
ClientConfig,
|
||||
IAppContext,
|
||||
ICanvasPosition,
|
||||
IPalleteContext,
|
||||
} from "../types";
|
||||
import { AuthSession } from "@sc07-canvas/lib/src/net";
|
||||
import { number } from "prop-types";
|
||||
import Network from "../lib/network";
|
||||
|
@ -18,6 +23,7 @@ export const useAppContext = () => useContext(appContext);
|
|||
export const AppContext = ({ children }: PropsWithChildren) => {
|
||||
const [config, setConfig] = useState<ClientConfig>(undefined as any);
|
||||
const [auth, setAuth] = useState<AuthSession>();
|
||||
const [canvasPosition, setCanvasPosition] = useState<ICanvasPosition>();
|
||||
|
||||
useEffect(() => {
|
||||
function handleConfig(config: ClientConfig) {
|
||||
|
@ -41,7 +47,9 @@ export const AppContext = ({ children }: PropsWithChildren) => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<appContext.Provider value={{ config, user: auth }}>
|
||||
<appContext.Provider
|
||||
value={{ config, user: auth, canvasPosition, setCanvasPosition }}
|
||||
>
|
||||
{config ? children : "Loading..."}
|
||||
</appContext.Provider>
|
||||
);
|
||||
|
|
|
@ -148,6 +148,46 @@ export class Canvas extends EventEmitter {
|
|||
});
|
||||
}
|
||||
|
||||
panZoomTransformToCanvas() {
|
||||
const { x, y, scale: zoom } = this.PanZoom.transform;
|
||||
const rect = this.canvas.getBoundingClientRect();
|
||||
|
||||
let canvasX = 0;
|
||||
let canvasY = 0;
|
||||
|
||||
if (this.PanZoom.flags.useZoom) {
|
||||
// css zoom doesn't change the bounding client rect
|
||||
// therefore dividing by zoom doesn't return the correct output
|
||||
canvasX = this.canvas.width - (x + rect.width / 2);
|
||||
canvasY = this.canvas.height - (y + rect.height / 2);
|
||||
} else {
|
||||
canvasX = this.canvas.width / 2 - (x + rect.width / zoom);
|
||||
canvasY = this.canvas.height / 2 - (y + rect.height / zoom);
|
||||
|
||||
canvasX += this.canvas.width;
|
||||
canvasY += this.canvas.height;
|
||||
}
|
||||
|
||||
canvasX >>= 0;
|
||||
canvasY >>= 0;
|
||||
|
||||
return { canvasX, canvasY };
|
||||
}
|
||||
|
||||
debug(x: number, y: number, id?: string) {
|
||||
if (document.getElementById("debug-" + id)) {
|
||||
document.getElementById("debug-" + id)!.style.top = y + "px";
|
||||
document.getElementById("debug-" + id)!.style.left = x + "px";
|
||||
return;
|
||||
}
|
||||
let el = document.createElement("div");
|
||||
if (id) el.id = "debug-" + id;
|
||||
el.classList.add("debug-point");
|
||||
el.style.setProperty("top", y + "px");
|
||||
el.style.setProperty("left", x + "px");
|
||||
document.body.appendChild(el);
|
||||
}
|
||||
|
||||
screenToPos(x: number, y: number) {
|
||||
// the rendered dimentions in the browser
|
||||
const rect = this.canvas.getBoundingClientRect();
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { ICanvasPosition } from "../types";
|
||||
|
||||
export const Routes = {
|
||||
canvas: (pos: ICanvasPosition) => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("x", pos.x + "");
|
||||
params.set("y", pos.y + "");
|
||||
params.set("zoom", pos.zoom + "");
|
||||
|
||||
return "/#" + params;
|
||||
},
|
||||
};
|
|
@ -122,5 +122,19 @@ main {
|
|||
image-rendering: crisp-edges;
|
||||
}
|
||||
|
||||
.btn-link {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&:active {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@import "./components/Pallete.scss";
|
||||
@import "./board.scss";
|
||||
|
|
|
@ -18,12 +18,20 @@ export interface ClientToServerEvents {
|
|||
export interface IAppContext {
|
||||
config: ClientConfig;
|
||||
user?: AuthSession;
|
||||
canvasPosition?: ICanvasPosition;
|
||||
setCanvasPosition: (v: ICanvasPosition) => void;
|
||||
}
|
||||
|
||||
export interface IPalleteContext {
|
||||
color?: number;
|
||||
}
|
||||
|
||||
export interface ICanvasPosition {
|
||||
x: number;
|
||||
y: number;
|
||||
zoom: number;
|
||||
}
|
||||
|
||||
// other
|
||||
|
||||
export type Pixel = {
|
||||
|
|
|
@ -95,10 +95,17 @@ export interface HoverEvent {
|
|||
clientY: number;
|
||||
}
|
||||
|
||||
export interface ViewportMoveEvent {
|
||||
scale: number;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface PanZoomEvents {
|
||||
doubleTap: (e: TouchEvent) => void;
|
||||
click: (e: ClickEvent) => void;
|
||||
hover: (e: HoverEvent) => void;
|
||||
viewportMove: (e: ViewportMoveEvent) => void;
|
||||
}
|
||||
|
||||
export class PanZoom extends EventEmitter<PanZoomEvents> {
|
||||
|
@ -446,6 +453,12 @@ export class PanZoom extends EventEmitter<PanZoomEvents> {
|
|||
}
|
||||
|
||||
update() {
|
||||
this.emit("viewportMove", {
|
||||
scale: this.transform.scale,
|
||||
x: this.transform.x,
|
||||
y: this.transform.y,
|
||||
});
|
||||
|
||||
if (this.flags.useZoom) {
|
||||
this.$zoom.style.setProperty("zoom", this.transform.scale * 100 + "%");
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue