feat: improve control of middleware selector

This commit is contained in:
chenshenhai 2024-08-03 21:41:11 +08:00
parent 4ecd179940
commit 1dda8be77e
18 changed files with 446 additions and 116 deletions

View file

@ -122,6 +122,8 @@ export class Board<T extends BoardExtendEventMap = BoardExtendEventMap> {
// }, throttleTime)
// );
this.#watcher.on('pointMove', this.#handlePointMove.bind(this));
this.#watcher.on('pointLeave', this.#handlePointLeave.bind(this));
this.#watcher.on('hover', this.#handleHover.bind(this));
this.#watcher.on('wheel', this.#handleWheel.bind(this));
this.#watcher.on('wheelScale', this.#handleWheelScale.bind(this));
@ -166,6 +168,16 @@ export class Board<T extends BoardExtendEventMap = BoardExtendEventMap> {
}
}
#handlePointLeave(e: BoardWatcherEventMap['pointLeave']) {
for (let i = 0; i < this.#activeMiddlewareObjs.length; i++) {
const obj = this.#activeMiddlewareObjs[i];
const result = obj?.pointLeave?.(e);
if (result === false) {
return;
}
}
}
#handleHover(e: BoardWatcherEventMap['hover']) {
for (let i = 0; i < this.#activeMiddlewareObjs.length; i++) {
const obj = this.#activeMiddlewareObjs[i];

View file

@ -11,7 +11,7 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
#hasDestroyed: boolean = false;
constructor(opts: BoardWatcherOptions) {
super();
const store = new Store<BoardWatcherStore>({ defaultStorage: { hasPointDown: false, prevClickPoint: null } });
const store = new Store<BoardWatcherStore>({ defaultStorage: { hasPointDown: false, prevClickPoint: null, inCanvas: true } });
this.#store = store;
this.#opts = opts;
this.#init();
@ -30,7 +30,7 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
container.addEventListener('mousedown', this.#onPointStart);
container.addEventListener('mousemove', this.#onPointMove);
container.addEventListener('mouseup', this.#onPointEnd);
container.addEventListener('mouseleave', this.#onPointLeave);
// container.addEventListener('mouseleave', this.#onPointLeave);
container.addEventListener('wheel', this.#onWheel, { passive: false });
container.addEventListener('click', this.#onClick);
container.addEventListener('contextmenu', this.#onContextMenu);
@ -110,9 +110,6 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
#onPointLeave = (e: MouseEvent) => {
this.#store.set('hasPointDown', false);
if (!this.#isInTarget(e)) {
return;
}
e.preventDefault();
const point = this.#getPoint(e);
this.trigger('pointLeave', { point });
@ -169,8 +166,13 @@ export class BoardWatcher extends EventEmitter<BoardWatcherEventMap> {
#onHover = (e: MouseEvent) => {
if (!this.#isInTarget(e)) {
if (this.#store.get('inCanvas') === true) {
this.#store.set('inCanvas', false);
this.#onPointLeave(e);
}
return;
}
this.#store.set('inCanvas', true);
// if (!this.#store.get('hasPointDown')) {
// return;
// }

View file

@ -6,6 +6,8 @@ const infoTextColor = '#ffffff';
export const infoFontSize = 10;
export const infoLineHeight = 16;
export const MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE = '@middleware/internal-event/show-info-angle';
export const defaltStyle: MiddlewareInfoStyle = {
textBackground: infoBackground,
textColor: infoTextColor

View file

@ -1,6 +1,6 @@
import type { PointSize, ViewContext2D } from '@idraw/types';
import { rotateByCenter } from '@idraw/util';
import type { MiddlewareInfoStyle } from './types';
import type { MiddlewareInfoStyle } from '@idraw/types';
const fontFamily = 'monospace';

View file

@ -3,13 +3,21 @@ import { formatNumber, getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot
import { keySelectedElementList, keyActionType, keyGroupQueue } from '../selector';
import { drawSizeInfoText, drawPositionInfoText, drawAngleInfoText } from './draw-info';
import type { DeepInfoSharedStorage } from './types';
import { defaltStyle } from './config';
import { defaltStyle, MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE } from './config';
export { MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE };
const infoFontSize = 10;
const infoLineHeight = 16;
export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage, CoreEventMap, MiddlewareInfoConfig> = (opts, config) => {
const { boardContent, calculator } = opts;
export const MiddlewareInfo: BoardMiddleware<
DeepInfoSharedStorage,
CoreEventMap & {
[MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE]: { show: boolean };
},
MiddlewareInfoConfig
> = (opts, config) => {
const { boardContent, calculator, eventHub } = opts;
const { overlayContext } = boardContent;
const innerConfig = {
...defaltStyle,
@ -21,9 +29,23 @@ export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage, CoreEventMap
textColor
};
let showAngleInfo = true;
const showInfoAngleCallback = ({ show }: { show: boolean }) => {
showAngleInfo = show;
};
return {
name: '@middleware/info',
use() {
eventHub.on(MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE, showInfoAngleCallback);
},
disuse() {
eventHub.off(MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE, showInfoAngleCallback);
},
beforeDrawFrame({ snapshot }) {
const { sharedStore } = snapshot;
@ -123,18 +145,20 @@ export const MiddlewareInfo: BoardMiddleware<DeepInfoSharedStorage, CoreEventMap
style
});
drawAngleInfoText(overlayContext, {
point: {
x: rectInfo.top.x + infoFontSize,
y: rectInfo.top.y - infoFontSize * 2
},
rotateCenter: rectInfo.center,
angle: totalAngle,
text: angleText,
fontSize: infoFontSize,
lineHeight: infoLineHeight,
style
});
if (showAngleInfo) {
drawAngleInfoText(overlayContext, {
point: {
x: rectInfo.top.x + infoFontSize + 4,
y: rectInfo.top.y - infoFontSize * 2 - 18
},
rotateCenter: rectInfo.center,
angle: totalAngle,
text: angleText,
fontSize: infoFontSize,
lineHeight: infoLineHeight,
style
});
}
}
}
}

View file

@ -31,6 +31,11 @@ export const resizeControllerBorderWidth = 4;
export const areaBorderWidth = 1;
export const controllerSize = 10;
// export const rotateControllerSize = 16;
export const rotateControllerSize = 20;
export const rotateControllerPosition = 22;
const activeColor = '#1973ba';
const activeAreaColor = '#1976d21c';
const lockedColor = '#5b5959b5';

View file

@ -0,0 +1,49 @@
import type { ViewRectVertexes, ElementSizeController, ViewContext2D, ViewSizeInfo, ViewScaleInfo } from '@idraw/types';
import { calcViewPointSize } from '@idraw/util';
function drawDebugControllerVertexes(opts: {
ctx: ViewContext2D;
vertexes: ViewRectVertexes;
viewScaleInfo: ViewScaleInfo;
viewSizeInfo: ViewSizeInfo;
}): boolean {
const { ctx, viewScaleInfo, vertexes } = opts;
const v0 = calcViewPointSize(vertexes[0], { viewScaleInfo });
const v1 = calcViewPointSize(vertexes[1], { viewScaleInfo });
const v2 = calcViewPointSize(vertexes[2], { viewScaleInfo });
const v3 = calcViewPointSize(vertexes[3], { viewScaleInfo });
ctx.beginPath();
ctx.fillStyle = '#FF0000A1';
ctx.moveTo(v0.x, v0.y);
ctx.lineTo(v1.x, v1.y);
ctx.lineTo(v2.x, v2.y);
ctx.lineTo(v3.x, v3.y);
ctx.lineTo(v0.x, v0.y);
ctx.closePath();
ctx.fill();
return false;
}
export function drawDebugStoreSelectedElementController(
ctx: ViewContext2D,
controller: ElementSizeController | null,
opts: {
viewSizeInfo: ViewSizeInfo;
viewScaleInfo: ViewScaleInfo;
}
) {
if (!controller) {
return;
}
const { viewSizeInfo, viewScaleInfo } = opts;
const { left, right, top, bottom, topLeft, topRight, bottomLeft, bottomRight, rotate } = controller;
const ctrls = [left, right, top, bottom, topLeft, topRight, bottomLeft, bottomRight, rotate];
for (let i = 0; i < ctrls.length; i++) {
const ctrl = ctrls[i];
drawDebugControllerVertexes({ ctx, vertexes: ctrl.vertexes, viewSizeInfo, viewScaleInfo });
}
}

View file

@ -13,8 +13,8 @@ import type {
} from '@idraw/types';
import { rotateElementVertexes, calcViewPointSize, calcViewVertexes, calcViewElementSize } from '@idraw/util';
import type { AreaSize } from './types';
import { resizeControllerBorderWidth, areaBorderWidth, selectWrapperBorderWidth, controllerSize } from './config';
import { drawVertexes, drawLine, drawCircleController, drawCrossVertexes } from './draw-base';
import { resizeControllerBorderWidth, areaBorderWidth, selectWrapperBorderWidth } from './config';
import { drawVertexes, drawCircleController, drawCrossVertexes } from './draw-base';
// import { drawAuxiliaryExperimentBox } from './draw-auxiliary';
export function drawHoverVertexesWrapper(
@ -81,6 +81,7 @@ export function drawSelectedElementControllersVertexes(
element: Element | null;
calculator: ViewCalculator;
style: MiddlewareSelectorStyle;
rotateControllerPattern: ViewContext2D;
}
) {
if (!controller) {
@ -88,12 +89,15 @@ export function drawSelectedElementControllersVertexes(
}
const {
hideControllers,
style
style,
rotateControllerPattern,
viewSizeInfo
// calculator, element, viewScaleInfo, viewSizeInfo
} = opts;
const { devicePixelRatio = 1 } = viewSizeInfo;
const { activeColor } = style;
const { elementWrapper, topLeft, topRight, bottomLeft, bottomRight, top, rotate } = controller;
const { elementWrapper, topLeft, topRight, bottomLeft, bottomRight, rotate } = controller;
const wrapperOpts = { borderColor: activeColor, borderWidth: selectWrapperBorderWidth, background: 'transparent', lineDash: [] };
const ctrlOpts = { ...wrapperOpts, borderWidth: resizeControllerBorderWidth, background: '#FFFFFF' };
@ -103,12 +107,26 @@ export function drawSelectedElementControllersVertexes(
// drawVertexes(ctx, calcViewVertexes(top.vertexes, opts), ctrlOpts);
// drawVertexes(ctx, calcViewVertexes(bottom.vertexes, opts), ctrlOpts);
if (!hideControllers) {
drawLine(ctx, calcViewPointSize(top.center, opts), calcViewPointSize(rotate.center, opts), { ...ctrlOpts, borderWidth: 2 });
// drawLine(ctx, calcViewPointSize(top.center, opts), calcViewPointSize(rotate.center, opts), { ...ctrlOpts, borderWidth: 2 });
drawVertexes(ctx, calcViewVertexes(topLeft.vertexes, opts), ctrlOpts);
drawVertexes(ctx, calcViewVertexes(topRight.vertexes, opts), ctrlOpts);
drawVertexes(ctx, calcViewVertexes(bottomLeft.vertexes, opts), ctrlOpts);
drawVertexes(ctx, calcViewVertexes(bottomRight.vertexes, opts), ctrlOpts);
drawCircleController(ctx, calcViewPointSize(rotate.center, opts), { ...ctrlOpts, size: controllerSize, borderWidth: 2 });
// TODO
drawCircleController(ctx, calcViewPointSize(rotate.center, opts), { ...ctrlOpts, size: rotate.size, borderWidth: 0 });
const rotateCenter = calcViewPointSize(rotate.center, opts);
ctx.drawImage(
rotateControllerPattern.canvas,
0,
0,
rotateControllerPattern.canvas.width / devicePixelRatio,
rotateControllerPattern.canvas.height / devicePixelRatio,
rotateCenter.x - rotate.size / 2,
rotateCenter.y - rotate.size / 2,
rotate.size,
rotate.size
);
}
// drawAuxiliaryExperimentBox(ctx, {

View file

@ -72,6 +72,8 @@ import {
keyEnableSelectInGroup,
keyEnableSnapToGrid,
controllerSize,
rotateControllerSize,
rotateControllerPosition,
defaultStyle
// keyDebugElemCenter,
// keyDebugEnd0,
@ -83,11 +85,20 @@ import {
import { calcReferenceInfo } from './reference';
import { coreEventKeys } from '../../config';
import { keyLayoutIsSelected } from '../layout-selector';
import { createRotateControllerPattern } from './pattern';
import { MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE } from '../info';
// import { drawDebugStoreSelectedElementController } from './draw-debug';
export { keySelectedElementList, keyHoverElement, keyActionType, keyResizeType, keyGroupQueue };
export type { DeepSelectorSharedStorage, ActionType };
export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, CoreEventMap, MiddlewareSelectorConfig> = (opts, config) => {
export const MiddlewareSelector: BoardMiddleware<
DeepSelectorSharedStorage,
CoreEventMap & {
[MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE]: { show: boolean };
},
MiddlewareSelectorConfig
> = (opts, config) => {
const innerConfig = {
...defaultStyle,
...config
@ -102,6 +113,11 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
let inBusyMode: 'resize' | 'drag' | 'drag-list' | 'area' | null = null;
let hasChangedData: boolean | null = null;
const rotateControllerPattern = createRotateControllerPattern({
fill: style.activeColor,
devicePixelRatio: sharer.getActiveViewSizeInfo().devicePixelRatio
});
sharer.setSharedStorage(keyActionType, null);
sharer.setSharedStorage(keyEnableSnapToGrid, true);
@ -140,15 +156,24 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
sharer.setSharedStorage(keyHoverElementVertexes, vertexes);
};
const updateSelectedElementList = (list: Element<ElementType>[], opts?: { triggerEvent?: boolean }) => {
sharer.setSharedStorage(keySelectedElementList, list);
const updateSelectedElemenetController = () => {
const list = sharer.getSharedStorage(keySelectedElementList);
if (list.length === 1) {
const controller = calcElementSizeController(list[0], {
groupQueue: sharer.getSharedStorage(keyGroupQueue),
controllerSize,
viewScaleInfo: sharer.getActiveViewScaleInfo()
viewScaleInfo: sharer.getActiveViewScaleInfo(),
rotateControllerPosition,
rotateControllerSize
});
sharer.setSharedStorage(keySelectedElementController, controller);
}
};
const updateSelectedElementList = (list: Element<ElementType>[], opts?: { triggerEvent?: boolean }) => {
sharer.setSharedStorage(keySelectedElementList, list);
if (list.length === 1) {
updateSelectedElemenetController();
sharer.setSharedStorage(keySelectedElementPosition, getElementPositionFromList(list[0].uuid, sharer.getActiveStorage('data')?.elements || []));
} else {
sharer.setSharedStorage(keySelectedElementController, null);
@ -260,6 +285,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
const resizeType = sharer.getSharedStorage(keyResizeType);
const actionType = sharer.getSharedStorage(keyActionType);
const groupQueue = sharer.getSharedStorage(keyGroupQueue);
const triggerCursor = (target: PointTarget) => {
if (layoutIsSelected === true) {
return;
@ -327,6 +353,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
calculator
})
});
triggerCursor(target);
if (target.type === null) {
@ -478,6 +505,9 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (actionType === 'drag') {
hasChangedData = true;
inBusyMode = 'drag';
eventHub.trigger(MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE, { show: false });
if (data && elems?.length === 1 && moveOriginalStartElementSize && originalStart && end && elems[0]?.operations?.locked !== true) {
const { moveX, moveY } = calcMoveInGroup(originalStart, end, groupQueue);
@ -648,6 +678,10 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
moveOriginalStartPoint = null;
moveOriginalStartElementSize = null;
if (actionType === 'drag') {
eventHub.trigger(MIDDLEWARE_INTERNAL_EVENT_SHOW_INFO_ANGLE, { show: true });
}
if (actionType === 'resize' && resizeType) {
sharer.setSharedStorage(keyResizeType, null);
needDrawFrame = true;
@ -720,11 +754,11 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
},
pointLeave() {
prevPoint = null;
moveOriginalStartPoint = null;
moveOriginalStartElementSize = null;
clear();
viewer.drawFrame();
inBusyMode = null;
sharer.setSharedStorage(keyResizeType, null);
eventHub.trigger(coreEventKeys.CURSOR, {
type: 'default'
});
},
doubleClick(e: PointWatcherEvent) {
@ -758,6 +792,13 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
sharer.setSharedStorage(keyActionType, null);
},
wheel() {
updateSelectedElemenetController();
},
wheelScale() {
updateSelectedElemenetController();
},
contextMenu: (e: PointWatcherEvent) => {
const groupQueue = sharer.getSharedStorage(keyGroupQueue);
@ -825,16 +866,8 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
const enableSnapToGrid = sharedStore[keyEnableSnapToGrid];
const drawBaseOpts = { calculator, viewScaleInfo, viewSizeInfo, style };
// const selectedElementController = sharedStore[keySelectedElementController];
// const resizeType: ResizeType | null = sharedStore[keyResizeType];
const selectedElementController = elem
? calcElementSizeController(elem, {
groupQueue,
controllerSize: 10,
viewScaleInfo
})
: null;
const selectedElementController = sharedStore[keySelectedElementController];
const isHoverLocked: boolean = !!hoverElement?.operations?.locked;
@ -845,11 +878,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (isHoverLocked) {
drawLockedVertexesWrapper(overlayContext, hoverElementVertexes, {
...drawBaseOpts,
controller: calcElementSizeController(hoverElement, {
groupQueue,
controllerSize: 10,
viewScaleInfo
}),
controller: selectedElementController,
style
});
} else {
@ -862,6 +891,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
element: elem,
calculator,
hideControllers: !!isMoving && actionType === 'drag',
rotateControllerPattern,
style
});
if (actionType === 'drag') {
@ -892,11 +922,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
if (isHoverLocked) {
drawLockedVertexesWrapper(overlayContext, hoverElementVertexes, {
...drawBaseOpts,
controller: calcElementSizeController(hoverElement, {
groupQueue,
controllerSize: 10,
viewScaleInfo
}),
controller: selectedElementController,
style
});
} else {
@ -909,6 +935,7 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
element: elem,
calculator,
hideControllers: !!isMoving && actionType === 'drag',
rotateControllerPattern,
style
});
if (actionType === 'drag') {
@ -946,6 +973,12 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
}
}
// // TODO: debug
// drawDebugStoreSelectedElementController(overlayContext, sharer.getSharedStorage(keySelectedElementController), {
// viewScaleInfo,
// viewSizeInfo
// });
// // TODO mock data
// const elemCenter: any = sharer.getSharedStorage(keyDebugElemCenter);
// const startVertical = sharer.getSharedStorage(keyDebugStartVertical);

View file

@ -0,0 +1,102 @@
import type { Element } from '@idraw/types';
import { createUUID } from '@idraw/util';
export const createIconRotate = (opts?: { fill?: string }) => {
const iconRotate: Element<'path'> = {
uuid: createUUID(),
type: 'path',
x: 0,
y: 0,
w: 200,
h: 200,
detail: {
commands: [
{
type: 'M',
params: [512, 0]
},
{
type: 'c',
params: [282.8, 0, 512, 229.2, 512, 512]
},
{
type: 's',
params: [-229.2, 512, -512, 512]
},
{
type: 'S',
params: [0, 794.8, 0, 512, 229.2, 0, 512, 0]
},
{
type: 'z',
params: []
},
{
type: 'm',
params: [309.8, 253.8]
},
{
type: 'c',
params: [0, -10.5, -6.5, -19.8, -15.7, -23.8, -9.7, -4, -21, -2, -28.2, 5.6]
},
{
type: 'l',
params: [-52.5, 52]
},
{
type: 'c',
params: [
-56.9, -53.7, -133.9, -85.5, -213.4, -85.5, -170.7, 0, -309.8, 139.2, -309.8, 309.8, 0, 170.6, 139.2, 309.8, 309.8, 309.8, 92.4, 0, 179.5, -40.8,
238.4, -111.8, 4, -5.2, 4, -12.9, -0.8, -17.3
]
},
{
type: 'L',
params: [694.3, 637]
},
{
type: 'c',
params: [
-2.8, -2.4, -6.5, -3.6, -10.1, -3.6, -3.6, 0.4, -7.3, 2, -9.3, 4.8, -39.5, 51.2, -98.8, 80.3, -163, 80.3, -113.8, 0, -206.5, -92.8, -206.5, -206.5,
0, -113.8, 92.8, -206.5, 206.5, -206.5, 52.8, 0, 102.9, 20.2, 140.8, 55.3
]
},
{
type: 'L',
params: [597, 416.5]
},
{
type: 'c',
params: [-7.7, 7.3, -9.7, 18.6, -5.6, 27.9, 4, 9.7, 13.3, 16.1, 23.8, 16.1]
},
{
type: 'H',
params: [796]
},
{
type: 'c',
params: [14.1, 0, 25.8, -11.7, 25.8, -25.8]
},
{
type: 'V',
params: [253.8]
},
{
type: 'z',
params: []
}
],
fill: '#2c2c2c',
stroke: 'transparent',
strokeWidth: 0,
originX: 0,
originY: 0,
originW: 1024,
originH: 1024,
opacity: 1,
...opts
}
};
return iconRotate;
};

View file

@ -0,0 +1,45 @@
import type { ViewContext2D } from '@idraw/types';
import { createOffscreenContext2D } from '@idraw/util';
import { drawElement } from '@idraw/renderer';
import { createIconRotate } from './icon-rotate';
export function createRotateControllerPattern(opts: { fill: string; devicePixelRatio: number }): ViewContext2D {
const { fill, devicePixelRatio } = opts;
const iconRotate = createIconRotate({ fill });
const { w, h } = iconRotate;
const context2d = createOffscreenContext2D({
width: w,
height: h,
devicePixelRatio
});
// context2d.fillStyle = 'red'; // TODO
// context2d.fillRect(0, 0, size, size);
drawElement(context2d, iconRotate, {
loader: undefined as any,
viewScaleInfo: {
scale: 1,
offsetTop: 0,
offsetBottom: 0,
offsetLeft: 0,
offsetRight: 0
},
viewSizeInfo: {
width: w,
height: h,
devicePixelRatio,
contextWidth: w,
contextHeight: h
},
parentElementSize: {
x: 0,
y: 0,
w,
h
},
parentOpacity: 1
});
return context2d;
}

View file

@ -839,7 +839,7 @@ export function rotateElement(
});
const startAngle = limitAngle(angle);
const changedRadian = calcRadian(elemCenter, start, end);
const endAngle = startAngle + parseRadianToAngle(changedRadian);
const endAngle = limitAngle(startAngle + parseRadianToAngle(changedRadian));
return {
x,

View file

@ -4,6 +4,7 @@ export { drawImage } from './image';
export { drawSVG } from './svg';
export { drawHTML } from './html';
export { drawText } from './text';
export { drawGroup, drawElement } from './group';
export { drawElementList } from './elements';
export { drawLayout } from './layout';
export { drawGlobalBackground } from './global';

View file

@ -123,4 +123,16 @@ export class Renderer extends EventEmitter<RendererEventMap> implements BoardRen
}
}
export { drawRect } from './draw';
export {
drawCircle,
drawRect,
drawImage,
drawSVG,
drawHTML,
drawText,
drawGroup,
drawElement,
drawElementList,
drawLayout,
drawGlobalBackground
} from './draw';

View file

@ -169,5 +169,6 @@ export interface BoardWatcherOptions {
export interface BoardWatcherStore {
hasPointDown: boolean;
inCanvas: boolean;
prevClickPoint: Point | null;
}

View file

@ -20,6 +20,7 @@ export interface ElementSizeControllerItem {
type: ElementSizeControllerType;
vertexes: ViewRectVertexes;
center: PointSize;
size: number;
}
export interface ElementSizeController {

View file

@ -21,15 +21,20 @@ export function calcElementSizeController(
elemSize: ElementSize,
opts: {
groupQueue: Element<'group'>[];
controllerSize?: number;
controllerSize: number;
rotateControllerSize: number;
rotateControllerPosition: number;
viewScaleInfo: ViewScaleInfo;
}
): ElementSizeController {
const { groupQueue, controllerSize, viewScaleInfo } = opts;
const { groupQueue, controllerSize, viewScaleInfo, rotateControllerSize, rotateControllerPosition } = opts;
const ctrlSize = (controllerSize && controllerSize > 0 ? controllerSize : 8) / viewScaleInfo.scale;
const { x, y, w, h, angle = 0 } = elemSize;
const rotateCtrlSize = rotateControllerSize;
const rotateCtrlPos = rotateControllerPosition;
const ctrlGroupQueue = [
...[
{
@ -53,10 +58,10 @@ export function calcElementSizeController(
const vertexes = calcElementVertexesInGroup(elemSize, { groupQueue }) as ViewRectVertexes;
const rotateElemVertexes = calcElementVertexesInGroup(
{
x: x - ctrlSize * 2,
y: y - ctrlSize * 2,
h: h + ctrlSize * 4,
w: w + ctrlSize * 4,
x: x,
y: y - (rotateCtrlPos + rotateCtrlSize / 2) / viewScaleInfo.scale,
h: h + (rotateCtrlPos * 2 + rotateCtrlSize) / viewScaleInfo.scale,
w: w,
angle
},
{ groupQueue: [...groupQueue] }
@ -97,92 +102,94 @@ export function calcElementSizeController(
const bottomMiddleVertexes = calcElementVertexes(bottomMiddleSize);
const leftMiddleVertexes = calcElementVertexes(leftMiddleSize);
// const originRotateCenter: PointSize = {
// x: x + w / 2,
// y: y - ctrlSize * 4
// };
// const rotateCenter = topCenter;
const rotateCenter = getCenterFromTwoPoints(rotateElemVertexes[0], rotateElemVertexes[1]);
const rotateSize = createControllerElementSizeFromCenter(rotateCenter, { size: ctrlSize, angle: totalAngle });
// TODO
const tempRotateSizeRepairRatio = 1.1;
const rotateSize = createControllerElementSizeFromCenter(rotateCenter, {
size: (rotateControllerSize * tempRotateSizeRepairRatio) / viewScaleInfo.scale,
angle: totalAngle
});
const rotateVertexes = calcElementVertexes(rotateSize);
// const rotateCtrlElem: ElementSize = {
// x: originRotateCenter.x - ctrlSize / 2,
// y: originRotateCenter.x - ctrlSize / 2,
// w: ctrlSize,
// h: ctrlSize,
// angle
// };
// const rotateVertexes = calcElementVertexesInGroup(rotateCtrlElem, { groupQueue }) as ViewRectVertexes;
// const rotateCenter = getCenterFromTwoPoints(rotateVertexes[0], rotateVertexes[2]);
const sizeController: ElementSizeController = {
elementWrapper: vertexes,
left: {
type: 'left',
vertexes: leftVertexes,
center: leftCenter
center: leftCenter,
size: ctrlSize
},
right: {
type: 'right',
vertexes: rightVertexes,
center: rightCenter
center: rightCenter,
size: ctrlSize
},
top: {
type: 'top',
vertexes: topVertexes,
center: topCenter
center: topCenter,
size: ctrlSize
},
bottom: {
type: 'bottom',
vertexes: bottomVertexes,
center: bottomCenter
center: bottomCenter,
size: ctrlSize
},
topLeft: {
type: 'top-left',
vertexes: topLeftVertexes,
center: topLeftCenter
center: topLeftCenter,
size: ctrlSize
},
topRight: {
type: 'top-right',
vertexes: topRightVertexes,
center: topRightCenter
center: topRightCenter,
size: ctrlSize
},
bottomLeft: {
type: 'bottom-left',
vertexes: bottomLeftVertexes,
center: bottomLeftCenter
center: bottomLeftCenter,
size: ctrlSize
},
bottomRight: {
type: 'bottom-right',
vertexes: bottomRightVertexes,
center: bottomRightCenter
center: bottomRightCenter,
size: ctrlSize
},
leftMiddle: {
type: 'left-middle',
vertexes: leftMiddleVertexes,
center: leftCenter
center: leftCenter,
size: ctrlSize
},
rightMiddle: {
type: 'right-middle',
vertexes: rightMiddleVertexes,
center: rightCenter
center: rightCenter,
size: ctrlSize
},
topMiddle: {
type: 'top-middle',
vertexes: topMiddleVertexes,
center: topCenter
center: topCenter,
size: ctrlSize
},
bottomMiddle: {
type: 'bottom-middle',
vertexes: bottomMiddleVertexes,
center: bottomCenter
center: bottomCenter,
size: ctrlSize
},
rotate: {
type: 'rotate',
vertexes: rotateVertexes,
center: rotateCenter
center: rotateCenter,
size: rotateControllerSize
}
};
return sizeController;
@ -241,62 +248,74 @@ export function calcLayoutSizeController(
left: {
type: 'left',
vertexes: leftVertexes,
center: leftCenter
center: leftCenter,
size: ctrlSize
},
right: {
type: 'right',
vertexes: rightVertexes,
center: rightCenter
center: rightCenter,
size: ctrlSize
},
top: {
type: 'top',
vertexes: topVertexes,
center: topCenter
center: topCenter,
size: ctrlSize
},
bottom: {
type: 'bottom',
vertexes: bottomVertexes,
center: bottomCenter
center: bottomCenter,
size: ctrlSize
},
topLeft: {
type: 'top-left',
vertexes: topLeftVertexes,
center: topLeftCenter
center: topLeftCenter,
size: ctrlSize
},
topRight: {
type: 'top-right',
vertexes: topRightVertexes,
center: topRightCenter
center: topRightCenter,
size: ctrlSize
},
bottomLeft: {
type: 'bottom-left',
vertexes: bottomLeftVertexes,
center: bottomLeftCenter
center: bottomLeftCenter,
size: ctrlSize
},
bottomRight: {
type: 'bottom-right',
vertexes: bottomRightVertexes,
center: bottomRightCenter
center: bottomRightCenter,
size: ctrlSize
},
leftMiddle: {
type: 'left-middle',
vertexes: leftMiddleVertexes,
center: leftCenter
center: leftCenter,
size: ctrlSize
},
rightMiddle: {
type: 'right-middle',
vertexes: rightMiddleVertexes,
center: rightCenter
center: rightCenter,
size: ctrlSize
},
topMiddle: {
type: 'top-middle',
vertexes: topMiddleVertexes,
center: topCenter
center: topCenter,
size: ctrlSize
},
bottomMiddle: {
type: 'bottom-middle',
vertexes: bottomMiddleVertexes,
center: bottomCenter
center: bottomCenter,
size: ctrlSize
}
};
return sizeController;

View file

@ -72,16 +72,18 @@ export function calcElementCenterFromVertexes(ves: ViewRectVertexes): PointSize
}
export function calcRadian(center: PointSize, start: PointSize, end: PointSize): number {
const startAngle = calcLineRadian(center, start);
const endAngle = calcLineRadian(center, end);
if (endAngle !== null && startAngle !== null) {
if (startAngle > (Math.PI * 3) / 2 && endAngle < Math.PI / 2) {
return endAngle + (Math.PI * 2 - startAngle);
} else if (endAngle > (Math.PI * 3) / 2 && startAngle < Math.PI / 2) {
return startAngle + (Math.PI * 2 - endAngle);
} else {
return endAngle - startAngle;
}
const startRadian = calcLineRadian(center, start);
const endRadian = calcLineRadian(center, end);
if (endRadian !== null && startRadian !== null) {
// if (startRadian > (Math.PI * 3) / 2 && endRadian < Math.PI / 2) {
// return endRadian + (Math.PI * 2 - startRadian);
// } else if (endRadian > (Math.PI * 3) / 2 && startRadian < Math.PI / 2) {
// return startRadian + (Math.PI * 2 - endRadian);
// } else {
// return endRadian - startRadian;
// }
return endRadian - startRadian;
} else {
return 0;
}
@ -221,12 +223,14 @@ export function rotateVertexes(center: PointSize, ves: ViewRectVertexes, radian:
// [0, 360], eg. 370 to 10, -10 to 350
export function limitAngle(angle: number): number {
if (!(angle > 0 || angle < 0) || angle === 0) {
if (!(angle > 0 || angle < 0) || angle === 0 || angle === 360) {
return 0;
}
let num = angle % 360;
if (num < 0) {
num += 360;
} else if (angle === 360) {
num = 0;
}
return num;
}