2022-02-16 11:20:17 +08:00

187 lines
4.1 KiB
TypeScript

import { obx, computed, makeObservable } from '@alilc/lowcode-editor-core';
import { Point, ScrollTarget } from '../designer';
import { AutoFit, IViewport } from '../simulator';
export default class Viewport implements IViewport {
@obx.ref private rect?: DOMRect;
private _bounds?: DOMRect;
get bounds(): DOMRect {
if (this._bounds) {
return this._bounds;
}
this._bounds = this.viewportElement!.getBoundingClientRect();
requestAnimationFrame(() => {
this._bounds = undefined;
});
return this._bounds;
}
get contentBounds(): DOMRect {
const { bounds, scale } = this;
return new DOMRect(0, 0, bounds.width / scale, bounds.height / scale);
}
private viewportElement?: HTMLElement;
constructor() {
makeObservable(this);
}
mount(viewportElement: HTMLElement | null) {
if (!viewportElement || this.viewportElement === viewportElement) {
return;
}
this.viewportElement = viewportElement;
this.touch();
}
touch() {
if (this.viewportElement) {
this.rect = this.bounds;
}
}
@computed get height(): number {
if (!this.rect) {
return 600;
}
return this.rect.height;
}
set height(newHeight: number) {
this._contentHeight = newHeight / this.scale;
if (this.viewportElement) {
this.viewportElement.style.height = `${newHeight}px`;
this.touch();
}
}
@computed get width(): number {
if (!this.rect) {
return 1000;
}
return this.rect.width;
}
set width(newWidth: number) {
this._contentWidth = newWidth / this.scale;
if (this.viewportElement) {
this.viewportElement.style.width = `${newWidth}px`;
this.touch();
}
}
@obx.ref private _scale = 1;
/**
* 缩放比例
*/
@computed get scale(): number {
return this._scale;
}
set scale(newScale: number) {
if (isNaN(newScale) || newScale <= 0) {
throw new Error(`invalid new scale "${newScale}"`);
}
this._scale = newScale;
this._contentWidth = this.width / this.scale;
this._contentHeight = this.height / this.scale;
}
@obx.ref private _contentWidth: number | AutoFit = AutoFit;
@obx.ref private _contentHeight: number | AutoFit = AutoFit;
@computed get contentHeight(): number | AutoFit {
return this._contentHeight;
}
set contentHeight(newContentHeight: number | AutoFit) {
this._contentHeight = newContentHeight;
}
@computed get contentWidth(): number | AutoFit {
return this._contentWidth;
}
set contentWidth(val: number | AutoFit) {
this._contentWidth = val;
}
@obx.ref private _scrollX = 0;
@obx.ref private _scrollY = 0;
get scrollX() {
return this._scrollX;
}
get scrollY() {
return this._scrollY;
}
private _scrollTarget?: ScrollTarget;
/**
* 滚动对象
*/
get scrollTarget(): ScrollTarget | undefined {
return this._scrollTarget;
}
@obx private _scrolling = false;
get scrolling(): boolean {
return this._scrolling;
}
setScrollTarget(target: Window) {
const scrollTarget = new ScrollTarget(target);
this._scrollX = scrollTarget.left;
this._scrollY = scrollTarget.top;
let scrollTimer: any;
target.addEventListener('scroll', () => {
this._scrollX = scrollTarget.left;
this._scrollY = scrollTarget.top;
this._scrolling = true;
if (scrollTimer) {
clearTimeout(scrollTimer);
}
scrollTimer = setTimeout(() => {
this._scrolling = false;
}, 80);
});
target.addEventListener('resize', () => this.touch());
this._scrollTarget = scrollTarget;
}
toGlobalPoint(point: Point): Point {
if (!this.viewportElement) {
return point;
}
const rect = this.bounds;
return {
clientX: point.clientX * this.scale + rect.left,
clientY: point.clientY * this.scale + rect.top,
};
}
toLocalPoint(point: Point): Point {
if (!this.viewportElement) {
return point;
}
const rect = this.bounds;
return {
clientX: (point.clientX - rect.left) / this.scale,
clientY: (point.clientY - rect.top) / this.scale,
};
}
}