diff --git a/packages/board/src/index.ts b/packages/board/src/index.ts index bc06c332a..f3850364b 100644 --- a/packages/board/src/index.ts +++ b/packages/board/src/index.ts @@ -342,7 +342,8 @@ export class Board { clear() { const { viewContent } = this._opts; - const { helperContext, viewContext, boardContext } = viewContent; + const { underContext, helperContext, viewContext, boardContext } = viewContent; + underContext.clearRect(0, 0, underContext.canvas.width, underContext.canvas.height); helperContext.clearRect(0, 0, helperContext.canvas.width, helperContext.canvas.height); viewContext.clearRect(0, 0, viewContext.canvas.width, viewContext.canvas.height); boardContext.clearRect(0, 0, boardContext.canvas.width, boardContext.canvas.height); diff --git a/packages/board/src/lib/viewer.ts b/packages/board/src/lib/viewer.ts index 34486f25b..63a444af1 100644 --- a/packages/board/src/lib/viewer.ts +++ b/packages/board/src/lib/viewer.ts @@ -44,7 +44,7 @@ export class Viewer extends EventEmitter implements BoardVi if (snapshot) { const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight, width, height, contextHeight, contextWidth, devicePixelRatio } = snapshot.activeStore; - const { viewContext, helperContext, boardContext } = viewContent; + const { underContext, viewContext, helperContext, boardContext } = viewContent; if (snapshot?.activeStore.data) { renderer.drawData(snapshot.activeStore.data, { @@ -66,8 +66,10 @@ export class Viewer extends EventEmitter implements BoardVi } beforeDrawFrame({ snapshot }); boardContext.clearRect(0, 0, width, height); + boardContext.drawImage(underContext.canvas, 0, 0, width, height); boardContext.drawImage(viewContext.canvas, 0, 0, width, height); boardContext.drawImage(helperContext.canvas, 0, 0, width, height); + underContext.clearRect(0, 0, width, height); viewContext.clearRect(0, 0, width, height); helperContext.clearRect(0, 0, width, height); afterDrawFrame({ snapshot }); @@ -154,12 +156,15 @@ export class Viewer extends EventEmitter implements BoardVi const newViewSize = { ...originViewSize, ...viewSize }; const { width, height, devicePixelRatio } = newViewSize; - const { boardContext, helperContext, viewContext } = this._opts.viewContent; + const { underContext, boardContext, helperContext, viewContext } = this._opts.viewContent; boardContext.canvas.width = width * devicePixelRatio; boardContext.canvas.height = height * devicePixelRatio; boardContext.canvas.style.width = `${width}px`; boardContext.canvas.style.height = `${height}px`; + underContext.canvas.width = width * devicePixelRatio; + underContext.canvas.height = height * devicePixelRatio; + helperContext.canvas.width = width * devicePixelRatio; helperContext.canvas.height = height * devicePixelRatio; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3a02606fa..742d0e46a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -7,6 +7,7 @@ import { Cursor } from './lib/cursor'; export { MiddlewareSelector } from './middleware/selector'; export { MiddlewareScroller } from './middleware/scroller'; export { MiddlewareScaler } from './middleware/scaler'; +export { MiddlewareRuler } from './middleware/ruler'; export class Core { private _board: Board; diff --git a/packages/core/src/middleware/ruler/index.ts b/packages/core/src/middleware/ruler/index.ts new file mode 100644 index 000000000..489070fce --- /dev/null +++ b/packages/core/src/middleware/ruler/index.ts @@ -0,0 +1,32 @@ +import type { BoardMiddleware, CoreEvent } from '@idraw/types'; +import { getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idraw/util'; +import { drawRulerBackground, drawXRuler, drawYRuler, calcXRulerScaleList, calcYRulerScaleList, drawUnderGrid } from './util'; + +export const MiddlewareRuler: BoardMiddleware, CoreEvent> = (opts) => { + const key = 'RULE'; + const { viewContent } = opts; + const { helperContext, underContext } = viewContent; + + return { + mode: key, + isDefault: true, + beforeDrawFrame: ({ snapshot }) => { + const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot); + const viewSizeInfo = getViewSizeInfoFromSnapshot(snapshot); + drawRulerBackground(helperContext, { viewScaleInfo, viewSizeInfo }); + + const xList = calcXRulerScaleList({ viewScaleInfo, viewSizeInfo }); + drawXRuler(helperContext, { scaleList: xList }); + + const yList = calcYRulerScaleList({ viewScaleInfo, viewSizeInfo }); + drawYRuler(helperContext, { scaleList: yList }); + + drawUnderGrid(underContext, { + xList, + yList, + viewScaleInfo, + viewSizeInfo + }); + } + }; +}; diff --git a/packages/core/src/middleware/ruler/util.ts b/packages/core/src/middleware/ruler/util.ts new file mode 100644 index 000000000..f1c3d87ba --- /dev/null +++ b/packages/core/src/middleware/ruler/util.ts @@ -0,0 +1,231 @@ +import type { ViewScaleInfo, ViewSizeInfo, ViewContext2D } from '@idraw/types'; +import { Context2D, formatNumber, rotateByCenter } from '@idraw/util'; + +const rulerSize = 16; +const background = '#FFFFFFA8'; +const borderColor = '#00000080'; +const scaleColor = '#000000'; +const textColor = '#00000080'; +const fontFamily = 'monospace'; +const fontSize = 10; +const fontWeight = 100; +const gridColor = '#AAAAAA30'; +const gridKeyColor = '#AAAAAA70'; + +// const rulerUnit = 10; +// const rulerKeyUnit = 100; +// const rulerSubKeyUnit = 50; + +interface RulerScale { + num: number; + showNum: boolean; + position: number; + isKeyNum: boolean; + isSubKeyNum: boolean; +} + +function calcRulerScaleList(opts: { scale: number; viewLength: number; viewOffset: number }): RulerScale[] { + const { scale, viewLength, viewOffset } = opts; + const list: RulerScale[] = []; + let rulerUnit = 10; + + rulerUnit = formatNumber(rulerUnit / scale, { decimalPlaces: 0 }); + rulerUnit = Math.max(10, Math.min(rulerUnit, 1000)); + + const rulerKeyUnit = rulerUnit * 10; + const rulerSubKeyUnit = rulerUnit * 5; + + let index: number = 0; + const viewUnit = rulerUnit * scale; + const startNum = 0 - viewOffset; + const startPosition = 0; + const remainderNum = startNum % viewUnit; + const firstNum = (startNum - remainderNum + viewUnit) / scale; + const firstPosition = startPosition + (viewUnit - remainderNum); + while (firstPosition + index * viewUnit < viewLength) { + const num = firstNum + index * rulerUnit; + const position = firstPosition + index * viewUnit; + const rulerScale = { + num: formatNumber(num, { decimalPlaces: 0 }), + position, + showNum: num % rulerKeyUnit === 0, + isKeyNum: num % rulerKeyUnit === 0, + isSubKeyNum: num % rulerSubKeyUnit === 0 + }; + // if (viewUnit >= rulerSubKeyUnit) { + // rulerScale.isKeyNum = true; + // } + list.push(rulerScale); + index++; + } + + return list; +} + +export function calcXRulerScaleList(opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): RulerScale[] { + const { viewScaleInfo, viewSizeInfo } = opts; + const { scale, offsetLeft } = viewScaleInfo; + const { width } = viewSizeInfo; + return calcRulerScaleList({ + scale, + viewLength: width, + viewOffset: offsetLeft + }); +} + +export function calcYRulerScaleList(opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): RulerScale[] { + const { viewScaleInfo, viewSizeInfo } = opts; + const { scale, offsetTop } = viewScaleInfo; + const { height } = viewSizeInfo; + return calcRulerScaleList({ + scale, + viewLength: height, + viewOffset: offsetTop + }); +} + +export function drawXRuler( + ctx: ViewContext2D, + opts: { + scaleList: RulerScale[]; + } +) { + const { scaleList } = opts; + const scaleDrawStart = rulerSize; + const scaleDrawEnd = (rulerSize * 4) / 5; + const subKeyScaleDrawEnd = (rulerSize * 2) / 5; + const keyScaleDrawEnd = (rulerSize * 1) / 5; + const fontStart = rulerSize / 5; + for (let i = 0; i < scaleList.length; i++) { + const item = scaleList[i]; + if (item.position < rulerSize) { + continue; + } + ctx.beginPath(); + ctx.moveTo(item.position, scaleDrawStart); + ctx.lineTo(item.position, item.isKeyNum ? keyScaleDrawEnd : item.isSubKeyNum ? subKeyScaleDrawEnd : scaleDrawEnd); + ctx.closePath(); + ctx.fillStyle = scaleColor; + ctx.stroke(); + if (item.isKeyNum) { + ctx.fillStyle = textColor; + ctx.textBaseline = 'top'; + ctx.$setFont({ + fontWeight, + fontSize, + fontFamily + }); + ctx.fillText(`${item.num}`, item.position + fontStart, fontStart); + } + } +} + +export function drawYRuler( + ctx: ViewContext2D, + opts: { + scaleList: RulerScale[]; + } +) { + const { scaleList } = opts; + const scaleDrawStart = rulerSize; + const scaleDrawEnd = (rulerSize * 4) / 5; + const subKeyScaleDrawEnd = (rulerSize * 2) / 5; + const keyScaleDrawEnd = (rulerSize * 1) / 5; + const fontStart = rulerSize / 5; + for (let i = 0; i < scaleList.length; i++) { + const item = scaleList[i]; + if (item.position < rulerSize) { + continue; + } + ctx.beginPath(); + ctx.moveTo(scaleDrawStart, item.position); + ctx.lineTo(item.isKeyNum ? keyScaleDrawEnd : item.isSubKeyNum ? subKeyScaleDrawEnd : scaleDrawEnd, item.position); + ctx.closePath(); + ctx.fillStyle = scaleColor; + ctx.stroke(); + if (item.showNum === true) { + const textX = fontStart; + const textY = item.position + fontStart; + const numText = `${item.num}`; + rotateByCenter(ctx, -90, { x: textX, y: textY }, () => { + ctx.fillStyle = textColor; + ctx.textBaseline = 'top'; + ctx.$setFont({ + fontWeight, + fontSize, + fontFamily + }); + ctx.fillText(numText, fontStart + fontSize, item.position + fontStart); + }); + } + } +} + +export function drawRulerBackground( + ctx: ViewContext2D, + opts: { + viewScaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; + } +) { + const { viewSizeInfo } = opts; + const { width, height } = viewSizeInfo; + + ctx.beginPath(); + ctx.moveTo(0, 0); + ctx.lineTo(width + 1, 0); + ctx.lineTo(width + 1, rulerSize); + ctx.lineTo(rulerSize, rulerSize); + ctx.lineTo(rulerSize, height + 1); + ctx.lineTo(0, height + 1); + ctx.lineTo(0, 0); + ctx.closePath(); + ctx.fillStyle = background; + ctx.fill(); + ctx.strokeStyle = borderColor; + ctx.stroke(); +} + +export function drawUnderGrid( + ctx: ViewContext2D, + opts: { + xList: RulerScale[]; + yList: RulerScale[]; + viewScaleInfo: ViewScaleInfo; + viewSizeInfo: ViewSizeInfo; + } +) { + const { xList, yList, viewSizeInfo } = opts; + const { width, height } = viewSizeInfo; + for (let i = 0; i < xList.length; i++) { + const item = xList[i]; + ctx.beginPath(); + ctx.moveTo(item.position, 0); + ctx.lineTo(item.position, height); + if (item.isKeyNum === true || item.isSubKeyNum === true) { + ctx.strokeStyle = gridKeyColor; + } else { + ctx.strokeStyle = gridColor; + } + + ctx.lineWidth = 1; + ctx.closePath(); + ctx.stroke(); + } + + for (let i = 0; i < yList.length; i++) { + const item = yList[i]; + ctx.beginPath(); + ctx.moveTo(0, item.position); + ctx.lineTo(width, item.position); + if (item.isKeyNum === true || item.isSubKeyNum === true) { + ctx.strokeStyle = gridKeyColor; + } else { + ctx.strokeStyle = gridColor; + } + ctx.lineWidth = 1; + ctx.closePath(); + ctx.stroke(); + } + // TODO +} diff --git a/packages/core/src/middleware/scroller/index.ts b/packages/core/src/middleware/scroller/index.ts index b2617a818..071f01f16 100644 --- a/packages/core/src/middleware/scroller/index.ts +++ b/packages/core/src/middleware/scroller/index.ts @@ -1,4 +1,3 @@ -import type { ElementSize } from '@idraw/types'; import type { Point, BoardMiddleware, PointWatcherEvent, BoardWatherWheelXEvent, BoardWatherWheelYEvent } from '@idraw/types'; import { drawScroller, isPointInScrollThumb } from './util'; // import type { ScrollbarThumbType } from './util'; diff --git a/packages/core/src/middleware/scroller/util.ts b/packages/core/src/middleware/scroller/util.ts index 018510cd5..ea27464c0 100644 --- a/packages/core/src/middleware/scroller/util.ts +++ b/packages/core/src/middleware/scroller/util.ts @@ -1,18 +1,18 @@ -import type { Point, PointSize, BoardViewerFrameSnapshot, ViewScaleInfo, ViewSizeInfo, ViewContext2D, ElementSize } from '@idraw/types'; +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'; const minScrollerWidth = 12; const scrollerLineWidth = 16; -const scrollerAlpha = 0.12; const scrollerThumbAlpha = 0.36; export type ScrollbarThumbType = 'X' | 'Y'; const scrollConfig = { width: minScrollerWidth, - color: '#000000', - showBackground: true + thumbColor: '#000000AA', + scrollBarColor: '#FFFFFF60', + showScrollBar: false }; function isPointAtRect(helperContext: ViewContext2D, p: Point, rect: ElementSize): boolean { @@ -81,7 +81,8 @@ function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeIn ySize = height; } - const xStart = lineSize / 2; + // const xStart = lineSize / 2; + const xStart = lineSize; const xEnd = width - xSize - lineSize; let translateX = xStart; @@ -90,11 +91,12 @@ function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeIn } else if (offsetRight > 0) { translateX = xEnd; } else if (offsetLeft <= 0 && xSize > 0 && !(offsetLeft === 0 && offsetRight === 0)) { - translateX = xSize / 2 + ((width - xSize) * Math.abs(offsetLeft)) / (Math.abs(offsetLeft) + Math.abs(offsetRight)); - translateX = Math.min(Math.max(0, translateX - xSize / 2), width - xSize); + translateX = xStart + ((width - xSize) * Math.abs(offsetLeft)) / (Math.abs(offsetLeft) + Math.abs(offsetRight)); + translateX = Math.min(Math.max(0, translateX - xStart), width - xSize); } - const yStart = lineSize / 2; + // const yStart = lineSize / 2; + const yStart = lineSize; const yEnd = height - ySize - lineSize; let translateY = yStart; if (offsetTop > 0) { @@ -102,8 +104,8 @@ function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeIn } else if (offsetBottom > 0) { translateY = yEnd; } else if (offsetTop <= 0 && ySize > 0 && !(offsetTop === 0 && offsetBottom === 0)) { - translateY = ySize / 2 + ((height - ySize) * Math.abs(offsetTop)) / (Math.abs(offsetTop) + Math.abs(offsetBottom)); - translateY = Math.min(Math.max(0, translateY - ySize / 2), height - ySize); + translateY = yStart + ((height - ySize) * Math.abs(offsetTop)) / (Math.abs(offsetTop) + Math.abs(offsetBottom)); + translateY = Math.min(Math.max(0, translateY - yStart), height - ySize); } const xThumbRect: ElementSize = { x: translateX, @@ -123,7 +125,8 @@ function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeIn ySize, translateY, translateX, - color: '#0000007A', + thumbColor: scrollConfig.thumbColor, + scrollBarColor: scrollConfig.scrollBarColor, xThumbRect, yThumbRect }; @@ -143,43 +146,52 @@ function drawScrollerThumb( } ): void { let { x, y, h, w } = opts; - const { color, axis } = opts; - if (axis === 'X') { - y = y + h / 4 + 0; - h = h / 2; - } else if (axis === 'Y') { - x = x + w / 4 + 0; - w = w / 2; - } - let r = opts.r; - r = Math.min(r, w / 2, h / 2); - if (w < r * 2 || h < r * 2) { - r = 0; - } - ctx.globalAlpha = scrollerThumbAlpha; - ctx.beginPath(); - ctx.moveTo(x + r, y); - ctx.arcTo(x + w, y, x + w, y + h, r); - ctx.arcTo(x + w, y + h, x, y + h, r); - ctx.arcTo(x, y + h, x, y, r); - ctx.arcTo(x, y, x + w, y, r); - ctx.closePath(); - ctx.fillStyle = color; - ctx.fill(); + ctx.save(); + ctx.shadowColor = '#FFFFFF'; + ctx.shadowOffsetX = 0; + ctx.shadowOffsetY = 0; + ctx.shadowBlur = 1; + { + const { color, axis } = opts; + if (axis === 'X') { + y = y + h / 4 + 0; + h = h / 2; + } else if (axis === 'Y') { + x = x + w / 4 + 0; + w = w / 2; + } - ctx.globalAlpha = 1; - ctx.beginPath(); - ctx.lineWidth = 1; - ctx.strokeStyle = color; - ctx.setLineDash([]); - ctx.moveTo(x + r, y); - ctx.arcTo(x + w, y, x + w, y + h, r); - ctx.arcTo(x + w, y + h, x, y + h, r); - ctx.arcTo(x, y + h, x, y, r); - ctx.arcTo(x, y, x + w, y, r); - ctx.closePath(); - ctx.stroke(); + let r = opts.r; + r = Math.min(r, w / 2, h / 2); + if (w < r * 2 || h < r * 2) { + r = 0; + } + ctx.globalAlpha = scrollerThumbAlpha; + ctx.beginPath(); + ctx.moveTo(x + r, y); + ctx.arcTo(x + w, y, x + w, y + h, r); + ctx.arcTo(x + w, y + h, x, y + h, r); + ctx.arcTo(x, y + h, x, y, r); + ctx.arcTo(x, y, x + w, y, r); + ctx.closePath(); + ctx.fillStyle = color; + ctx.fill(); + + ctx.globalAlpha = 1; + ctx.beginPath(); + ctx.lineWidth = 1; + ctx.strokeStyle = color; + ctx.setLineDash([]); + ctx.moveTo(x + r, y); + ctx.arcTo(x + w, y, x + w, y + h, r); + ctx.arcTo(x + w, y + h, x, y + h, r); + ctx.arcTo(x, y + h, x, y, r); + ctx.arcTo(x, y, x + w, y, r); + ctx.closePath(); + ctx.stroke(); + } + ctx.restore(); } function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo; scrollInfo: ScrollInfo }) { @@ -202,9 +214,8 @@ function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: V } // x-bar - if (scrollConfig.showBackground === true) { - ctx.globalAlpha = scrollerAlpha; - ctx.fillStyle = wrapper.color; + if (scrollConfig.showScrollBar === true) { + ctx.fillStyle = wrapper.scrollBarColor; // x-line ctx.fillRect(0, height - wrapper.lineSize, width, wrapper.lineSize); } @@ -215,13 +226,12 @@ function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: V axis: 'X', ...xThumbRect, r: wrapper.lineSize / 2, - color: wrapper.color + color: wrapper.thumbColor }); // y-bar - if (scrollConfig.showBackground === true) { - ctx.globalAlpha = scrollerAlpha; - ctx.fillStyle = wrapper.color; + if (scrollConfig.showScrollBar === true) { + ctx.fillStyle = wrapper.scrollBarColor; // y-line ctx.fillRect(width - wrapper.lineSize, 0, wrapper.lineSize, height); } @@ -232,7 +242,7 @@ function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: V axis: 'Y', ...yThumbRect, r: wrapper.lineSize / 2, - color: wrapper.color + color: wrapper.thumbColor }); ctx.globalAlpha = 1; diff --git a/packages/idraw/dev/index.html b/packages/idraw/dev/index.html index aad71faf3..811cf3a56 100644 --- a/packages/idraw/dev/index.html +++ b/packages/idraw/dev/index.html @@ -4,14 +4,16 @@ diff --git a/packages/idraw/src/idraw.ts b/packages/idraw/src/idraw.ts new file mode 100644 index 000000000..04794de26 --- /dev/null +++ b/packages/idraw/src/idraw.ts @@ -0,0 +1,109 @@ +import { Core, MiddlewareSelector, MiddlewareScroller, MiddlewareScaler, MiddlewareRuler } from '@idraw/core'; +import type { PointSize, IDrawOptions, Data, ViewSizeInfo, IDrawEvent } from '@idraw/types'; + +export class iDraw { + private _core: Core; + private _opts: IDrawOptions; + + constructor(mount: HTMLDivElement, opts: IDrawOptions) { + const core = new Core(mount, opts); + this._core = core; + this._opts = opts; + core.use(MiddlewareScroller); + core.use(MiddlewareSelector); + core.use(MiddlewareScaler); + core.use(MiddlewareRuler); + } + + setData(data: Data) { + this._core.setData(data); + } + + getData(): Data | null { + return this._core.getData(); + } + + selectElement() { + // TODO + } + + selectElementByIndex() { + // TODO + } + + cancelElement() { + // TODO + } + + cancelElementByIndex() { + // TODO + } + + updateElement() { + // TODO + } + + addElement() { + // TODO + } + + deleteElement() { + // TODO + } + + moveUpElement() { + // TODO + } + + moveDownElement() { + // TODO + } + + insertElementBefore() { + // TODO + } + + insertElementBeforeIndex() { + // TODO + } + + insertElementAfter() { + // TODO + } + + insertElementAfterIndex() { + // TODO + } + + scale(opts: { scale: number; point: PointSize }) { + this._core.scale(opts); + } + + resize(opts: Partial) { + this._core.resize(opts); + } + + on(name: T, callback: (e: IDrawEvent[T]) => void) { + this._core.on(name, callback); + } + + off(name: T, callback: (e: IDrawEvent[T]) => void) { + this._core.off(name, callback); + } + + trigger(name: T, e: IDrawEvent[T]) { + this._core.trigger(name, e); + } + + // scrollLeft() { + // // TODO + // } + + // scrollTop() { + // // TODO + // } + + // exportDataURL() { + // // TODO + // } +} diff --git a/packages/idraw/src/index.ts b/packages/idraw/src/index.ts index 082e19594..ed791c3f7 100644 --- a/packages/idraw/src/index.ts +++ b/packages/idraw/src/index.ts @@ -1,108 +1,94 @@ -import { Core, MiddlewareSelector, MiddlewareScroller, MiddlewareScaler } from '@idraw/core'; -import type { PointSize, IDrawOptions, Data, ViewSizeInfo, IDrawEvent } from '@idraw/types'; - -export class iDraw { - private _core: Core; - private _opts: IDrawOptions; - - constructor(mount: HTMLDivElement, opts: IDrawOptions) { - const core = new Core(mount, opts); - this._core = core; - this._opts = opts; - core.use(MiddlewareScroller); - core.use(MiddlewareSelector); - core.use(MiddlewareScaler); - } - - setData(data: Data) { - this._core.setData(data); - } - - getData(): Data | null { - return this._core.getData(); - } - - selectElement() { - // TODO - } - - selectElementByIndex() { - // TODO - } - - cancelElement() { - // TODO - } - - cancelElementByIndex() { - // TODO - } - - updateElement() { - // TODO - } - - addElement() { - // TODO - } - - deleteElement() { - // TODO - } - - moveUpElement() { - // TODO - } - - moveDownElement() { - // TODO - } - - insertElementBefore() { - // TODO - } - - insertElementBeforeIndex() { - // TODO - } - - insertElementAfter() { - // TODO - } - - insertElementAfterIndex() { - // TODO - } - - scale(opts: { scale: number; point: PointSize }) { - this._core.scale(opts); - } - - resize(opts: Partial) { - this._core.resize(opts); - } - - on(name: T, callback: (e: IDrawEvent[T]) => void) { - this._core.on(name, callback); - } - - off(name: T, callback: (e: IDrawEvent[T]) => void) { - this._core.off(name, callback); - } - - trigger(name: T, e: IDrawEvent[T]) { - this._core.trigger(name, e); - } - - // scrollLeft() { - // // TODO - // } - - // scrollTop() { - // // TODO - // } - - // exportDataURL() { - // // TODO - // } -} +export { iDraw } from './idraw'; +export type * from '@idraw/types'; +export { Core, MiddlewareSelector, MiddlewareScroller, MiddlewareScaler } from '@idraw/core'; +export { Renderer } from '@idraw/renderer'; +export { + delay, + compose, + throttle, + downloadImageFromCanvas, + parseFileToBase64, + pickFile, + parseFileToText, + toColorHexStr, + toColorHexNum, + isColorStr, + colorNameToHex, + colorToCSS, + colorToLinearGradientCSS, + mergeHexColorAlpha, + createUUID, + isAssetId, + createAssetId, + deepClone, + sortDataAsserts, + istype, + loadImage, + loadSVG, + loadHTML, + is, + check, + createBoardContexts, + createContext2D, + createOffscreenContext2D, + EventEmitter, + calcDistance, + calcSpeed, + equalPoint, + equalTouchPoint, + vaildPoint, + vaildTouchPoint, + getCenterFromTwoPoints, + Store, + getViewScaleInfoFromSnapshot, + getViewSizeInfoFromSnapshot, + Context2D, + rotateElement, + parseRadianToAngle, + parseAngleToRadian, + rotateElementVertexes, + getElementRotateVertexes, + calcElementCenter, + calcElementCenterFromVertexes, + rotatePointInGroup, + limitAngle, + getSelectedElementUUIDs, + validateElements, + calcElementsContextSize, + calcElementsViewInfo, + getElemenetsAssetIds, + findElementFromList, + findElementsFromList, + updateElementInList, + getGroupQueueFromList, + getElementSize, + mergeElementAsset, + filterElementAsset, + isResourceElement, + checkRectIntersect, + viewScale, + viewScroll, + calcViewElementSize, + calcViewPointSize, + calcViewVertexes, + isViewPointInElement, + getViewPointAtElement, + isElementInView, + rotatePoint, + rotateVertexes, + getElementVertexes, + calcElementVertexesInGroup, + calcElementVertexesQueueInGroup, + calcElementQueueVertexesQueueInGroup, + calcElementSizeController, + generateSVGPath, + parseSVGPath, + generateHTML, + parseHTML, + compressImage, + formatNumber, + matrixToAngle, + matrixToRadian, + getDefaultElementDetailConfig, + calcViewBoxSize +} from '@idraw/util'; diff --git a/packages/types/src/lib/view.ts b/packages/types/src/lib/view.ts index 9e59e6249..6be73637d 100644 --- a/packages/types/src/lib/view.ts +++ b/packages/types/src/lib/view.ts @@ -23,9 +23,10 @@ export interface ViewSizeInfo extends ViewContextSize { } export interface ViewContent { + boardContext: ViewContext2D; viewContext: ViewContext2D; helperContext: ViewContext2D; - boardContext: ViewContext2D; + underContext: ViewContext2D; } export interface ViewCalculatorOptions { diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index 4b219e607..609d7e64b 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -50,7 +50,7 @@ export { getViewPointAtElement, isElementInView } from './lib/view-calc'; -export { rotatePoint, rotateVertexes } from './lib/rotate'; +export { rotatePoint, rotateVertexes, rotateByCenter } from './lib/rotate'; export { getElementVertexes, calcElementVertexesInGroup, calcElementVertexesQueueInGroup, calcElementQueueVertexesQueueInGroup } from './lib/vertex'; export { calcElementSizeController } from './lib/controller'; export { generateSVGPath, parseSVGPath } from './lib/svg-path'; diff --git a/packages/util/src/lib/canvas.ts b/packages/util/src/lib/canvas.ts index 691c8338c..85d10d8d9 100644 --- a/packages/util/src/lib/canvas.ts +++ b/packages/util/src/lib/canvas.ts @@ -32,8 +32,10 @@ export function createBoardContexts(ctx: CanvasRenderingContext2D, opts?: { devi }; const viewContext = createContext2D(ctxOpts); const helperContext = createContext2D(ctxOpts); + const underContext = createContext2D(ctxOpts); const boardContext = createContext2D({ ctx, ...ctxOpts }); const content: ViewContent = { + underContext, viewContext, helperContext, boardContext diff --git a/packages/util/src/lib/number.ts b/packages/util/src/lib/number.ts index 2ed77c9a9..9a909ac3a 100644 --- a/packages/util/src/lib/number.ts +++ b/packages/util/src/lib/number.ts @@ -1,4 +1,7 @@ export function formatNumber(num: number, opts?: { decimalPlaces?: number }): number { - const decimalPlaces = opts?.decimalPlaces || 2; + let decimalPlaces = 2; + if (typeof opts?.decimalPlaces !== 'undefined' && opts?.decimalPlaces >= 0) { + decimalPlaces = opts.decimalPlaces; + } return parseFloat(num.toFixed(decimalPlaces)); } diff --git a/packages/util/src/lib/rotate.ts b/packages/util/src/lib/rotate.ts index 39a636133..b65896679 100644 --- a/packages/util/src/lib/rotate.ts +++ b/packages/util/src/lib/rotate.ts @@ -1,4 +1,4 @@ -import type { ViewContext2D, PointSize, ElementSize, ViewRectVertexes, Element, ElementType } from '@idraw/types'; +import type { ViewContext2D, PointSize, ElementSize, ViewRectVertexes, Element } from '@idraw/types'; import { calcDistance } from './point'; // import { calcElementVertexes } from './vertex'; @@ -18,21 +18,19 @@ export function parseAngleToRadian(angle: number): number { // return p; // } -export function rotateElement( +export function rotateByCenter( ctx: ViewContext2D | CanvasRenderingContext2D | ViewContext2D, - elemSize: ElementSize, + angle: number, + center: PointSize, callback: (ctx: ViewContext2D | CanvasRenderingContext2D) => void ): void { - const center = calcElementCenter(elemSize); - const radian = parseAngleToRadian(elemSize.angle || 0); + const radian = parseAngleToRadian(angle || 0); if (center && (radian > 0 || radian < 0)) { ctx.translate(center.x, center.y); ctx.rotate(radian); ctx.translate(-center.x, -center.y); } - callback(ctx); - if (center && (radian > 0 || radian < 0)) { ctx.translate(center.x, center.y); ctx.rotate(-radian); @@ -40,6 +38,17 @@ export function rotateElement( } } +export function rotateElement( + ctx: ViewContext2D | CanvasRenderingContext2D | ViewContext2D, + elemSize: ElementSize, + callback: (ctx: ViewContext2D | CanvasRenderingContext2D) => void +): void { + const center = calcElementCenter(elemSize); + rotateByCenter(ctx, elemSize.angle || 0, center, () => { + callback(ctx); + }); +} + export function calcElementCenter(elem: ElementSize): PointSize { const p = { x: elem.x + elem.w / 2,