feat: optimize middlewares and renderer

This commit is contained in:
chenshenhai 2024-06-22 08:52:39 +08:00
parent 289657030e
commit 25a772420e
14 changed files with 437 additions and 275 deletions

View file

@ -1,8 +1,12 @@
export const key = 'LAYOUT_SELECT';
// export const keyHoverElement = Symbol(`${key}_hoverElementSize`);
export const keyLayoutActionType = Symbol(`${key}_layoutActionType`); // 'hover' | 'resize' | null = null;
export const keyLayoutActionType = Symbol(`${key}_layoutActionType`); // 'resize' | null = null;
export const keyLayoutControlType = Symbol(`${key}_layoutControlType`); // ControlType | null;
export const keyLayoutController = Symbol(`${key}_layoutController`); // ElementSizeController | null = null;
export const keyLayoutIsHover = Symbol(`${key}_layoutIsHover`); // boolean | null
export const keyLayoutIsSelected = Symbol(`${key}_layoutIsSelected`); // boolean | null
export const selectColor = '#1973ba';
// export const selectColor = '#1973ba';
export const selectColor = '#b331c9';
export const disableColor = '#5b5959b5';
export const controllerSize = 10;

View file

@ -1,60 +1,48 @@
import type { BoardMiddleware, ElementSize, Point } from '@idraw/types';
import { calcLayoutSizeController, isViewPointInVertexes, getViewScaleInfoFromSnapshot } from '@idraw/util';
import { calcLayoutSizeController, isViewPointInVertexes, getViewScaleInfoFromSnapshot, isViewPointInElementSize, calcViewElementSize } from '@idraw/util';
import type { LayoutSelectorSharedStorage, ControlType } from './types';
import { keyLayoutActionType, keyLayoutController, keyLayoutControlType } from './config';
import { keyActionType as keyElementActionType, middlewareEventSelectClear } from '../selector';
import { drawLayoutController } from './util';
import { keyLayoutActionType, keyLayoutController, keyLayoutControlType, keyLayoutIsHover, keyLayoutIsSelected, controllerSize } from './config';
import { keyActionType as keyElementActionType, keyHoverElement, middlewareEventSelectClear } from '../selector';
import { drawLayoutController, drawLayoutHover } from './util';
import { eventChange } from '../../config';
export { keyLayoutIsSelected };
export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStorage> = (opts) => {
const { sharer, boardContent, calculator, viewer, eventHub } = opts;
const { overlayContext } = boardContent;
let prevPoint: Point | null = null;
let prevIsHover: boolean | null = null;
let prevIsSelected: boolean | null = null;
let isBusy: boolean | null = null;
const clear = () => {
prevPoint = null;
sharer.setSharedStorage(keyLayoutActionType, null);
sharer.setSharedStorage(keyLayoutControlType, null);
sharer.setSharedStorage(keyLayoutController, null);
sharer.setSharedStorage(keyLayoutIsHover, null);
sharer.setSharedStorage(keyLayoutIsSelected, null);
prevIsHover = null;
prevIsSelected = null;
isBusy = null;
};
const isInElementAction = () => {
const elementType = sharer.getSharedStorage(keyElementActionType);
if (elementType) {
const isInElementHover = () => {
const hoverElement = sharer.getSharedStorage(keyHoverElement);
if (hoverElement) {
clear();
return true;
}
return false;
};
const isDisbaledControl = (controlType: ControlType) => {
const data = sharer.getActiveStorage('data');
if (data?.layout?.operations) {
const operations = data.layout.operations;
if (controlType === 'left' && operations.disabledLeft === true) {
return true;
}
if (controlType === 'top' && operations.disabledTop === true) {
return true;
}
if (controlType === 'right' && operations.disabledRight === true) {
return true;
}
if (controlType === 'bottom' && operations.disabledBottom === true) {
return true;
}
if (controlType === 'top-left' && operations.disabledTopLeft === true) {
return true;
}
if (controlType === 'top-right' && operations.disabledTopRight === true) {
return true;
}
if (controlType === 'bottom-left' && operations.disabledBottomLeft === true) {
return true;
}
if (controlType === 'bottom-right' && operations.disabledBottomRight === true) {
return true;
}
const isInElementAction = () => {
const elementActionType = sharer.getSharedStorage(keyElementActionType);
if (elementActionType && elementActionType !== 'area') {
clear();
return true;
}
return false;
};
@ -68,6 +56,25 @@ export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStora
return null;
};
const isInLayout = (p: Point) => {
const size = getLayoutSize();
if (size) {
const { x, y, w, h } = size;
const viewScaleInfo = sharer.getActiveViewScaleInfo();
const viewSize = calcViewElementSize(
{
x: x - controllerSize / 2,
y: y - controllerSize / 2,
w: w + controllerSize,
h: h + controllerSize
},
{ viewScaleInfo }
);
return isViewPointInElementSize(p, viewSize);
}
return false;
};
const resetController = () => {
const viewScaleInfo = sharer.getActiveViewScaleInfo();
const size: ElementSize | null = getLayoutSize();
@ -105,6 +112,17 @@ export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStora
return null;
};
const updateCursor = (controlType?: ControlType | null) => {
if (isBusy === true) {
return;
}
eventHub.trigger('cursor', {
type: controlType ? `resize-${controlType}` : controlType,
groupQueue: [],
element: getLayoutSize()
});
};
return {
name: '@middleware/layout-selector',
use: () => {
@ -112,100 +130,160 @@ export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStora
resetController();
},
hover: (e) => {
if (isBusy === true) {
return;
}
if (isInElementAction()) {
return;
}
const prevLayoutActionType = sharer.getSharedStorage(keyLayoutActionType);
const data = sharer.getActiveStorage('data');
if (data?.layout && prevLayoutActionType !== 'resize') {
resetController();
const layoutControlType = resetControlType(e);
if (layoutControlType) {
sharer.setSharedStorage(keyLayoutActionType, 'hover');
if (!isDisbaledControl(layoutControlType)) {
eventHub.trigger('cursor', {
type: `resize-${layoutControlType}`,
groupQueue: [],
element: getLayoutSize()
});
}
if (isInElementHover()) {
return;
}
if (isInLayout(e.point)) {
sharer.setSharedStorage(keyLayoutIsHover, true);
} else {
sharer.setSharedStorage(keyLayoutIsHover, false);
if (prevIsHover === true) {
viewer.drawFrame();
} else {
sharer.setSharedStorage(keyLayoutActionType, null);
prevIsHover = false;
}
}
if (['hover', 'resize'].includes(sharer.getSharedStorage(keyLayoutActionType) as string)) {
return false;
if (sharer.getSharedStorage(keyLayoutIsSelected) === true) {
const prevLayoutActionType = sharer.getSharedStorage(keyLayoutActionType);
const data = sharer.getActiveStorage('data');
if (data?.layout) {
if (prevLayoutActionType !== 'resize') {
resetController();
const layoutControlType = resetControlType(e);
if (layoutControlType) {
updateCursor(layoutControlType);
} else {
updateCursor();
sharer.setSharedStorage(keyLayoutActionType, null);
}
} else {
const layoutControlType = resetControlType(e);
updateCursor(layoutControlType);
}
}
return;
}
if (prevLayoutActionType === 'hover' && !sharer.getSharedStorage(keyLayoutActionType)) {
if (sharer.getSharedStorage(keyLayoutIsHover) && !prevIsHover) {
viewer.drawFrame();
}
prevIsHover = sharer.getSharedStorage(keyLayoutIsHover);
},
pointStart: (e) => {
if (isInElementAction()) {
return;
}
if (isInLayout(e.point)) {
sharer.setSharedStorage(keyLayoutIsSelected, true);
} else {
if (prevIsSelected === true) {
clear();
viewer.drawFrame();
}
sharer.setSharedStorage(keyLayoutIsSelected, false);
}
resetController();
const layoutControlType = resetControlType(e);
prevPoint = e.point;
if (layoutControlType) {
if (isDisbaledControl(layoutControlType)) {
return;
}
sharer.setSharedStorage(keyLayoutActionType, 'resize');
return false;
}
const layoutActionType = sharer.getSharedStorage(keyLayoutActionType);
if (['hover', 'resize'].includes(layoutActionType as string)) {
return false;
if (sharer.getSharedStorage(keyLayoutIsSelected) === true && !prevIsSelected) {
viewer.drawFrame();
}
prevIsSelected = sharer.getSharedStorage(keyLayoutIsSelected);
},
pointMove: (e) => {
if (isInElementAction()) {
return;
if (!sharer.getSharedStorage(keyLayoutIsSelected)) {
if (isInElementAction()) {
return;
}
}
const layoutActionType = sharer.getSharedStorage(keyLayoutActionType);
const layoutControlType = sharer.getSharedStorage(keyLayoutControlType);
const data = sharer.getActiveStorage('data');
if (layoutControlType && isDisbaledControl(layoutControlType)) {
return;
}
if (layoutActionType === 'resize' && layoutControlType && data?.layout) {
if (prevPoint) {
isBusy = true;
const scale = sharer.getActiveStorage('scale');
const moveX = (e.point.x - prevPoint.x) / scale;
const moveY = (e.point.y - prevPoint.y) / scale;
const { x, y, w, h } = data.layout;
const viewMoveX = e.point.x - prevPoint.x;
const viewMoveY = e.point.y - prevPoint.y;
const moveX = viewMoveX / scale;
const moveY = viewMoveY / scale;
const { x, y, w, h, operations = {} } = data.layout;
const { position = 'absolute' } = operations;
if (layoutControlType === 'top') {
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.h = calculator.toGridNum(h - moveY);
if (position === 'relative') {
data.layout.h = calculator.toGridNum(h - moveY);
viewer.scroll({ moveY: viewMoveY });
} else {
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.h = calculator.toGridNum(h - moveY);
}
} else if (layoutControlType === 'right') {
data.layout.w = calculator.toGridNum(w + moveX);
} else if (layoutControlType === 'bottom') {
data.layout.h = calculator.toGridNum(h + moveY);
} else if (layoutControlType === 'left') {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.w = calculator.toGridNum(w - moveX);
if (position === 'relative') {
data.layout.w = calculator.toGridNum(w - moveX);
viewer.scroll({ moveX: viewMoveX });
} else {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.w = calculator.toGridNum(w - moveX);
}
} else if (layoutControlType === 'top-left') {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.w = calculator.toGridNum(w - moveX);
data.layout.h = calculator.toGridNum(h - moveY);
if (position === 'relative') {
data.layout.w = calculator.toGridNum(w - moveX);
data.layout.h = calculator.toGridNum(h - moveY);
viewer.scroll({ moveX: viewMoveX, moveY: viewMoveY });
} else {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.w = calculator.toGridNum(w - moveX);
data.layout.h = calculator.toGridNum(h - moveY);
}
} else if (layoutControlType === 'top-right') {
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.w = calculator.toGridNum(w + moveX);
data.layout.h = calculator.toGridNum(h - moveY);
if (position === 'relative') {
viewer.scroll({
moveY: viewMoveY
});
data.layout.w = calculator.toGridNum(w + moveX);
data.layout.h = calculator.toGridNum(h - moveY);
} else {
data.layout.y = calculator.toGridNum(y + moveY);
data.layout.w = calculator.toGridNum(w + moveX);
data.layout.h = calculator.toGridNum(h - moveY);
}
} else if (layoutControlType === 'bottom-right') {
data.layout.w = calculator.toGridNum(w + moveX);
data.layout.h = calculator.toGridNum(h + moveY);
} else if (layoutControlType === 'bottom-left') {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.w = calculator.toGridNum(w - moveX);
data.layout.h = calculator.toGridNum(h + moveY);
if (position === 'relative') {
viewer.scroll({
moveX: viewMoveX
});
data.layout.w = calculator.toGridNum(w - moveX);
data.layout.h = calculator.toGridNum(h + moveY);
} else {
data.layout.x = calculator.toGridNum(x + moveX);
data.layout.w = calculator.toGridNum(w - moveX);
data.layout.h = calculator.toGridNum(h + moveY);
}
}
}
prevPoint = e.point;
@ -215,35 +293,44 @@ export const MiddlewareLayoutSelector: BoardMiddleware<LayoutSelectorSharedStora
return false;
}
if (['hover', 'resize'].includes(layoutActionType as string)) {
if (['resize'].includes(layoutActionType as string)) {
return false;
}
},
pointEnd: () => {
isBusy = false;
const layoutActionType = sharer.getSharedStorage(keyLayoutActionType);
const layoutControlType = sharer.getSharedStorage(keyLayoutControlType);
const data = sharer.getActiveStorage('data');
if (data && layoutActionType === 'resize' && layoutControlType && !isDisbaledControl(layoutControlType)) {
if (data && layoutActionType === 'resize' && layoutControlType) {
eventHub.trigger(eventChange, {
type: 'changeLayout',
data
});
}
clear();
},
beforeDrawFrame: ({ snapshot }) => {
if (isInElementAction()) {
return;
}
const { sharedStore, activeStore } = snapshot;
const layoutActionType = sharedStore[keyLayoutActionType];
const layoutControlType = sharedStore[keyLayoutControlType];
const layoutIsHover = sharedStore[keyLayoutIsHover];
const layoutIsSelected = sharedStore[keyLayoutIsSelected];
if (activeStore.data?.layout && layoutActionType && layoutControlType) {
if (['hover', 'resize'].includes(layoutActionType)) {
const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot);
const { x, y, w, h } = activeStore.data.layout;
const size = { x, y, w, h };
const controller = calcLayoutSizeController(size, { viewScaleInfo, controllerSize: 10 });
if (activeStore.data?.layout) {
const { x, y, w, h } = activeStore.data.layout;
const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot);
const size = { x, y, w, h };
const controller = calcLayoutSizeController(size, { viewScaleInfo, controllerSize });
if (layoutIsHover === true) {
const viewSize = calcViewElementSize(size, { viewScaleInfo });
drawLayoutHover(overlayContext, { layoutSize: viewSize });
}
if ((layoutActionType && ['resize'].includes(layoutActionType)) || layoutIsSelected === true) {
drawLayoutController(overlayContext, { controller, operations: activeStore.data.layout.operations || {} });
}
}

View file

@ -1,9 +1,9 @@
import type { LayoutSizeController } from '@idraw/types';
import { keyLayoutActionType, keyLayoutControlType, keyLayoutController } from './config';
import { keyActionType as keyElementActionType } from '../selector';
import type { LayoutSizeController, Element } from '@idraw/types';
import { keyLayoutActionType, keyLayoutControlType, keyLayoutController, keyLayoutIsHover, keyLayoutIsSelected } from './config';
import { keyActionType as keyElementActionType, keyHoverElement } from '../selector';
import type { ActionType as ElementActionType } from '../selector';
export type ActionType = 'hover' | 'resize' | null;
export type ActionType = 'resize' | null;
export type ControlType = 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
@ -12,4 +12,7 @@ export type LayoutSelectorSharedStorage = {
[keyLayoutControlType]: ControlType | null;
[keyLayoutController]: LayoutSizeController | null;
[keyElementActionType]: ElementActionType | null;
[keyHoverElement]: Element | null;
[keyLayoutIsHover]: boolean | null;
[keyLayoutIsSelected]: boolean | null;
};

View file

@ -1,4 +1,4 @@
import type { ViewContext2D, LayoutSizeController, DataLayout, ViewRectVertexes, PointSize } from '@idraw/types';
import type { ViewContext2D, LayoutSizeController, DataLayout, ViewRectVertexes, PointSize, ElementSize } from '@idraw/types';
import { selectColor, disableColor } from './config';
function drawControllerBox(ctx: ViewContext2D, boxVertexes: ViewRectVertexes) {
@ -109,3 +109,24 @@ export function drawLayoutController(
drawControllerBox(ctx, bottomLeft.vertexes);
}
}
export function drawLayoutHover(
ctx: ViewContext2D,
opts: {
layoutSize: ElementSize;
}
) {
const { layoutSize } = opts;
const { x, y, w, h } = layoutSize;
ctx.setLineDash([]);
ctx.strokeStyle = selectColor;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + w, y);
ctx.lineTo(x + w, y + h);
ctx.lineTo(x, y + h);
ctx.lineTo(x, y);
ctx.closePath();
ctx.stroke();
}

View file

@ -1,6 +1,8 @@
export const key = 'SCROLL';
export const keyXThumbRect = Symbol(`${key}_xThumbRect`);
export const keyYThumbRect = Symbol(`${key}_yThumbRect`);
export const keyHoverXThumbRect = Symbol(`${key}_hoverXThumbRect`);
export const keyHoverYThumbRect = Symbol(`${key}_hoverYThumbRect`);
export const keyPrevPoint = Symbol(`${key}_prevPoint`);
export const keyActivePoint = Symbol(`${key}_activePoint`);
export const keyActiveThumbType = Symbol(`${key}_activeThumbType`);

View file

@ -1,20 +1,24 @@
import type { Point, BoardMiddleware, PointWatcherEvent, BoardWatherWheelEvent } from '@idraw/types';
import { drawScroller, isPointInScrollThumb } from './util';
// import type { ScrollbarThumbType } from './util';
import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType } from './config';
import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType, keyHoverXThumbRect, keyHoverYThumbRect } from './config';
import type { DeepScrollerSharedStorage } from './types';
export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage> = (opts) => {
const { viewer, boardContent, sharer } = opts;
const { viewer, boardContent, sharer, eventHub } = opts;
const { overlayContext } = boardContent;
sharer.setSharedStorage(keyXThumbRect, null); // null | ElementSize
sharer.setSharedStorage(keyYThumbRect, null); // null | ElementSize
let isBusy: boolean = false;
// viewer.drawFrame();
const clear = () => {
sharer.setSharedStorage(keyPrevPoint, null); // null | Point;
sharer.setSharedStorage(keyActivePoint, null); // null | Point;
sharer.setSharedStorage(keyActiveThumbType, null); // null | 'X' | 'Y'
sharer.setSharedStorage(keyHoverXThumbRect, null); // null | boolean
sharer.setSharedStorage(keyHoverYThumbRect, null); // null | boolean
isBusy = false;
};
clear();
@ -64,10 +68,33 @@ export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage> = (o
viewer.drawFrame();
},
hover: (e: PointWatcherEvent) => {
if (isBusy === true) {
return false;
}
const { point } = e;
const thumbType = getThumbType(point);
if (thumbType === 'X' || thumbType === 'Y') {
if (thumbType === 'X') {
sharer.setSharedStorage(keyHoverXThumbRect, true);
sharer.setSharedStorage(keyHoverYThumbRect, false);
} else {
sharer.setSharedStorage(keyHoverXThumbRect, false);
sharer.setSharedStorage(keyHoverYThumbRect, true);
}
eventHub.trigger('cursor', { type: 'default' });
return false;
}
sharer.setSharedStorage(keyHoverXThumbRect, false);
sharer.setSharedStorage(keyHoverYThumbRect, false);
},
pointStart: (e: PointWatcherEvent) => {
const { point } = e;
const thumbType = getThumbType(point);
if (thumbType === 'X' || thumbType === 'Y') {
isBusy = true;
sharer.setSharedStorage(keyActiveThumbType, thumbType);
sharer.setSharedStorage(keyPrevPoint, point);
return false;
@ -88,6 +115,7 @@ export const MiddlewareScroller: BoardMiddleware<DeepScrollerSharedStorage> = (o
}
},
pointEnd: () => {
isBusy = false;
const activeThumbType = sharer.getSharedStorage(keyActiveThumbType);
clear();
if (activeThumbType === 'X' || activeThumbType === 'Y') {

View file

@ -1,9 +1,11 @@
import type { Point, ElementSize } from '@idraw/types';
import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType } from './config';
import { keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType, keyHoverXThumbRect, keyHoverYThumbRect } from './config';
export type DeepScrollerSharedStorage = {
[keyXThumbRect]: null | ElementSize;
[keyYThumbRect]: null | ElementSize;
[keyHoverXThumbRect]: boolean | null;
[keyHoverYThumbRect]: boolean | null;
[keyPrevPoint]: null | Point;
[keyActivePoint]: null | Point;
[keyActiveThumbType]: null | 'X' | 'Y';

View file

@ -1,6 +1,6 @@
import type { Point, BoardViewerFrameSnapshot, ViewScaleInfo, ViewSizeInfo, ViewContext2D, ElementSize } from '@idraw/types';
import { getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idraw/util';
import { keyActivePoint, keyActiveThumbType, keyPrevPoint, keyXThumbRect, keyYThumbRect } from './config';
import { keyActivePoint, keyActiveThumbType, keyPrevPoint, keyXThumbRect, keyYThumbRect, keyHoverXThumbRect, keyHoverYThumbRect } from './config';
const minScrollerWidth = 12;
const scrollerLineWidth = 16;
@ -10,7 +10,8 @@ export type ScrollbarThumbType = 'X' | 'Y';
const scrollConfig = {
width: minScrollerWidth,
thumbColor: '#000000AA',
thumbColor: '#0000008A',
thumbHoverColor: '#000000EE',
scrollBarColor: '#FFFFFF60',
showScrollBar: false
};
@ -51,6 +52,8 @@ interface ScrollInfo {
activeThumbType: ScrollbarThumbType | null;
xThumbRect: ElementSize | null;
yThumbRect: ElementSize | null;
hoverXThumb: boolean | null;
hoverYThumb: boolean | null;
}
function getScrollInfoFromSnapshot(snapshot: BoardViewerFrameSnapshot): ScrollInfo {
const { sharedStore } = snapshot;
@ -59,12 +62,15 @@ function getScrollInfoFromSnapshot(snapshot: BoardViewerFrameSnapshot): ScrollIn
prevPoint: sharedStore[keyPrevPoint] || null,
activeThumbType: sharedStore[keyActiveThumbType] || null,
xThumbRect: sharedStore[keyXThumbRect] || null,
yThumbRect: sharedStore[keyYThumbRect] || null
yThumbRect: sharedStore[keyYThumbRect] || null,
hoverXThumb: sharedStore[keyHoverXThumbRect],
hoverYThumb: sharedStore[keyHoverYThumbRect]
};
return info;
}
function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo) {
function calcScrollerInfo(opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; hoverXThumb: boolean | null; hoverYThumb: boolean | null }) {
const { viewScaleInfo, viewSizeInfo, hoverXThumb, hoverYThumb } = opts;
const { width, height } = viewSizeInfo;
const { offsetTop, offsetBottom, offsetLeft, offsetRight } = viewScaleInfo;
const sliderMinSize = scrollerLineWidth * 2.5;
@ -125,7 +131,8 @@ function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeIn
ySize,
translateY,
translateX,
thumbColor: scrollConfig.thumbColor,
xThumbColor: hoverXThumb ? scrollConfig.thumbHoverColor : scrollConfig.thumbColor,
yThumbColor: hoverYThumb ? scrollConfig.thumbHoverColor : scrollConfig.thumbColor,
scrollBarColor: scrollConfig.scrollBarColor,
xThumbRect,
yThumbRect
@ -197,9 +204,9 @@ function drawScrollerThumb(
function drawScrollerInfo(overlayContext: ViewContext2D, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; scrollInfo: ScrollInfo }) {
const ctx = overlayContext;
const { viewScaleInfo, viewSizeInfo, scrollInfo } = opts;
const { activeThumbType, prevPoint, activePoint } = scrollInfo;
const { activeThumbType, prevPoint, activePoint, hoverXThumb, hoverYThumb } = scrollInfo;
const { width, height } = viewSizeInfo;
const wrapper = calcScrollerInfo(viewScaleInfo, viewSizeInfo);
const wrapper = calcScrollerInfo({ viewScaleInfo, viewSizeInfo, hoverXThumb, hoverYThumb });
let xThumbRect: ElementSize = { ...wrapper.xThumbRect };
let yThumbRect: ElementSize = { ...wrapper.yThumbRect };
@ -225,7 +232,7 @@ function drawScrollerInfo(overlayContext: ViewContext2D, opts: { viewScaleInfo:
axis: 'X',
...xThumbRect,
r: wrapper.lineSize / 2,
color: wrapper.thumbColor
color: wrapper.xThumbColor
});
// y-bar
@ -240,7 +247,7 @@ function drawScrollerInfo(overlayContext: ViewContext2D, opts: { viewScaleInfo:
axis: 'Y',
...yThumbRect,
r: wrapper.lineSize / 2,
color: wrapper.thumbColor
color: wrapper.yThumbColor
});
ctx.globalAlpha = 1;

View file

@ -75,8 +75,9 @@ import {
import { calcReferenceInfo } from './reference';
import { middlewareEventTextEdit } from '../text-editor';
import { eventChange } from '../../config';
import { keyLayoutIsSelected } from '../layout-selector';
export { keySelectedElementList, keyActionType, keyResizeType, keyGroupQueue };
export { keySelectedElementList, keyHoverElement, keyActionType, keyResizeType, keyGroupQueue };
export type { DeepSelectorSharedStorage, ActionType };
export { middlewareEventSelect, middlewareEventSelectClear, middlewareEventSelectInGroup, middlewareEventSnapToGrid };
@ -237,10 +238,15 @@ export const MiddlewareSelector: BoardMiddleware<DeepSelectorSharedStorage, Core
},
hover: (e: PointWatcherEvent) => {
const layoutIsSelected = sharer.getSharedStorage(keyLayoutIsSelected);
const resizeType = sharer.getSharedStorage(keyResizeType);
const actionType = sharer.getSharedStorage(keyActionType);
const groupQueue = sharer.getSharedStorage(keyGroupQueue);
const triggerCursor = (target: PointTarget) => {
if (layoutIsSelected === true) {
return;
}
const cursor: string | null = target.type;
if (inBusyMode === null) {
eventHub.trigger('cursor', {

View file

@ -40,6 +40,7 @@ import {
keyDebugStartHorizontal,
keyDebugStartVertical
} from './config';
import { keyLayoutIsSelected } from '../layout-selector';
export {
Data,
@ -112,4 +113,7 @@ export type DeepSelectorSharedStorage = {
[keyDebugEndVertical]: PointSize | null;
[keyDebugStartHorizontal]: PointSize | null;
[keyDebugStartVertical]: PointSize | null;
// layout-selector
[keyLayoutIsSelected]: boolean | null;
};

View file

@ -174,9 +174,8 @@ export const MiddlewareTextEditor: BoardMiddleware<ExtendEventMap, CoreEventMap
textarea.style.resize = 'none';
textarea.style.overflow = 'hidden';
textarea.style.wordBreak = 'break-all';
// textarea.style.background = '#FFFFFF';
textarea.style.background = 'transparent';
// textarea.style.color = '#333333';
textarea.style.borderRadius = `${(typeof detail.borderRadius === 'number' ? detail.borderRadius : 0) * scale}px`;
textarea.style.background = `${detail.background || 'transparent'}`;
textarea.style.color = `${detail.color || '#333333'}`;
textarea.style.fontSize = `${detail.fontSize * scale}px`;
textarea.style.lineHeight = `${(detail.lineHeight || detail.fontSize) * scale}px`;

View file

@ -19,7 +19,7 @@ export function drawBox(
originElem: Element;
calcElemSize: ElementSize;
pattern?: string | CanvasPattern | null;
renderContent: () => void;
renderContent?: () => void;
viewScaleInfo: ViewScaleInfo;
viewSizeInfo: ViewSizeInfo;
parentOpacity: number;

View file

@ -1,7 +1,7 @@
import type { Element, RendererDrawElementOptions, ViewContext2D } from '@idraw/types';
import { rotateElement, calcViewElementSize, enhanceFontFamliy } from '@idraw/util';
import { is, isColorStr, getDefaultElementDetailConfig } from '@idraw/util';
import { drawBox } from './box';
import { drawBox, drawBoxShadow } from './box';
const detailConfig = getDefaultElementDetailConfig();
@ -20,172 +20,178 @@ export function drawText(ctx: ViewContext2D, elem: Element<'text'>, opts: Render
const { x, y, w, h, angle } = calcViewElementSize(elem, { viewScaleInfo }) || elem;
const viewElem = { ...elem, ...{ x, y, w, h, angle } };
rotateElement(ctx, { x, y, w, h, angle }, () => {
drawBox(ctx, viewElem, {
originElem: elem,
calcElemSize: { x, y, w, h, angle },
drawBoxShadow(ctx, viewElem, {
viewScaleInfo,
viewSizeInfo,
parentOpacity,
renderContent: () => {
const detail: Element<'text'>['detail'] = {
...detailConfig,
...elem.detail
};
const originFontSize = detail.fontSize || detailConfig.fontSize;
const fontSize = originFontSize * viewScaleInfo.scale;
if (fontSize < 2) {
return;
}
const originLineHeight = detail.lineHeight || originFontSize;
const lineHeight = originLineHeight * viewScaleInfo.scale;
ctx.fillStyle = elem.detail.color || detailConfig.color;
ctx.textBaseline = 'top';
ctx.$setFont({
fontWeight: detail.fontWeight,
fontSize: fontSize,
fontFamily: enhanceFontFamliy(detail.fontFamily)
drawBox(ctx, viewElem, {
originElem: elem,
calcElemSize: { x, y, w, h, angle },
viewScaleInfo,
viewSizeInfo,
parentOpacity
});
let detailText = detail.text.replace(/\r\n/gi, '\n');
if (detail.textTransform === 'lowercase') {
detailText = detailText.toLowerCase();
} else if (detail.textTransform === 'uppercase') {
detailText = detailText.toUpperCase();
}
}
});
{
const detail: Element<'text'>['detail'] = {
...detailConfig,
...elem.detail
};
const originFontSize = detail.fontSize || detailConfig.fontSize;
const fontSize = originFontSize * viewScaleInfo.scale;
const fontHeight = lineHeight;
const detailTextList = detailText.split('\n');
const lines: { text: string; width: number }[] = [];
if (fontSize < 2) {
return;
}
let lineNum = 0;
detailTextList.forEach((itemText: string, idx: number) => {
if (detail.minInlineSize === 'maxContent') {
lines.push({
text: itemText,
width: ctx.$undoPixelRatio(ctx.measureText(itemText).width)
const originLineHeight = detail.lineHeight || originFontSize;
const lineHeight = originLineHeight * viewScaleInfo.scale;
ctx.fillStyle = elem.detail.color || detailConfig.color;
ctx.textBaseline = 'top';
ctx.$setFont({
fontWeight: detail.fontWeight,
fontSize: fontSize,
fontFamily: enhanceFontFamliy(detail.fontFamily)
});
let detailText = detail.text.replace(/\r\n/gi, '\n');
if (detail.textTransform === 'lowercase') {
detailText = detailText.toLowerCase();
} else if (detail.textTransform === 'uppercase') {
detailText = detailText.toUpperCase();
}
const fontHeight = lineHeight;
const detailTextList = detailText.split('\n');
const lines: { text: string; width: number }[] = [];
let lineNum = 0;
detailTextList.forEach((itemText: string, idx: number) => {
if (detail.minInlineSize === 'maxContent') {
lines.push({
text: itemText,
width: ctx.$undoPixelRatio(ctx.measureText(itemText).width)
});
} else {
let lineText = '';
let splitStr = '';
let tempStrList: string[] = itemText.split(splitStr);
if (detail.wordBreak === 'normal') {
const splitStr = ' ';
const wordList = itemText.split(splitStr);
tempStrList = [];
wordList.forEach((word: string, idx: number) => {
tempStrList.push(word);
if (idx < wordList.length - 1) {
tempStrList.push(splitStr);
}
});
} else {
let lineText = '';
let splitStr = '';
let tempStrList: string[] = itemText.split(splitStr);
if (detail.wordBreak === 'normal') {
const splitStr = ' ';
const wordList = itemText.split(splitStr);
tempStrList = [];
wordList.forEach((word: string, idx: number) => {
tempStrList.push(word);
if (idx < wordList.length - 1) {
tempStrList.push(splitStr);
}
});
}
}
if (tempStrList.length === 1 && detail.overflow === 'visible') {
lines.push({
text: tempStrList[0],
width: ctx.$undoPixelRatio(ctx.measureText(tempStrList[0]).width)
});
} else if (tempStrList.length > 0) {
for (let i = 0; i < tempStrList.length; i++) {
if (isTextWidthWithinErrorRange(ctx.$doPixelRatio(w), ctx.measureText(lineText + tempStrList[i]).width, viewScaleInfo.scale)) {
lineText += tempStrList[i] || '';
} else {
if (tempStrList.length === 1 && detail.overflow === 'visible') {
lines.push({
text: tempStrList[0],
width: ctx.$undoPixelRatio(ctx.measureText(tempStrList[0]).width)
});
} else if (tempStrList.length > 0) {
for (let i = 0; i < tempStrList.length; i++) {
if (isTextWidthWithinErrorRange(ctx.$doPixelRatio(w), ctx.measureText(lineText + tempStrList[i]).width, viewScaleInfo.scale)) {
lineText += tempStrList[i] || '';
} else {
lines.push({
text: lineText,
width: ctx.$undoPixelRatio(ctx.measureText(lineText).width)
});
lineText = tempStrList[i] || '';
lineNum++;
}
if ((lineNum + 1) * fontHeight > h && detail.overflow === 'hidden') {
break;
}
if (tempStrList.length - 1 === i) {
if ((lineNum + 1) * fontHeight <= h) {
lines.push({
text: lineText,
width: ctx.$undoPixelRatio(ctx.measureText(lineText).width)
});
lineText = tempStrList[i] || '';
lineNum++;
}
if ((lineNum + 1) * fontHeight > h && detail.overflow === 'hidden') {
if (idx < detailTextList.length - 1) {
lineNum++;
}
break;
}
if (tempStrList.length - 1 === i) {
if ((lineNum + 1) * fontHeight <= h) {
lines.push({
text: lineText,
width: ctx.$undoPixelRatio(ctx.measureText(lineText).width)
});
if (idx < detailTextList.length - 1) {
lineNum++;
}
break;
}
}
}
} else {
lines.push({
text: '',
width: 0
});
}
}
});
let startY = 0;
let eachLineStartY = 0;
if (fontHeight > fontSize) {
eachLineStartY = (fontHeight - fontSize) / 2;
}
if (lines.length * fontHeight < h) {
if (elem.detail.verticalAlign === 'top') {
startY = 0;
} else if (elem.detail.verticalAlign === 'bottom') {
startY += h - lines.length * fontHeight;
} else {
// middle and default
startY += (h - lines.length * fontHeight) / 2;
lines.push({
text: '',
width: 0
});
}
}
});
// draw text lines
{
const _y = y + startY;
if (detail.textShadowColor !== undefined && isColorStr(detail.textShadowColor)) {
ctx.shadowColor = detail.textShadowColor;
}
if (detail.textShadowOffsetX !== undefined && is.number(detail.textShadowOffsetX)) {
ctx.shadowOffsetX = detail.textShadowOffsetX;
}
if (detail.textShadowOffsetY !== undefined && is.number(detail.textShadowOffsetY)) {
ctx.shadowOffsetY = detail.textShadowOffsetY;
}
if (detail.textShadowBlur !== undefined && is.number(detail.textShadowBlur)) {
ctx.shadowBlur = detail.textShadowBlur;
}
lines.forEach((line, i) => {
let _x = x;
if (detail.textAlign === 'center') {
_x = x + (w - line.width) / 2;
} else if (detail.textAlign === 'right') {
_x = x + (w - line.width);
}
ctx.fillText(line.text, _x, _y + fontHeight * i + eachLineStartY);
});
}
// // draw text stroke
// if (isColorStr(detail.strokeColor) && detail.strokeWidth !== undefined && detail.strokeWidth > 0) {
// const _y = y + startY;
// lines.forEach((line, i) => {
// let _x = x;
// if (detail.textAlign === 'center') {
// _x = x + (w - line.width) / 2;
// } else if (detail.textAlign === 'right') {
// _x = x + (w - line.width);
// }
// if (detail.strokeColor !== undefined) {
// ctx.strokeStyle = detail.strokeColor;
// }
// if (detail.strokeWidth !== undefined && detail.strokeWidth > 0) {
// ctx.lineWidth = detail.strokeWidth;
// }
// ctx.strokeText(line.text, _x, _y + fontHeight * i);
// });
// }
let startY = 0;
let eachLineStartY = 0;
if (fontHeight > fontSize) {
eachLineStartY = (fontHeight - fontSize) / 2;
}
});
if (lines.length * fontHeight < h) {
if (elem.detail.verticalAlign === 'top') {
startY = 0;
} else if (elem.detail.verticalAlign === 'bottom') {
startY += h - lines.length * fontHeight;
} else {
// middle and default
startY += (h - lines.length * fontHeight) / 2;
}
}
// draw text lines
{
const _y = y + startY;
if (detail.textShadowColor !== undefined && isColorStr(detail.textShadowColor)) {
ctx.shadowColor = detail.textShadowColor;
}
if (detail.textShadowOffsetX !== undefined && is.number(detail.textShadowOffsetX)) {
ctx.shadowOffsetX = detail.textShadowOffsetX;
}
if (detail.textShadowOffsetY !== undefined && is.number(detail.textShadowOffsetY)) {
ctx.shadowOffsetY = detail.textShadowOffsetY;
}
if (detail.textShadowBlur !== undefined && is.number(detail.textShadowBlur)) {
ctx.shadowBlur = detail.textShadowBlur;
}
lines.forEach((line, i) => {
let _x = x;
if (detail.textAlign === 'center') {
_x = x + (w - line.width) / 2;
} else if (detail.textAlign === 'right') {
_x = x + (w - line.width);
}
ctx.fillText(line.text, _x, _y + fontHeight * i + eachLineStartY);
});
}
// // draw text stroke
// if (isColorStr(detail.strokeColor) && detail.strokeWidth !== undefined && detail.strokeWidth > 0) {
// const _y = y + startY;
// lines.forEach((line, i) => {
// let _x = x;
// if (detail.textAlign === 'center') {
// _x = x + (w - line.width) / 2;
// } else if (detail.textAlign === 'right') {
// _x = x + (w - line.width);
// }
// if (detail.strokeColor !== undefined) {
// ctx.strokeStyle = detail.strokeColor;
// }
// if (detail.strokeWidth !== undefined && detail.strokeWidth > 0) {
// ctx.lineWidth = detail.strokeWidth;
// }
// ctx.strokeText(line.text, _x, _y + fontHeight * i);
// });
// }
}
});
}

View file

@ -6,14 +6,7 @@ export type DataLayout = Pick<ElementSize, 'x' | 'y' | 'w' | 'h'> & {
'background' | 'borderWidth' | 'overflow' | 'borderColor' | 'borderDash' | 'borderRadius' | 'shadowBlur' | 'shadowColor' | 'shadowOffsetX' | 'shadowOffsetY'
>;
operations?: {
disabledLeft?: boolean;
disabledTop?: boolean;
disabledRight?: boolean;
disabledBottom?: boolean;
disabledTopLeft?: boolean;
disabledTopRight?: boolean;
disabledBottomLeft?: boolean;
disabledBottomRight?: boolean;
position?: 'absolute' | 'relative';
};
};