diff options
-rw-r--r-- | src/client/views/InkingCanvas.scss | 13 | ||||
-rw-r--r-- | src/client/views/InkingCanvas.tsx | 54 | ||||
-rw-r--r-- | src/client/views/InkingStroke.tsx | 8 |
3 files changed, 48 insertions, 27 deletions
diff --git a/src/client/views/InkingCanvas.scss b/src/client/views/InkingCanvas.scss index 2128401b8..35c8ee942 100644 --- a/src/client/views/InkingCanvas.scss +++ b/src/client/views/InkingCanvas.scss @@ -2,18 +2,19 @@ .inkingCanvas-paths-ink, .inkingCanvas-paths-markers, .inkingCanvas-noSelect, .inkingCanvas-canSelect { position: absolute; - top: -4096px; - left: -4096px; width: 8192px; height: 8192px; - .inkingCanvas-children { - transform: translate(4096px, 4096px); - pointer-events: none; - } cursor:"crosshair"; pointer-events: auto; } +.inkingCanvas-canSelect, +.inkingCanvas-noSelect { + top:-50000px; + left:-50000px; + width: 100000px; + height: 100000px; +} .inkingCanvas-noSelect { pointer-events: none; cursor: "arrow"; diff --git a/src/client/views/InkingCanvas.tsx b/src/client/views/InkingCanvas.tsx index fcf6db390..cad4b74b1 100644 --- a/src/client/views/InkingCanvas.tsx +++ b/src/client/views/InkingCanvas.tsx @@ -1,4 +1,4 @@ -import { action, computed, trace } from "mobx"; +import { action, computed, trace, observable, runInAction } from "mobx"; import { observer } from "mobx-react"; import { Document } from "../../fields/Document"; import { FieldWaiting } from "../../fields/Field"; @@ -14,26 +14,40 @@ import React = require("react"); interface InkCanvasProps { getScreenTransform: () => Transform; Document: Document; + children: () => JSX.Element[]; } @observer export class InkingCanvas extends React.Component<InkCanvasProps> { - static InkOffset: number = 4096; + maxCanvasDim = 8192 / 2; // 1/2 of the maximum canvas dimension for Chrome + @observable inkMidX: number = 0; + @observable inkMidY: number = 0; private _currentStrokeId: string = ""; public static IntersectStrokeRect(stroke: StrokeData, selRect: { left: number, top: number, width: number, height: number }): boolean { - return stroke.pathData.reduce((inside, val) => inside || - (selRect.left < val.x - InkingCanvas.InkOffset && selRect.left + selRect.width > val.x - InkingCanvas.InkOffset && - selRect.top < val.y - InkingCanvas.InkOffset && selRect.top + selRect.height > val.y - InkingCanvas.InkOffset) + return stroke.pathData.reduce((inside: boolean, val) => inside || + (selRect.left < val.x && selRect.left + selRect.width > val.x && + selRect.top < val.y && selRect.top + selRect.height > val.y) , false); } + componentDidMount() { + this.props.Document.GetTAsync(KeyStore.Ink, InkField, ink => runInAction(() => { + if (ink) { + let bounds = Array.from(ink.Data).reduce(([mix, max, miy, may], [id, strokeData]) => + strokeData.pathData.reduce(([mix, max, miy, may], p) => + [Math.min(mix, p.x), Math.max(max, p.x), Math.min(miy, p.y), Math.max(may, p.y)], + [mix, max, miy, may]), + [Number.MAX_VALUE, Number.MIN_VALUE, Number.MAX_VALUE, Number.MIN_VALUE]); + this.inkMidX = (bounds[0] + bounds[1]) / 2; + this.inkMidY = (bounds[2] + bounds[3]) / 2; + } + })); + } + @computed get inkData(): StrokeMap { let map = this.props.Document.GetT(KeyStore.Ink, InkField); - if (!map || map === FieldWaiting) { - return new Map; - } - return new Map(map.Data); + return !map || map === FieldWaiting ? new Map : new Map(map.Data); } set inkData(value: StrokeMap) { @@ -63,9 +77,15 @@ export class InkingCanvas extends React.Component<InkCanvasProps> { } } + @action onPointerUp = (e: PointerEvent): void => { document.removeEventListener("pointermove", this.onPointerMove, true); document.removeEventListener("pointerup", this.onPointerUp, true); + let coord = this.relativeCoordinatesForEvent(e.clientX, e.clientY); + if (Math.abs(coord.x - this.inkMidX) > 500 || Math.abs(coord.y - this.inkMidY) > 500) { + this.inkMidX = coord.x; + this.inkMidY = coord.y; + } e.stopPropagation(); e.preventDefault(); } @@ -87,8 +107,6 @@ export class InkingCanvas extends React.Component<InkCanvasProps> { relativeCoordinatesForEvent = (ex: number, ey: number): { x: number, y: number } => { let [x, y] = this.props.getScreenTransform().transformPoint(ex, ey); - x += InkingCanvas.InkOffset; - y += InkingCanvas.InkOffset; return { x, y }; } @@ -101,30 +119,32 @@ export class InkingCanvas extends React.Component<InkCanvasProps> { @computed get drawnPaths() { - // parse data from server let curPage = this.props.Document.GetNumber(KeyStore.CurPage, -1) let paths = Array.from(this.inkData).reduce((paths, [id, strokeData]) => { if (strokeData.page == -1 || strokeData.page == curPage) paths.push(<InkingStroke key={id} id={id} line={strokeData.pathData} + offsetX={this.maxCanvasDim - this.inkMidX} + offsetY={this.maxCanvasDim - this.inkMidY} color={strokeData.color} width={strokeData.width} tool={strokeData.tool} deleteCallback={this.removeLine} />) return paths; }, [] as JSX.Element[]); - return [<svg className={`inkingCanvas-paths-markers`} key="Markers" > + return [<svg className={`inkingCanvas-paths-markers`} key="Markers" + style={{ left: `${this.inkMidX - this.maxCanvasDim}px`, top: `${this.inkMidY - this.maxCanvasDim}px` }} > {paths.filter(path => path.props.tool == InkTool.Highlighter)} </svg>, - <svg className={`inkingCanvas-paths-ink`} key="Pens" > + <svg className={`inkingCanvas-paths-ink`} key="Pens" + style={{ left: `-${this.inkMidX - this.maxCanvasDim}px`, top: `-${this.inkMidY - this.maxCanvasDim}px` }}> {paths.filter(path => path.props.tool != InkTool.Highlighter)} </svg>]; } render() { let svgCanvasStyle = InkingControl.Instance.selectedTool != InkTool.None ? "canSelect" : "noSelect"; - return ( <div className="inkingCanvas" > - <svg className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} /> - {(this.props.children as any)() /* bcz: is there a better way to know that children is a function? */} + <div className={`inkingCanvas-${svgCanvasStyle}`} onPointerDown={this.onPointerDown} /> + {this.props.children()} {this.drawnPaths} </div > ) diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 44048f377..615f8af7e 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -6,6 +6,8 @@ import React = require("react"); interface StrokeProps { + offsetX: number; + offsetY: number; id: string; line: Array<{ x: number, y: number }>; color: string; @@ -28,7 +30,7 @@ export class InkingStroke extends React.Component<StrokeProps> { } parseData = (line: Array<{ x: number, y: number }>): string => { - return !line.length ? "" : "M " + line.map(p => p.x + " " + p.y).join(" L "); + return !line.length ? "" : "M " + line.map(p => (p.x + this.props.offsetX) + " " + (p.y + this.props.offsetY)).join(" L "); } createStyle() { @@ -43,15 +45,13 @@ export class InkingStroke extends React.Component<StrokeProps> { } } - render() { let pathStyle = this.createStyle(); let pathData = this.parseData(this.props.line); let pointerEvents: any = InkingControl.Instance.selectedTool == InkTool.Eraser ? "all" : "none"; return ( - <path className={(this._strokeTool === InkTool.Highlighter) ? "highlight" : "normal"} - d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round" + <path d={pathData} style={{ ...pathStyle, pointerEvents: pointerEvents }} strokeLinejoin="round" strokeLinecap="round" onPointerOver={this.deleteStroke} onPointerDown={this.deleteStroke} /> ) } |