chessground
Version:
lichess.org chess ui
155 lines (136 loc) • 4.42 kB
text/typescript
import { State } from './state.js';
import { unselect, cancelMove, getKeyAtDomPos, getSnappedKeyAtDomPos, whitePov } from './board.js';
import { eventPosition, isRightButton } from './util.js';
import * as cg from './types.js';
export interface DrawShape {
orig: cg.Key;
dest?: cg.Key;
brush?: string; // if no brush, no shape. label moved to top right of square
modifiers?: DrawModifiers;
piece?: DrawShapePiece;
customSvg?: { html: string; center?: 'orig' | 'dest' | 'label' }; // 100 x 100 viewbox cenetered at [50,50]
label?: { text: string; fill?: string }; // fill is in '#rrggbb' format
}
export interface DrawModifiers {
lineWidth?: number;
hilite?: boolean;
}
export interface DrawShapePiece {
role: cg.Role;
color: cg.Color;
scale?: number;
}
export interface DrawBrush {
key: string;
color: string;
opacity: number;
lineWidth: number;
}
export interface DrawBrushes {
green: DrawBrush;
red: DrawBrush;
blue: DrawBrush;
yellow: DrawBrush;
[color: string]: DrawBrush;
}
export interface Drawable {
enabled: boolean; // can draw
visible: boolean; // can view
defaultSnapToValidMove: boolean;
eraseOnClick: boolean;
onChange?: (shapes: DrawShape[]) => void;
shapes: DrawShape[]; // user shapes
autoShapes: DrawShape[]; // computer shapes
current?: DrawCurrent;
brushes: DrawBrushes;
prevSvgHash: string;
}
export interface DrawCurrent {
orig: cg.Key; // orig key of drawing
dest?: cg.Key; // shape dest, or undefined for circle
mouseSq?: cg.Key; // square being moused over
pos: cg.NumberPair; // relative current position
brush: cg.BrushColor; // brush name for shape
snapToValidMove: boolean; // whether to snap to valid piece moves
}
const brushes: cg.BrushColor[] = ['green', 'red', 'blue', 'yellow'];
export function start(state: State, e: cg.MouchEvent): void {
// support one finger touch only
if (e.touches && e.touches.length > 1) return;
e.stopPropagation();
e.preventDefault();
e.ctrlKey ? unselect(state) : cancelMove(state);
const pos = eventPosition(e)!,
orig = getKeyAtDomPos(pos, whitePov(state), state.dom.bounds());
if (!orig) return;
state.drawable.current = {
orig,
pos,
brush: eventBrush(e),
snapToValidMove: state.drawable.defaultSnapToValidMove,
};
processDraw(state);
}
export function processDraw(state: State): void {
requestAnimationFrame(() => {
const cur = state.drawable.current;
if (cur) {
const keyAtDomPos = getKeyAtDomPos(cur.pos, whitePov(state), state.dom.bounds());
if (!keyAtDomPos) {
cur.snapToValidMove = false;
}
const mouseSq = cur.snapToValidMove
? getSnappedKeyAtDomPos(cur.orig, cur.pos, whitePov(state), state.dom.bounds())
: keyAtDomPos;
if (mouseSq !== cur.mouseSq) {
cur.mouseSq = mouseSq;
cur.dest = mouseSq !== cur.orig ? mouseSq : undefined;
state.dom.redrawNow();
}
processDraw(state);
}
});
}
export function move(state: State, e: cg.MouchEvent): void {
if (state.drawable.current) state.drawable.current.pos = eventPosition(e)!;
}
export function end(state: State): void {
const cur = state.drawable.current;
if (cur) {
if (cur.mouseSq) addShape(state.drawable, cur);
cancel(state);
}
}
export function cancel(state: State): void {
if (state.drawable.current) {
state.drawable.current = undefined;
state.dom.redraw();
}
}
export function clear(state: State): void {
if (state.drawable.shapes.length) {
state.drawable.shapes = [];
state.dom.redraw();
onChange(state.drawable);
}
}
function eventBrush(e: cg.MouchEvent): cg.BrushColor {
const modA = (e.shiftKey || e.ctrlKey) && isRightButton(e);
const modB = e.altKey || e.metaKey || e.getModifierState?.('AltGraph');
return brushes[(modA ? 1 : 0) + (modB ? 2 : 0)];
}
function addShape(drawable: Drawable, cur: DrawCurrent): void {
const sameShape = (s: DrawShape) => s.orig === cur.orig && s.dest === cur.dest;
const similar = drawable.shapes.find(sameShape);
if (similar) drawable.shapes = drawable.shapes.filter(s => !sameShape(s));
if (!similar || similar.brush !== cur.brush)
drawable.shapes.push({
orig: cur.orig,
dest: cur.dest,
brush: cur.brush,
});
onChange(drawable);
}
function onChange(drawable: Drawable): void {
if (drawable.onChange) drawable.onChange(drawable.shapes);
}