From 5a5b4708e1ed03e0aae6c4584a8ff881a95098a4 Mon Sep 17 00:00:00 2001 From: chenshenhai Date: Sat, 18 Mar 2023 22:04:44 +0800 Subject: [PATCH] feat: improve scroller of iDraw.js board --- packages/board/src/lib/scroller.ts | 209 ++++++++++++++++--------- packages/core/src/index.ts | 9 +- packages/idraw/dev/main.ts | 6 +- packages/idraw/src/config.ts | 4 +- packages/renderer/src/lib/draw/base.ts | 21 ++- packages/types/src/lib/board.ts | 33 ++-- packages/types/src/lib/config.ts | 40 +++-- 7 files changed, 200 insertions(+), 122 deletions(-) diff --git a/packages/board/src/lib/scroller.ts b/packages/board/src/lib/scroller.ts index 4c5048a..c7ee42f 100644 --- a/packages/board/src/lib/scroller.ts +++ b/packages/board/src/lib/scroller.ts @@ -1,91 +1,105 @@ import { TypePoint, TypeScreenPosition, - TypeBoardScrollConfig, + TypeBoardScrollConfig } from '@idraw/types'; import { isColorStr } from '@idraw/util'; type TypeOptions = { - width: number, - height: number, - devicePixelRatio: number, - scrollConfig?: TypeBoardScrollConfig, + width: number; + height: number; + devicePixelRatio: number; + scrollConfig?: TypeBoardScrollConfig; }; -type TypePrivateOptions = TypeOptions & { - width: number, - height: number, - devicePixelRatio: number, - scrollConfig: TypeBoardScrollConfig, -} +type TypePrivateOptions = Required< + TypeOptions & { scrollConfig: Required } +>; -const defaultScrollConfig = { - lineWidth: 12, - color: '#a0a0a0' +const minScrollerWidth = 12; +const scrollerAlpha = 0.12; +const scrollerThumbAlpha = 0.36; + +const defaultScrollConfig: Partial & { + width: number; + color: string; +} = { + width: minScrollerWidth, + color: '#000000', + showBackground: true }; export class Scroller { - private _displayCtx: CanvasRenderingContext2D; private _opts: TypePrivateOptions; - constructor( - ctx: CanvasRenderingContext2D, - opts: TypeOptions - ) { + constructor(ctx: CanvasRenderingContext2D, opts: TypeOptions) { this._displayCtx = ctx; this._opts = this._getOpts(opts); } draw(position: TypeScreenPosition) { - const { width, height } = this._opts; + const { width, height, scrollConfig } = this._opts; const wrapper = this.calc(position); const ctx = this._displayCtx; - - if (wrapper.xSize > 0) { - ctx.globalAlpha = 0.2; - ctx.fillStyle = wrapper.color; - // x-line - ctx.fillRect(0, this._doSize(height - wrapper.lineSize), this._doSize(width), this._doSize(wrapper.lineSize)); - ctx.globalAlpha = 1; + if (wrapper.xSize > 0) { + if (scrollConfig.showBackground === true) { + ctx.globalAlpha = scrollerAlpha; + ctx.fillStyle = wrapper.color; + // x-line + ctx.fillRect( + 0, + this._doSize(height - wrapper.lineSize), + this._doSize(width), + this._doSize(wrapper.lineSize) + ); + } + + // ctx.globalAlpha = 1; // x-slider - drawBox(ctx, { + drawBoxScrollerThumb(ctx, { + axis: 'X', x: this._doSize(wrapper.translateX), y: this._doSize(height - wrapper.lineSize), w: this._doSize(wrapper.xSize), h: this._doSize(wrapper.lineSize), r: this._doSize(wrapper.lineSize / 2), - color: wrapper.color, + color: wrapper.color }); } if (wrapper.ySize > 0) { - ctx.globalAlpha = 0.2; - ctx.fillStyle = wrapper.color; + if (scrollConfig.showBackground === true) { + ctx.globalAlpha = scrollerAlpha; + ctx.fillStyle = wrapper.color; + // y-line + ctx.fillRect( + this._doSize(width - wrapper.lineSize), + 0, + this._doSize(wrapper.lineSize), + this._doSize(height) + ); + } - // y-line - ctx.fillRect(this._doSize(width - wrapper.lineSize), 0, this._doSize(wrapper.lineSize), this._doSize(height)); - - ctx.globalAlpha = 1; + // ctx.globalAlpha = 1; // y-slider - drawBox(ctx, { + drawBoxScrollerThumb(ctx, { + axis: 'Y', x: this._doSize(width - wrapper.lineSize), y: this._doSize(wrapper.translateY), w: this._doSize(wrapper.lineSize), h: this._doSize(wrapper.ySize), r: this._doSize(wrapper.lineSize / 2), - color: wrapper.color, + color: wrapper.color }); } - ctx.globalAlpha = 1; - } - resetSize(opts: { width: number, height: number, devicePixelRatio: number }) { - this._opts = {...this._opts, ...opts}; + resetSize(opts: { width: number; height: number; devicePixelRatio: number }) { + this._opts = { ...this._opts, ...opts }; } isPointAtScrollY(p: TypePoint): boolean { @@ -93,15 +107,15 @@ export class Scroller { const ctx = this._displayCtx; ctx.beginPath(); ctx.rect( - this._doSize(width - scrollConfig.lineWidth), - 0, - this._doSize(scrollConfig.lineWidth), + this._doSize(width - scrollConfig.width), + 0, + this._doSize(scrollConfig.width), this._doSize(height) ); ctx.closePath(); if (ctx.isPointInPath(this._doSize(p.x), this._doSize(p.y))) { return true; - } + } return false; } @@ -110,42 +124,50 @@ export class Scroller { const ctx = this._displayCtx; ctx.beginPath(); ctx.rect( - 0, - this._doSize(height - scrollConfig.lineWidth), - this._doSize(width - scrollConfig.lineWidth), - this._doSize(scrollConfig.lineWidth) + 0, + this._doSize(height - scrollConfig.width), + this._doSize(width - scrollConfig.width), + this._doSize(scrollConfig.width) ); ctx.closePath(); if (ctx.isPointInPath(this._doSize(p.x), this._doSize(p.y))) { return true; - } + } return false; } getLineWidth(): number { - const lineWidth = this._opts.scrollConfig.lineWidth; + const lineWidth = this._opts.scrollConfig.width; return lineWidth; } - calc(position: TypeScreenPosition) { const { width, height, scrollConfig } = this._opts; - const sliderMinSize = scrollConfig.lineWidth * 2.5; - const lineSize = scrollConfig.lineWidth; + const sliderMinSize = scrollConfig.width * 2.5; + const lineSize = scrollConfig.width; let xSize = 0; let ySize = 0; if (position.left <= 0 && position.right <= 0) { - xSize = Math.max(sliderMinSize, width - (Math.abs(position.left) + Math.abs(position.right))); + xSize = Math.max( + sliderMinSize, + width - (Math.abs(position.left) + Math.abs(position.right)) + ); if (xSize >= width) xSize = 0; } if (position.top <= 0 || position.bottom <= 0) { - ySize = Math.max(sliderMinSize, height - (Math.abs(position.top) + Math.abs(position.bottom))); + ySize = Math.max( + sliderMinSize, + height - (Math.abs(position.top) + Math.abs(position.bottom)) + ); if (ySize >= height) ySize = 0; } let translateX = 0; if (xSize > 0) { - translateX = xSize / 2 + (width - xSize) * Math.abs(position.left) / (Math.abs(position.left) + Math.abs(position.right)); + translateX = + xSize / 2 + + ((width - xSize) * Math.abs(position.left)) / + (Math.abs(position.left) + Math.abs(position.right)); translateX = Math.min(Math.max(0, translateX - xSize / 2), width - xSize); // const xUnit = this.calcScreenScrollUnit(position.left, position.right, xSize, width); // translateX = translateX * xUnit; @@ -153,8 +175,14 @@ export class Scroller { let translateY = 0; if (ySize > 0) { - translateY = ySize / 2 + (height - ySize) * Math.abs(position.top) / (Math.abs(position.top) + Math.abs(position.bottom)); - translateY = Math.min(Math.max(0, translateY - ySize / 2), height - ySize); + translateY = + ySize / 2 + + ((height - ySize) * Math.abs(position.top)) / + (Math.abs(position.top) + Math.abs(position.bottom)); + translateY = Math.min( + Math.max(0, translateY - ySize / 2), + height - ySize + ); // const yUnit = this.calcScreenScrollUnit(position.top, position.bottom, ySize, height); // translateY = translateY * yUnit; } @@ -173,16 +201,25 @@ export class Scroller { return num * this._opts.devicePixelRatio; } - private _getOpts(opts: TypeOptions): TypePrivateOptions { - const options = { ...{ scrollConfig: defaultScrollConfig }, ...opts}; + console.log('opts ====', opts); + const options: TypePrivateOptions = { + ...opts, + ...{ + scrollConfig: { ...defaultScrollConfig, ...(opts.scrollConfig || {}) } + } + } as TypePrivateOptions; if (!options.scrollConfig) { - options.scrollConfig = defaultScrollConfig; + options.scrollConfig = + defaultScrollConfig as TypePrivateOptions['scrollConfig']; } - if (!(options.scrollConfig.lineWidth > 0)) { - options.scrollConfig.lineWidth = defaultScrollConfig.lineWidth; + if (!(options?.scrollConfig?.width > 0)) { + options.scrollConfig.width = defaultScrollConfig.width; } - options.scrollConfig.lineWidth = Math.max(options.scrollConfig.lineWidth, defaultScrollConfig.lineWidth); + options.scrollConfig.width = Math.max( + options.scrollConfig.width, + defaultScrollConfig.width + ); if (isColorStr(options.scrollConfig.color) !== true) { options.scrollConfig.color = options.scrollConfig.color; @@ -191,25 +228,53 @@ export class Scroller { } } - -function drawBox( +function drawBoxScrollerThumb( ctx: CanvasRenderingContext2D, - opts: { x: number, y: number, w: number, h: number, r: number, color: string } + opts: { + axis: 'X' | 'Y'; + x: number; + y: number; + w: number; + h: number; + r: number; + color: string; + } ): void { + let { x, y, h, w } = opts; + const { color, axis } = opts; + if (axis === 'X') { + y = y + h / 4 + 1; + h = h / 2; + } else if (axis === 'Y') { + x = x + w / 4 + 1; + w = w / 2; + } - const { x, y, w, h, color } = opts; 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.closePath(); ctx.fillStyle = color; - ctx.fill(); -} \ No newline at end of file + ctx.fill(); + + ctx.globalAlpha = 1; + ctx.beginPath(); + ctx.lineWidth = 1; + ctx.strokeStyle = color; + 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(); +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index cf2aeea..8cde021 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -93,8 +93,9 @@ export default class Core { ...this[_opts], canScroll: config?.scrollWrapper?.use, scrollConfig: { - color: config?.scrollWrapper?.color || '#a0a0a0', - lineWidth: config?.scrollWrapper?.lineWidth || 12 + color: config?.scrollWrapper?.color || '#000000', + width: config?.scrollWrapper?.width || 12, + ...(config?.scrollWrapper || {}) } }); this[_renderer] = new Renderer(); @@ -173,8 +174,8 @@ export default class Core { return selectElement(this, uuid); } - cancelElementByIndex(index: number, opts?: { useMode?: boolean }): void { - return cancelElementByIndex(this, index, opts); + cancelElementByIndex(index: number): void { + return cancelElementByIndex(this, index); } cancelElement(uuid: string, opts?: { useMode?: boolean }): void { diff --git a/packages/idraw/dev/main.ts b/packages/idraw/dev/main.ts index 6e01018..af00ed5 100644 --- a/packages/idraw/dev/main.ts +++ b/packages/idraw/dev/main.ts @@ -27,17 +27,21 @@ const idraw = new iDraw( { scrollWrapper: { use: true + // color: 'red' + // showBackground: false } } ); idraw.setData(data); -const parseData = idraw.getData(); +// const parseData = idraw.getData(); idraw.on('changeData', (d) => { console.log('changeData ======', d); }); +idraw.scale(1.5); + idraw.selectElementByIndex(1); setTimeout(() => { diff --git a/packages/idraw/src/config.ts b/packages/idraw/src/config.ts index 4acd5ce..300fbf0 100644 --- a/packages/idraw/src/config.ts +++ b/packages/idraw/src/config.ts @@ -8,5 +8,5 @@ export const defaultOptions: PrivateOptions = { devicePixelRatio: 1, onlyRender: false, maxRecords: 10, - disableKeyboard: true, -} \ No newline at end of file + disableKeyboard: true +}; diff --git a/packages/renderer/src/lib/draw/base.ts b/packages/renderer/src/lib/draw/base.ts index 6d0a26c..b2e01ed 100644 --- a/packages/renderer/src/lib/draw/base.ts +++ b/packages/renderer/src/lib/draw/base.ts @@ -1,7 +1,7 @@ import { TypeContext, // TypeElemDesc, - TypeElement, + TypeElement } from '@idraw/types'; import { is, istype, isColorStr } from '@idraw/util'; import { rotateElement } from './../transform'; @@ -14,7 +14,7 @@ export function clearContext(ctx: TypeContext) { ctx.setLineDash([]); ctx.setGlobalAlpha(1); ctx.setShadowColor('#00000000'); - ctx.setShadowOffsetX(0) + ctx.setShadowOffsetX(0); ctx.setShadowOffsetY(0); ctx.setShadowBlur(0); } @@ -28,7 +28,7 @@ export function drawBgColor(ctx: TypeContext, color: string) { export function drawBox( ctx: TypeContext, elem: TypeElement<'text' | 'rect'>, - pattern: string | CanvasPattern | null, + pattern: string | CanvasPattern | null ): void { clearContext(ctx); drawBoxBorder(ctx, elem); @@ -46,20 +46,19 @@ export function drawBox( 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.closePath(); if (typeof pattern === 'string') { - ctx.setFillStyle(pattern); + ctx.setFillStyle(pattern); } else if (['CanvasPattern'].includes(istype.type(pattern))) { ctx.setFillStyle(pattern as CanvasPattern); } - ctx.fill(); + ctx.fill(); }); } - export function drawBoxBorder( ctx: TypeContext, - elem: TypeElement<'text'|'rect'>, + elem: TypeElement<'text' | 'rect'> ): void { clearContext(ctx); rotateElement(ctx, elem, () => { @@ -102,7 +101,7 @@ export function drawBoxBorder( 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.closePath(); + ctx.stroke(); }); -} \ No newline at end of file +} diff --git a/packages/types/src/lib/board.ts b/packages/types/src/lib/board.ts index 2c81524..8e1cbe1 100644 --- a/packages/types/src/lib/board.ts +++ b/packages/types/src/lib/board.ts @@ -1,12 +1,13 @@ type TypePoint = { x: number; y: number; -} +}; type TypeBoardScrollConfig = { - color: string, - lineWidth: number -} + color: string; + width: number; + showBackground?: boolean; +}; type TypeBoardSizeOptions = { width?: number; @@ -14,7 +15,7 @@ type TypeBoardSizeOptions = { contextWidth?: number; contextHeight?: number; devicePixelRatio?: number; -} +}; type TypeBoardOptions = TypeBoardSizeOptions & { width: number; @@ -22,16 +23,26 @@ type TypeBoardOptions = TypeBoardSizeOptions & { contextWidth: number; contextHeight: number; canScroll?: boolean; - scrollConfig?: TypeBoardScrollConfig -} + scrollConfig?: TypeBoardScrollConfig; +}; -type TypePointCursor = 'auto' | 'move' | 'n-resize' | 'e-resize' | 's-resize' | 'w-resize' -| 'ne-resize' | 'nw-resize' | 'se-resize' | 'sw-resize' | 'grab'; +type TypePointCursor = + | 'auto' + | 'move' + | 'n-resize' + | 'e-resize' + | 's-resize' + | 'w-resize' + | 'ne-resize' + | 'nw-resize' + | 'se-resize' + | 'sw-resize' + | 'grab'; export { TypePoint, TypePointCursor, TypeBoardSizeOptions, TypeBoardOptions, - TypeBoardScrollConfig, -}; \ No newline at end of file + TypeBoardScrollConfig +}; diff --git a/packages/types/src/lib/config.ts b/packages/types/src/lib/config.ts index a422bd1..457e1be 100644 --- a/packages/types/src/lib/config.ts +++ b/packages/types/src/lib/config.ts @@ -1,28 +1,26 @@ type TypeConfig = { elementWrapper?: { - color?: string, - controllerSize?: number, - lineWidth?: number, - lineDash?: number[], - }, + color?: string; + controllerSize?: number; + lineWidth?: number; + lineDash?: number[]; + }; scrollWrapper?: { - use?: boolean, - color?: string, - lineWidth?: number, - } -} + use?: boolean; + color?: string; + width?: number; + showBackground?: boolean; + }; +}; type TypeConfigStrict = TypeConfig & { elementWrapper: { - color: string, - lockColor: string, - controllerSize: number, - lineWidth: number, - lineDash: number[], - }, -} + color: string; + lockColor: string; + controllerSize: number; + lineWidth: number; + lineDash: number[]; + }; +}; -export { - TypeConfig, - TypeConfigStrict -}; \ No newline at end of file +export { TypeConfig, TypeConfigStrict };