diff --git a/packages/board/src/index.ts b/packages/board/src/index.ts index 5c0a93a..ceab8a8 100644 --- a/packages/board/src/index.ts +++ b/packages/board/src/index.ts @@ -1,6 +1,6 @@ import { Renderer } from '@idraw/renderer'; -import { throttle } from '@idraw/util'; -import type { Data, BoardMode, BoardOptions, BoardMiddleware, BoardMiddlewareObject, BoardWatcherEventMap, ViewSizeInfo } from '@idraw/types'; +import { throttle, calcElementsContextSize } from '@idraw/util'; +import type { Data, BoardMode, BoardOptions, BoardMiddleware, BoardMiddlewareObject, BoardWatcherEventMap, ViewSizeInfo, PointSize } from '@idraw/types'; import { Calculator } from './lib/calculator'; import { BoardWatcher } from './lib/watcher'; import { Sharer } from './lib/sharer'; @@ -90,7 +90,7 @@ export class Board { this._handleWheelScale(e); }, frameTime) ); - this._watcher.on('scale', this._handleScale.bind(this)); + // this._watcher.on('scale', this._handleScale.bind(this)); this._watcher.on('scrollX', this._handleScrollX.bind(this)); this._watcher.on('scrollY', this._handleScrollY.bind(this)); this._watcher.on('resize', this._handleResize.bind(this)); @@ -177,15 +177,15 @@ export class Board { } } - private _handleScale(e: BoardWatcherEventMap['scale']) { - for (let i = 0; i < this._activeMiddlewareObjs.length; i++) { - const obj = this._activeMiddlewareObjs[i]; - const result = obj?.scale?.(e); - if (result === false) { - return; - } - } - } + // private _handleScale(e: BoardWatcherEventMap['scale']) { + // for (let i = 0; i < this._activeMiddlewareObjs.length; i++) { + // const obj = this._activeMiddlewareObjs[i]; + // const result = obj?.scale?.(e); + // if (result === false) { + // return; + // } + // } + // } private _handleScrollX(e: BoardWatcherEventMap['scrollX']) { for (let i = 0; i < this._activeMiddlewareObjs.length; i++) { @@ -266,9 +266,24 @@ export class Board { return this._sharer; } - setData(data: Data) { + setData(data: Data): { viewSizeInfo: ViewSizeInfo } { + const sharer = this._sharer; this._sharer.setActiveStorage('data', data); + const viewSizeInfo = sharer.getActiveViewSizeInfo(); + // const currentScaleInfo = sharer.getActiveViewScaleInfo(); + const newViewContextSize = calcElementsContextSize(data.elements, { + viewWidth: viewSizeInfo.width, + viewHeight: viewSizeInfo.height, + extend: true + }); this._viewer.drawFrame(); + const newViewSizeInfo = { + ...viewSizeInfo, + ...newViewContextSize + }; + + this._sharer.setActiveViewSizeInfo(newViewSizeInfo); + return { viewSizeInfo: newViewSizeInfo }; } getData(): Data | null { @@ -284,22 +299,14 @@ export class Board { this._activeMiddlewareObjs.push(obj); } - scale(num: number) { - const viewScaleInfo = this._viewer.scale(num); - this._viewer.drawFrame(); - this._watcher.trigger('scale', viewScaleInfo); + scale(opts: { scale: number; point: PointSize }) { + const { _viewer: viewer } = this; + const { moveX, moveY } = viewer.scale(opts); + viewer.scroll({ moveX, moveY }); } - scrollX(num: number) { - const viewScaleInfo = this._viewer.scrollX(num); - this._viewer.drawFrame(); - this._watcher.trigger('scrollX', viewScaleInfo); - } - - scrollY(num: number) { - const viewScaleInfo = this._viewer.scrollY(num); - this._viewer.drawFrame(); - this._watcher.trigger('scrollY', viewScaleInfo); + scroll(opts: { moveX: number; moveY: number }) { + return this._viewer.scroll(opts); } resize(newViewSize: ViewSizeInfo) { diff --git a/packages/board/src/lib/sharer.ts b/packages/board/src/lib/sharer.ts index 9e7e60c..a51b2ea 100644 --- a/packages/board/src/lib/sharer.ts +++ b/packages/board/src/lib/sharer.ts @@ -60,7 +60,7 @@ export class Sharer implements StoreSharer // get/set active info - getActiveScaleInfo(): ViewScaleInfo { + getActiveViewScaleInfo(): ViewScaleInfo { const viewScaleInfo: ViewScaleInfo = { scale: this._activeStore.get('scale'), offsetTop: this._activeStore.get('offsetTop'), @@ -71,7 +71,7 @@ export class Sharer implements StoreSharer return viewScaleInfo; } - setActiveScaleInfo(viewScaleInfo: ViewScaleInfo) { + setActiveViewScaleInfo(viewScaleInfo: ViewScaleInfo) { const { scale, offsetTop, offsetBottom, offsetLeft, offsetRight } = viewScaleInfo; this._activeStore.set('scale', scale); this._activeStore.set('offsetTop', offsetTop); diff --git a/packages/board/src/lib/viewer.ts b/packages/board/src/lib/viewer.ts index 5f8be6b..6824905 100644 --- a/packages/board/src/lib/viewer.ts +++ b/packages/board/src/lib/viewer.ts @@ -1,5 +1,14 @@ import { EventEmitter, viewScale, viewScroll } from '@idraw/util'; -import type { BoardViewer, BoardViewerEventMap, BoardViewerOptions, ActiveStore, BoardViewerFrameSnapshot, ViewScaleInfo, ViewSizeInfo } from '@idraw/types'; +import type { + PointSize, + BoardViewer, + BoardViewerEventMap, + BoardViewerOptions, + ActiveStore, + BoardViewerFrameSnapshot, + ViewScaleInfo, + ViewSizeInfo +} from '@idraw/types'; const { requestAnimationFrame } = window; @@ -89,33 +98,58 @@ export class Viewer extends EventEmitter implements BoardVi this._drawAnimationFrame(); } - scale(num: number): ViewScaleInfo { - const { sharer, renderer } = this._opts; - const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); + scale(opts: { scale: number; point: PointSize }): { moveX: number; moveY: number } { + const { scale, point } = opts; + const { sharer } = this._opts; + const { moveX, moveY } = viewScale({ + scale, + point, + viewScaleInfo: sharer.getActiveViewScaleInfo(), + viewSizeInfo: sharer.getActiveViewSizeInfo() + }); + sharer.setActiveStorage('scale', scale); + // renderer.scale(scale); + return { moveX, moveY }; + } + + scroll(opts: { moveX: number; moveY: number }): ViewScaleInfo { + const { sharer } = this._opts; + const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveViewScaleInfo(); + const { moveX, moveY } = opts; const viewSizeInfo: ViewSizeInfo = sharer.getActiveViewSizeInfo(); - const viewScaleInfo = viewScale({ num, prevViewScaleInfo, viewSizeInfo }); - sharer.setActiveScaleInfo(viewScaleInfo); - renderer.scale(num); + const viewScaleInfo = viewScroll({ + moveX, + moveY, + viewScaleInfo: prevViewScaleInfo, + viewSizeInfo + }); + sharer.setActiveViewScaleInfo(viewScaleInfo); return viewScaleInfo; } - scrollX(num: number): ViewScaleInfo { - const { sharer } = this._opts; - const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); - const viewSizeInfo: ViewSizeInfo = sharer.getActiveViewSizeInfo(); - const viewScaleInfo = viewScroll({ moveX: num - (prevViewScaleInfo.offsetLeft || 0), viewScaleInfo: prevViewScaleInfo, viewSizeInfo }); - sharer.setActiveScaleInfo(viewScaleInfo); - return viewScaleInfo; - } + // scrollX(num: number): ViewScaleInfo { + // // TODO + // const { sharer } = this._opts; + // return sharer.getActiveViewScaleInfo(); + // // const { sharer } = this._opts; + // // const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveViewScaleInfo(); + // // const viewSizeInfo: ViewSizeInfo = sharer.getActiveViewSizeInfo(); + // // const viewScaleInfo = viewScroll({ moveX: num - (prevViewScaleInfo.offsetLeft || 0), viewScaleInfo: prevViewScaleInfo, viewSizeInfo }); + // // sharer.setActiveViewScaleInfo(viewScaleInfo); + // // return viewScaleInfo; + // } - scrollY(num: number): ViewScaleInfo { - const { sharer } = this._opts; - const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveScaleInfo(); - const viewSizeInfo: ViewSizeInfo = sharer.getActiveViewSizeInfo(); - const viewScaleInfo = viewScroll({ moveY: num - (prevViewScaleInfo.offsetTop || 0), viewScaleInfo: prevViewScaleInfo, viewSizeInfo }); - sharer.setActiveScaleInfo(viewScaleInfo); - return viewScaleInfo; - } + // scrollY(num: number): ViewScaleInfo { + // // TODO + // const { sharer } = this._opts; + // return sharer.getActiveViewScaleInfo(); + // // const { sharer } = this._opts; + // // const prevViewScaleInfo: ViewScaleInfo = sharer.getActiveViewScaleInfo(); + // // const viewSizeInfo: ViewSizeInfo = sharer.getActiveViewSizeInfo(); + // // const viewScaleInfo = viewScroll({ moveY: num - (prevViewScaleInfo.offsetTop || 0), viewScaleInfo: prevViewScaleInfo, viewSizeInfo }); + // // sharer.setActiveViewScaleInfo(viewScaleInfo); + // // return viewScaleInfo; + // } resize(viewSize: Partial = {}): ViewSizeInfo { const { sharer } = this._opts; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 7fa4049..bd3e200 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,4 +1,4 @@ -import type { Data, CoreOptions, BoardMiddleware, ViewSizeInfo } from '@idraw/types'; +import type { Data, PointSize, CoreOptions, BoardMiddleware, ViewSizeInfo } from '@idraw/types'; import { Board } from '@idraw/board'; import { createBoardContexts, validateElements, calcElementsContextSize } from '@idraw/util'; @@ -45,37 +45,19 @@ export class Core { setData(data: Data) { validateElements(data?.elements || []); this._board.setData(data); - const sharer = this._board.getSharer(); - const currentViewSize = sharer.getActiveViewSizeInfo(); - // const currentScaleInfo = sharer.getActiveScaleInfo(); - const newViewContextSize = calcElementsContextSize(data.elements, { - viewWidth: currentViewSize.width, - viewHeight: currentViewSize.height, - extend: true - }); - this.resize({ - ...currentViewSize, - ...newViewContextSize - }); - this.scrollX(newViewContextSize.contextX); - this.scrollY(newViewContextSize.contextY); } - scale(num: number) { - this._board.scale(num); + scale(opts: { scale: number; point: PointSize }) { + this._board.scale(opts); } - scrollX(num: number) { - this._board.scrollX(num); - } - - scrollY(num: number) { - this._board.scrollY(num); - } + // scroll(num: number) { + // this._board.scroll(num); + // } resize(newViewSize: ViewSizeInfo) { // const sharer = this._board.getSharer(); - // const viewScaleInfo = sharer.getActiveScaleInfo(); + // const viewScaleInfo = sharer.getActiveViewScaleInfo(); this._board.resize(newViewSize); // this._board.scale(viewScaleInfo.scale); // this._board.scrollX(viewScaleInfo.offsetLeft); diff --git a/packages/core/src/middleware/scaler/index.ts b/packages/core/src/middleware/scaler/index.ts index 9520ae4..fa34287 100644 --- a/packages/core/src/middleware/scaler/index.ts +++ b/packages/core/src/middleware/scaler/index.ts @@ -1,4 +1,4 @@ -import type { Point, BoardMiddleware, PointWatcherEvent, BoardWatherWheelXEvent, BoardWatherWheelYEvent } from '@idraw/types'; +import type { PointSize, BoardMiddleware, ViewScaleInfo, ViewSizeInfo } from '@idraw/types'; export const MiddlewareScaler: BoardMiddleware = (opts) => { const key = 'SCALE'; @@ -7,16 +7,17 @@ export const MiddlewareScaler: BoardMiddleware = (opts) => { mode: key, isDefault: true, wheelScale(e) { - // console.log(' wheelScale ============= ', e); - const { deltaY } = e; - const { scale } = sharer.getActiveScaleInfo(); + const { deltaY, point } = e; + const { scale } = sharer.getActiveViewScaleInfo(); + let newScaleNum = scale; if (deltaY < 0) { - viewer.scale(scale * 1.1); - viewer.drawFrame(); + newScaleNum = scale * 1.1; } else if (deltaY > 0) { - viewer.scale(scale * 0.9); - viewer.drawFrame(); + newScaleNum = scale * 0.9; } + const { moveX, moveY } = viewer.scale({ scale: newScaleNum, point }); + viewer.scroll({ moveX, moveY }); + viewer.drawFrame(); } }; }; diff --git a/packages/core/src/middleware/scroller/config.ts b/packages/core/src/middleware/scroller/config.ts new file mode 100644 index 0000000..6aa1c8d --- /dev/null +++ b/packages/core/src/middleware/scroller/config.ts @@ -0,0 +1,6 @@ +export const key = 'SCROLL'; +export const keyXThumbRect = Symbol(`${key}_xThumbRect`); +export const keyYThumbRect = Symbol(`${key}_yThumbRect`); +export const keyPrevPoint = Symbol(`${key}_prevPoint`); +export const keyActivePoint = Symbol(`${key}_activePoint`); +export const keyActiveThumbType = Symbol(`${key}_activeThumbType`); diff --git a/packages/core/src/middleware/scroller/index.ts b/packages/core/src/middleware/scroller/index.ts index 6ea7247..90756ac 100644 --- a/packages/core/src/middleware/scroller/index.ts +++ b/packages/core/src/middleware/scroller/index.ts @@ -1,92 +1,120 @@ +import type { ElementSize } from '@idraw/types'; import type { Point, BoardMiddleware, PointWatcherEvent, BoardWatherWheelXEvent, BoardWatherWheelYEvent } from '@idraw/types'; -import { drawScroller, isPointInScrollbar, calcScrollerInfo } from './util'; -import type { ScrollbarThumbType } from './util'; +import { drawScroller, isPointInScrollThumb } from './util'; +// import type { ScrollbarThumbType } from './util'; +import { key, keyXThumbRect, keyYThumbRect, keyPrevPoint, keyActivePoint, keyActiveThumbType } from './config'; export const MiddlewareScroller: BoardMiddleware = (opts) => { - const key = 'SCROLL'; const { viewer, viewContent, sharer } = opts; const { helperContext } = viewContent; - // viewer.drawFrame(); + sharer.setSharedStorage(keyXThumbRect, null); // null | ElementSize + sharer.setSharedStorage(keyYThumbRect, null); // null | ElementSize - let activeThumbType: ScrollbarThumbType | null = null; + // viewer.drawFrame(); + const clear = () => { + sharer.setSharedStorage(keyPrevPoint, null); // null | Point; + sharer.setSharedStorage(keyActivePoint, null); // null | Point; + sharer.setSharedStorage(keyActiveThumbType, null); // null | 'X' | 'Y' + }; + + clear(); + + // let activeThumbType: ScrollbarThumbType | null = null; const scrollX = (p: Point) => { - const scrollerInfo = calcScrollerInfo(sharer.getActiveScaleInfo(), sharer.getActiveViewSizeInfo()); - const offsetLeft = sharer.getActiveStorage('offsetLeft'); - const moveX = p.x - (scrollerInfo.translateX + scrollerInfo.xSize / 2); - viewer.scrollX(offsetLeft - moveX); - viewer.drawFrame(); + const prevPoint: null | Point = sharer.getSharedStorage(keyPrevPoint); + if (prevPoint) { + const { offsetLeft, offsetRight } = sharer.getActiveViewScaleInfo(); + const { width } = sharer.getActiveViewSizeInfo(); + const thumbMoveX = -(p.x - prevPoint.x); + const totalWidth = width + Math.abs(offsetLeft) + Math.abs(offsetRight); + const moveX = (thumbMoveX * totalWidth) / width; + viewer.scroll({ moveX }); + viewer.drawFrame(); + } }; const scrollY = (p: Point) => { - const scrollerInfo = calcScrollerInfo(sharer.getActiveScaleInfo(), sharer.getActiveViewSizeInfo()); - const offsetTop = sharer.getActiveStorage('offsetTop'); - const moveY = p.y - (scrollerInfo.translateY + scrollerInfo.ySize / 2); - viewer.scrollY(offsetTop - moveY); - viewer.drawFrame(); + const prevPoint: null | Point = sharer.getSharedStorage(keyPrevPoint); + if (prevPoint) { + const { offsetTop, offsetBottom } = sharer.getActiveViewScaleInfo(); + const { height } = sharer.getActiveViewSizeInfo(); + const thumbMoveY = -(p.y - prevPoint.y); + const totalHeight = height + Math.abs(offsetTop) + Math.abs(offsetBottom); + const moveY = (thumbMoveY * totalHeight) / height; + viewer.scroll({ moveY }); + viewer.drawFrame(); + } + }; + + const getThumbType = (p: Point) => { + return isPointInScrollThumb(helperContext, p, { + xThumbRect: sharer.getSharedStorage(keyXThumbRect), + yThumbRect: sharer.getSharedStorage(keyYThumbRect) + }); }; return { mode: key, - hover: (e: PointWatcherEvent) => { - const { point } = e; - const thumbType = isPointInScrollbar(helperContext, point, sharer.getActiveViewSizeInfo()); - if (thumbType === 'X' || thumbType === 'Y') { - return false; - } - }, + // hover: (e: PointWatcherEvent) => { + // const { point } = e; + // const thumbType = getThumbType(point); + // if (thumbType === 'X' || thumbType === 'Y') { + // return false; + // } + // }, wheelX: (e: BoardWatherWheelXEvent) => { - const offsetLeft = sharer.getActiveStorage('offsetLeft'); - if ((e.deltaX >= 0 || e.deltaX < 0) && offsetLeft <= 0) { - viewer.scrollX(offsetLeft - e.deltaX); + if (e.deltaX >= 0 || e.deltaX < 0) { + viewer.scroll({ moveX: 0 - e.deltaX }); viewer.drawFrame(); } }, wheelY: (e: BoardWatherWheelYEvent) => { - const offsetTop = sharer.getActiveStorage('offsetTop'); - if ((e.deltaY >= 0 || e.deltaY < 0) && offsetTop <= 0) { - viewer.scrollY(offsetTop - e.deltaY); + if (e.deltaY >= 0 || e.deltaY < 0) { + viewer.scroll({ moveY: 0 - e.deltaY }); viewer.drawFrame(); } }, pointStart: (e: PointWatcherEvent) => { const { point } = e; - const thumbType = isPointInScrollbar(helperContext, point, sharer.getActiveViewSizeInfo()); + const thumbType = getThumbType(point); if (thumbType === 'X' || thumbType === 'Y') { - activeThumbType = thumbType; - if (thumbType === 'X') { - scrollX(point); - } else if (thumbType === 'Y') { - scrollY(point); - } + sharer.setSharedStorage(keyActiveThumbType, thumbType); + sharer.setSharedStorage(keyPrevPoint, point); return false; } }, pointMove: (e: PointWatcherEvent) => { const { point } = e; + const activeThumbType = sharer.getSharedStorage(keyActiveThumbType); if (activeThumbType === 'X' || activeThumbType === 'Y') { + sharer.setSharedStorage(keyActivePoint, point); if (activeThumbType === 'X') { scrollX(point); } else if (activeThumbType === 'Y') { scrollY(point); } + sharer.setSharedStorage(keyPrevPoint, point); return false; } }, pointEnd: (e: PointWatcherEvent) => { - const { point } = e; - if (activeThumbType === 'X' || activeThumbType === 'Y') { - if (activeThumbType === 'X') { - scrollX(point); - } else if (activeThumbType === 'Y') { - scrollY(point); - } - activeThumbType = null; - return false; - } + // const { point } = e; + // if (activeThumbType === 'X' || activeThumbType === 'Y') { + // if (activeThumbType === 'X') { + // scrollX(point); + // } else if (activeThumbType === 'Y') { + // scrollY(point); + // } + // activeThumbType = null; + // return false; + // } + clear(); }, beforeDrawFrame({ snapshot }) { - drawScroller(helperContext, { snapshot }); + const { xThumbRect, yThumbRect } = drawScroller(helperContext, { snapshot }); + sharer.setSharedStorage(keyXThumbRect, xThumbRect); + sharer.setSharedStorage(keyYThumbRect, yThumbRect); } }; }; diff --git a/packages/core/src/middleware/scroller/util.ts b/packages/core/src/middleware/scroller/util.ts index bfb5135..1fce01f 100644 --- a/packages/core/src/middleware/scroller/util.ts +++ b/packages/core/src/middleware/scroller/util.ts @@ -1,4 +1,4 @@ -import type { Point, BoardViewerFrameSnapshot, ViewScaleInfo, ViewSizeInfo, ViewContext2D } from '@idraw/types'; +import type { Point, BoardViewerFrameSnapshot, ViewScaleInfo, ViewSizeInfo, ViewContext2D, ElementSize } from '@idraw/types'; import { getViewScaleInfoFromSnapshot, getViewSizeInfoFromSnapshot } from '@idraw/util'; const minScrollerWidth = 12; @@ -48,40 +48,98 @@ export function isPointInScrollbar(helperContext: ViewContext2D, p: Point, viewS return thumbType; } -export function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo) { +function isPointAtRect(helperContext: ViewContext2D, p: Point, rect: ElementSize): boolean { + const ctx = helperContext; + const { x, y, w, h } = rect; + ctx.beginPath(); + ctx.rect(x, y, w, h); + ctx.closePath(); + if (ctx.isPointInPath(p.x, p.y)) { + return true; + } + return false; +} + +export function isPointInScrollThumb( + helperContext: ViewContext2D, + p: Point, + opts: { + xThumbRect?: ElementSize | null; + yThumbRect?: ElementSize | null; + } +): ScrollbarThumbType | null { + let thumbType: ScrollbarThumbType | null = null; + const { xThumbRect, yThumbRect } = opts; + if (xThumbRect && isPointAtRect(helperContext, p, xThumbRect)) { + thumbType = 'X'; + } else if (yThumbRect && isPointAtRect(helperContext, p, yThumbRect)) { + thumbType = 'Y'; + } + return thumbType; +} + +function calcScrollerInfo(viewScaleInfo: ViewScaleInfo, viewSizeInfo: ViewSizeInfo) { const { width, height } = viewSizeInfo; const { offsetTop, offsetBottom, offsetLeft, offsetRight } = viewScaleInfo; const sliderMinSize = scrollerLineWidth * 2.5; const lineSize = scrollerLineWidth; + let xSize = 0; let ySize = 0; - if (offsetLeft <= 0 && offsetRight <= 0) { - xSize = Math.max(sliderMinSize, width - (Math.abs(offsetLeft) + Math.abs(offsetRight))); - if (xSize >= width) xSize = 0; + xSize = Math.max(sliderMinSize, width - (Math.abs(offsetLeft) + Math.abs(offsetRight))); + if (xSize >= width) { + xSize = width; } - if (offsetTop <= 0 || offsetBottom <= 0) { - ySize = Math.max(sliderMinSize, height - (Math.abs(offsetTop) + Math.abs(offsetBottom))); - if (ySize >= height) ySize = 0; + ySize = Math.max(sliderMinSize, height - (Math.abs(offsetTop) + Math.abs(offsetBottom))); + if (ySize >= height) { + ySize = width; } - let translateX = 0; - if (xSize > 0) { + const xStart = lineSize / 2; + const xEnd = width - xSize - lineSize; + let translateX = xStart; + + if (offsetLeft > 0) { + translateX = xStart; + } 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); } - let translateY = 0; - if (ySize > 0) { + const yStart = lineSize / 2; + const yEnd = width - xSize - lineSize; + let translateY = yStart; + if (offsetTop > 0) { + translateY = yStart; + } 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); } + const xThumbRect: ElementSize = { + x: translateX, + y: height - lineSize, + w: xSize, + h: lineSize + }; + const yThumbRect: ElementSize = { + x: width - lineSize, + y: translateY, + w: lineSize, + h: ySize + }; const scrollWrapper = { lineSize, xSize, ySize, translateY, translateX, - color: '#0000007A' + color: '#0000007A', + xThumbRect, + yThumbRect }; return scrollWrapper; } @@ -143,6 +201,7 @@ function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: V const { viewScaleInfo, viewSizeInfo } = opts; const { width, height } = viewSizeInfo; const wrapper = calcScrollerInfo(viewScaleInfo, viewSizeInfo); + const { xThumbRect, yThumbRect } = wrapper; if (wrapper.xSize > 0) { if (scrollConfig.showBackground === true) { ctx.globalAlpha = scrollerAlpha; @@ -155,10 +214,7 @@ function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: V // x-slider drawScrollerThumb(ctx, { axis: 'X', - x: wrapper.translateX, - y: height - wrapper.lineSize, - w: wrapper.xSize, - h: wrapper.lineSize, + ...xThumbRect, r: wrapper.lineSize / 2, color: wrapper.color }); @@ -176,21 +232,23 @@ function drawScrollerInfo(helperContext: ViewContext2D, opts: { viewScaleInfo: V // y-slider drawScrollerThumb(ctx, { axis: 'Y', - x: width - wrapper.lineSize, - y: wrapper.translateY, - w: wrapper.lineSize, - h: wrapper.ySize, + ...yThumbRect, r: wrapper.lineSize / 2, color: wrapper.color }); } ctx.globalAlpha = 1; + return { + xThumbRect, + yThumbRect + }; } export function drawScroller(ctx: ViewContext2D, opts: { snapshot: BoardViewerFrameSnapshot }) { const { snapshot } = opts; const viewSizeInfo = getViewSizeInfoFromSnapshot(snapshot); const viewScaleInfo = getViewScaleInfoFromSnapshot(snapshot); - drawScrollerInfo(ctx, { viewSizeInfo, viewScaleInfo }); + const { xThumbRect, yThumbRect } = drawScrollerInfo(ctx, { viewSizeInfo, viewScaleInfo }); + return { xThumbRect, yThumbRect }; } diff --git a/packages/core/src/middleware/selector/index.ts b/packages/core/src/middleware/selector/index.ts index 8fb04ac..4af8849 100644 --- a/packages/core/src/middleware/selector/index.ts +++ b/packages/core/src/middleware/selector/index.ts @@ -91,7 +91,7 @@ export const MiddlewareSelector: BoardMiddleware = (o const controller = calcElementSizeController(list[0], { groupQueue: sharer.getSharedStorage(keyGroupQueue), controllerSize: 10, - viewScaleInfo: sharer.getActiveScaleInfo() + viewScaleInfo: sharer.getActiveViewScaleInfo() }); sharer.setSharedStorage(keySelectedElementController, controller); } else { @@ -105,7 +105,7 @@ export const MiddlewareSelector: BoardMiddleware = (o calculator, data: sharer.getActiveStorage('data'), selectedElements: getActiveElements(), - viewScaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveViewScaleInfo(), viewSizeInfo: sharer.getActiveViewSizeInfo(), groupQueue: sharer.getSharedStorage(keyGroupQueue), areaSize: null, @@ -140,7 +140,7 @@ export const MiddlewareSelector: BoardMiddleware = (o // in group const isInActiveGroup = isPointInViewActiveGroup(e.point, { ctx: helperContext, - viewScaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveViewScaleInfo(), viewSizeInfo: sharer.getActiveViewSizeInfo(), groupQueue: sharer.getSharedStorage(keyGroupQueue) }); @@ -177,7 +177,7 @@ export const MiddlewareSelector: BoardMiddleware = (o } const selectedElements = getActiveElements(); - const viewScaleInfo = sharer.getActiveScaleInfo(); + const viewScaleInfo = sharer.getActiveViewScaleInfo(); const viewSizeInfo = sharer.getActiveViewSizeInfo(); const target = getPointTarget(e.point, { ...pointTargetBaseOptions(), @@ -212,7 +212,7 @@ export const MiddlewareSelector: BoardMiddleware = (o if ( isPointInViewActiveGroup(e.point, { ctx: helperContext, - viewScaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveViewScaleInfo(), viewSizeInfo: sharer.getActiveViewSizeInfo(), groupQueue }) @@ -238,7 +238,7 @@ export const MiddlewareSelector: BoardMiddleware = (o // not in group const listAreaSize = calcSelectedElementsArea(getActiveElements(), { - viewScaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveViewScaleInfo(), viewSizeInfo: sharer.getActiveViewSizeInfo(), calculator }); @@ -341,9 +341,9 @@ export const MiddlewareSelector: BoardMiddleware = (o const data = sharer.getActiveStorage('data'); const resizeType = sharer.getSharedStorage(keyResizeType); const actionType = sharer.getSharedStorage(keyActionType); - const viewScaleInfo = sharer.getActiveScaleInfo(); + // const viewScaleInfo = sharer.getActiveViewScaleInfo(); const viewSizeInfo = sharer.getActiveViewSizeInfo(); - const { offsetLeft, offsetTop } = viewScaleInfo; + // const { offsetLeft, offsetTop } = viewScaleInfo; let needDrawFrame = false; prevPoint = null; if (actionType === 'resize' && resizeType) { @@ -358,7 +358,7 @@ export const MiddlewareSelector: BoardMiddleware = (o start, end, calculator, - viewScaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveViewScaleInfo(), viewSizeInfo: sharer.getActiveViewSizeInfo() }); @@ -373,7 +373,11 @@ export const MiddlewareSelector: BoardMiddleware = (o sharer.setSharedStorage(keyActionType, 'drag-list-end'); needDrawFrame = true; } else if (data) { - const result = calculator.getPointElement(e.point, { data, viewScaleInfo: sharer.getActiveScaleInfo(), viewSizeInfo: sharer.getActiveViewSizeInfo() }); + const result = calculator.getPointElement(e.point, { + data, + viewScaleInfo: sharer.getActiveViewScaleInfo(), + viewSizeInfo: sharer.getActiveViewSizeInfo() + }); if (result.element) { sharer.setSharedStorage(keyActionType, 'select'); needDrawFrame = true; @@ -396,8 +400,9 @@ export const MiddlewareSelector: BoardMiddleware = (o sharer.setActiveStorage('contextY', viewInfo.contextSize.contextY); sharer.setActiveStorage('contextHeight', viewInfo.contextSize.contextHeight); sharer.setActiveStorage('contextWidth', viewInfo.contextSize.contextWidth); - viewer.scrollX(offsetLeft + viewInfo.changeContextLeft); - viewer.scrollY(offsetTop + viewInfo.changeContextTop); + // TODO + // viewer.scrollX(offsetLeft + viewInfo.changeContextLeft); + // viewer.scrollY(offsetTop + viewInfo.changeContextTop); } viewer.drawFrame(); }; @@ -464,7 +469,7 @@ export const MiddlewareSelector: BoardMiddleware = (o drawArea(helperContext, { start: areaStart, end: areaEnd }); } else if ((['drag-list', 'drag-list-end'] as ActionType[]).includes(actionType)) { const listAreaSize = calcSelectedElementsArea(getActiveElements(), { - viewScaleInfo: sharer.getActiveScaleInfo(), + viewScaleInfo: sharer.getActiveViewScaleInfo(), viewSizeInfo: sharer.getActiveViewSizeInfo(), calculator }); diff --git a/packages/types/src/lib/board.ts b/packages/types/src/lib/board.ts index dfced23..2d30171 100644 --- a/packages/types/src/lib/board.ts +++ b/packages/types/src/lib/board.ts @@ -1,4 +1,4 @@ -import type { Point } from './point'; +import type { Point, PointSize } from './point'; import type { ViewContent, ViewCalculator, ViewScaleInfo, ViewSizeInfo } from './view'; import type { UtilEventEmitter } from './util'; import type { ActiveStore, StoreSharer } from './store'; @@ -23,7 +23,7 @@ export interface BoardWatherDrawFrameEvent> snapshot: BoardViewerFrameSnapshot; } -export type BoardWatherScaleEvent = ViewScaleInfo; +export type BoardWatherScaleEvent = { scale: number }; export type BoardWatherScrollXEvent = ViewScaleInfo; @@ -41,7 +41,7 @@ export interface BoardWatcherEventMap = any> wheelX: BoardWatherWheelXEvent; wheelY: BoardWatherWheelYEvent; wheelScale: BoardWatherWheelScaleEvent; - scale: BoardWatherScaleEvent; + // scale: BoardWatherScaleEvent; scrollX: BoardWatherScrollXEvent; scrollY: BoardWatherScrollYEvent; resize: BoardWatherResizeEvent; @@ -67,7 +67,7 @@ export interface BoardMiddlewareObject = any wheelY?: (e: BoardWatcherEventMap['wheelY']) => void | boolean; wheelScale?: (e: BoardWatcherEventMap['wheelScale']) => void | boolean; - scale?: (e: BoardWatcherEventMap['scale']) => void | boolean; + // scale?: (e: BoardWatcherEventMap['scale']) => void | boolean; scrollX?: (e: BoardWatcherEventMap['scrollX']) => void | boolean; scrollY?: (e: BoardWatcherEventMap['scrollY']) => void | boolean; resize?: (e: BoardWatcherEventMap['resize']) => void | boolean; @@ -113,9 +113,10 @@ export interface BoardViewerOptions { export interface BoardViewer extends UtilEventEmitter { drawFrame(): void; - scale(num: number): ViewScaleInfo; - scrollX(num: number): ViewScaleInfo; - scrollY(num: number): ViewScaleInfo; + scale(opts: { scale: number; point: PointSize }): { moveX: number; moveY: number }; + scroll(opts: { moveX?: number; moveY?: number }): ViewScaleInfo; + // scrollX(num: number): ViewScaleInfo; + // scrollY(num: number): ViewScaleInfo; resize(viewSize: Partial): ViewSizeInfo; } diff --git a/packages/types/src/lib/store.ts b/packages/types/src/lib/store.ts index fe68c5a..e641b79 100644 --- a/packages/types/src/lib/store.ts +++ b/packages/types/src/lib/store.ts @@ -14,8 +14,8 @@ export interface StoreSharer = any> { setSharedStorage(key: K, storage: S[K]): void; getSharedStoreSnapshot(): Record; - getActiveScaleInfo(): ViewScaleInfo; - setActiveScaleInfo(viewScaleInfo: ViewScaleInfo): void; + getActiveViewScaleInfo(): ViewScaleInfo; + setActiveViewScaleInfo(viewScaleInfo: ViewScaleInfo): void; setActiveViewSizeInfo(size: ViewSizeInfo): void; getActiveViewSizeInfo(): ViewSizeInfo; } diff --git a/packages/util/src/lib/view-calc.ts b/packages/util/src/lib/view-calc.ts index 98b17af..64a0917 100644 --- a/packages/util/src/lib/view-calc.ts +++ b/packages/util/src/lib/view-calc.ts @@ -2,86 +2,41 @@ import { Point, PointSize, Data, ViewScaleInfo, ViewSizeInfo, Element, ElementTy import { rotateElementVertexes } from './rotate'; import { checkRectIntersect } from './rect'; -export function viewScale(opts: { num: number; prevViewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): ViewScaleInfo { - const { num: scale, prevViewScaleInfo, viewSizeInfo } = opts; - const { width, height, contextWidth, contextHeight } = viewSizeInfo; - let offsetLeft = 0; - let offsetRight = 0; - let offsetTop = 0; - let offsetBottom = 0; - - if (contextWidth * scale < width) { - offsetLeft = offsetRight = (width - contextWidth * scale) / 2; - } else if (contextWidth * scale > width) { - if (prevViewScaleInfo.offsetLeft < 0) { - offsetLeft = (prevViewScaleInfo.offsetLeft / prevViewScaleInfo.scale) * scale; - offsetRight = 0 - (contextWidth * scale - width - Math.abs(offsetLeft)); - } - } - - if (contextHeight * scale < height) { - offsetTop = offsetBottom = (height - contextHeight * scale) / 2; - } else if (contextHeight * scale > height) { - if (prevViewScaleInfo.offsetTop < 0) { - offsetTop = (prevViewScaleInfo.offsetTop / prevViewScaleInfo.scale) * scale; - offsetBottom = 0 - (contextHeight * scale - height - Math.abs(offsetTop)); - } - } - +export function viewScale(opts: { scale: number; point: PointSize; viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): { + moveX: number; + moveY: number; +} { + const { scale, point, viewScaleInfo: prevViewScaleInfo } = opts; + const { offsetLeft, offsetTop } = prevViewScaleInfo; + const scaleDiff = scale / prevViewScaleInfo.scale; + const x0 = point.x; + const y0 = point.y; + const moveX = x0 - x0 * scaleDiff + (offsetLeft * scaleDiff - offsetLeft); + const moveY = y0 - y0 * scaleDiff + (offsetTop * scaleDiff - offsetTop); return { - scale, - offsetTop, - offsetLeft, - offsetRight, - offsetBottom + moveX, + moveY }; } export function viewScroll(opts: { moveX?: number; moveY?: number; viewScaleInfo: ViewScaleInfo; viewSizeInfo: ViewSizeInfo }): ViewScaleInfo { - const { moveX, moveY, viewScaleInfo, viewSizeInfo } = opts; - const scale = viewScaleInfo.scale; + const { moveX = 0, moveY = 0, viewScaleInfo, viewSizeInfo } = opts; + + const { scale } = viewScaleInfo; const { width, height, contextWidth, contextHeight } = viewSizeInfo; let offsetLeft = viewScaleInfo.offsetLeft; let offsetRight = viewScaleInfo.offsetRight; let offsetTop = viewScaleInfo.offsetTop; let offsetBottom = viewScaleInfo.offsetBottom; - if (moveX !== undefined && (moveX > 0 || moveX <= 0)) { - if (contextWidth * scale < width) { - offsetLeft = offsetRight = (width - contextWidth * scale) / 2; - } else if (contextWidth * scale > width) { - if (offsetLeft + moveX >= 0) { - offsetLeft = 0; - offsetRight = width - contextWidth * scale; - } else if (offsetLeft + moveX < width - contextWidth * scale) { - offsetLeft = width - contextWidth * scale; - offsetRight = 0; - } else { - offsetLeft += moveX; - offsetRight = width - contextWidth * scale - offsetLeft; - } - } else { - offsetLeft = offsetRight = 0; - } - } - if (moveY !== undefined && (moveY > 0 || moveY <= 0)) { - if (contextHeight * scale < height) { - offsetTop = offsetBottom = (height - contextHeight * scale) / 2; - } else if (contextHeight * scale > height) { - if (offsetTop + moveY >= 0) { - offsetTop = 0; - offsetBottom = height - contextHeight * scale; - } else if (offsetTop + moveY < height - contextHeight * scale) { - offsetTop = height - contextHeight * scale; - offsetBottom = 0; - } else { - offsetTop += moveY; - offsetBottom = height - contextHeight * scale - offsetTop; - } - } else { - offsetTop = offsetBottom = 0; - } - } + offsetLeft += moveX; + offsetTop += moveY; + + const w = contextWidth * scale; + const h = contextHeight * scale; + + offsetRight = width - (w + offsetLeft); + offsetBottom = height - (h + offsetTop); return { scale,