import { SMouseButton, SMouseEvent, SNetUtil, SObject, STouchState } from "@saga-web/base/lib/"; import { SSvgPaintEngine } from "./engines/SSvgPaintEngine"; import { SPoint } from "./types/SPoint"; import { SPainter } from "./SPainter"; /** * Canvas视图基类 * * @author 庞利祥(sybotan@126.com) */ export class SCanvasView extends SObject { private _needDraw = true; /** canvas视图 */ protected readonly canvasView: HTMLCanvasElement; get canvas(): CanvasRenderingContext2D | null { return this.canvasView.getContext("2d") as CanvasRenderingContext2D; } // Get canvas /** 宽度 */ get width(): number { return this.canvasView.width; } // Get width() /** 高度 */ get height(): number { return this.canvasView.height; } // Get height() /** 原点坐标 */ private _origin = new SPoint(); get origin(): SPoint { return this._origin; } // Get origin set origin(v: SPoint) { if (!this.moveable) { return; } this._origin.x = v.x; this._origin.y = v.y; this._needDraw = true; } // Set origin /** 可否进行平移操作 */ moveable = true; /** 缩放比例 */ private _scale: number = 1; get scale(): number { return this._scale; } // Get scale set scale(v: number) { if (!this.scalable) { return; } this._scale = v; this._needDraw = true; } // Set scale /** 可否执行绽放操作 */ scalable = true; /** 鼠标滚轮缩放速度 */ wheelZoom = 1.05; /** 最小缩放比例 */ minScale = 0.000001; /** 最大缩放比例 */ maxScale = 1000000; /** 最后一次更新时间 */ private _lastFrameTime = 0; /** 鼠标中键按下时位置 */ private _midKeyPos = new SPoint(); /** 手执状态 */ private _touchState = STouchState.None; /** 手指拖动点 */ private _touchPoint = new SPoint(); /** 未缩放时二指间距离 */ private _beforeLength = 0; /** 缩放后二指间距离 */ private _afterLength = 0; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // /** * 构造函数 * * @param id 画布对象ID */ constructor(id: string) { super(); this.canvasView = document.getElementById(id) as HTMLCanvasElement; this.bindEvent(this.canvasView); this.requestAnimationFrame(this.loop.bind(this)); } // Constructor /** * 更新视图 */ update(): void { this._needDraw = true; } // Function update() /** * 保存图像 * * @param name 名称 * @param type 图像类型 */ saveImage(name: string, type: string): void { let url = this.canvasView.toDataURL(`image/${type}`); SNetUtil.downLoad(name, url); } // Function saveImage() /** * 图像的URL * * @param type 图像类型 */ imageUrl(type: string): string { return this.canvasView.toDataURL(`image/${type}`); } // Function imageUrl() /** * 视图内容保存为SVG文件 * * @param name 文件名 */ saveSvg(name: string): void { let url = URL.createObjectURL( new Blob([this.svgData()], { type: "text/xml,charset=UTF-8" }) ); SNetUtil.downLoad(name, url); } // Function saveSvg() /** * 视图SVG图形的数据 * * @return URL地址 */ svgData(): string { let engine = new SSvgPaintEngine(this.width, this.height); let painter = new SPainter(engine); this.onDraw(painter); return engine.toSvg(); } // Function saveSvg() /** * 缩放视图时计算视图的位置与缩放比例 * * @param zoom 缩放比例 * @param x0 缩放计算的中心点X坐标 * @param y0 缩放计算的中心点Y坐标 */ scaleByPoint(zoom: number, x0: number, y0: number): void { if (!this.scalable) { return; } let z = zoom; /** * 缩放比例在最小比例和最大比例范围内 */ if (this.scale * zoom >= this.maxScale) { z = this.maxScale / this.scale; this.scale = this.maxScale; } else if (this.scale * zoom <= this.minScale) { z = this.minScale / this.scale; this.scale = this.minScale; } else { this.scale *= zoom; } this.origin.x = x0 - (x0 - this.origin.x) * z; this.origin.y = y0 - (y0 - this.origin.y) * z; } // Function scaleByPoint() //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // /** * 循环 */ protected loop(): void { /** painter对象 */ let ctx = this.canvas; if (null != ctx && this._needDraw) { this._needDraw = false; let painter = new SPainter(this); this.onDraw(painter); } this.requestAnimationFrame(this.loop.bind(this)); } // Function loop() /** * 绘制视图 * * @param painter painter对象 */ protected onDraw(painter: SPainter): void {} // Function onDraw() /** * 鼠标单击事件 * * @param event 事件参数 */ protected onClick(event: MouseEvent): void {} // Function onClick() /** * 鼠标双击事件 * * @param event 事件参数 */ protected onDoubleClick(event: MouseEvent): void {} // Function onDoubleClick() /** * 鼠标按下事件 * * @param event 事件参数 */ protected onMouseDown(event: MouseEvent): void { let se = new SMouseEvent(event); if (se.buttons & SMouseButton.MidButton) { this._midKeyPos.x = se.x; this._midKeyPos.y = se.y; } } // Function onMouseDown() /** * 鼠标移动事件 * * @param event 事件参数 */ protected onMouseMove(event: MouseEvent): void { // 如果可以移动 if (this.moveable) { // 按中键移动 let se = new SMouseEvent(event); if (se.buttons & SMouseButton.MidButton) { this.origin.x += se.x - this._midKeyPos.x; this.origin.y += se.y - this._midKeyPos.y; this._midKeyPos.x = se.x; this._midKeyPos.y = se.y; this.update(); return; } } } // Function onMouseMove() /** * 鼠标松开事件 * * @param event 事件参数 */ protected onMouseUp(event: MouseEvent): void {} // Function onMouseUp() /** * 上下文菜单事件 * * @param event 事件参数 */ protected onContextMenu(event: MouseEvent): void {} // Function onContextMenu() /** * 鼠标滚轮事件 * * @param event 事件参数 */ protected onMouseWheel(event: WheelEvent): void { if (event.deltaY < 0) { this.scaleByPoint(this.wheelZoom, event.offsetX, event.offsetY); } else { this.scaleByPoint(1 / this.wheelZoom, event.offsetX, event.offsetY); } } // Function onMouseWheel() /** * 按键按下事件 * * @param event 事件参数 */ protected onKeyDown(event: KeyboardEvent): void {} // Function onKeydown() /** * 按键press事件 * * @param event 事件参数 */ protected onKeyPress(event: KeyboardEvent): void {} // Function onKeypress() /** * 按键松开事件 * * @param event 事件参数 */ protected onKeyUp(event: KeyboardEvent): void {} // Function onKeyup() /** * 开始触摸事件 * * @param event 事件参数 */ protected onTouchStart(event: TouchEvent): void { let touches = event.touches; if (touches.length == 1) { this._touchPoint.x = event.touches[0].clientX; this._touchPoint.y = event.touches[0].clientY; } if (touches.length == 2) { this._touchState = STouchState.Zoom; this._beforeLength = this.getDistance(event); } } // Function onTouchStart() /** * 触摸移动事件 * * @param event 事件参数 */ protected onTouchMove(event: TouchEvent): void { let touches = event.touches; if (touches.length == 1) { this.origin.x += event.touches[0].clientX - this._touchPoint.x; this.origin.y += event.touches[0].clientY - this._touchPoint.y; this._touchPoint.x = event.touches[0].clientX; this._touchPoint.y = event.touches[0].clientY; } if (touches.length == 2) { this.viewZoom(event); } } // Function onTouchMove() /** * 结束触摸事件 * * @param event 事件参数 */ protected onTouchEnd(event: TouchEvent): void { this._touchState = STouchState.None; } // Function onTouchEnd() /** * View大小变更事件 * * @param event 事件参数 */ protected onResize(event: UIEvent): void {} // Function onClick() /** * 绑定事件 * * @param element 要绑定事件的元素 */ private bindEvent(element: HTMLElement): void { // 绑定鼠标事件 element.onclick = this.onClick.bind(this); element.ondblclick = this.onDoubleClick.bind(this); element.onmousedown = this.onMouseDown.bind(this); element.onmousemove = this.onMouseMove.bind(this); element.onmouseup = this.onMouseUp.bind(this); element.oncontextmenu = this.onContextMenu.bind(this); element.onwheel = this.onMouseWheel.bind(this); // 绑定按键事件 element.onkeydown = this.onKeyDown.bind(this); element.onkeypress = this.onKeyPress.bind(this); element.onkeyup = this.onKeyUp.bind(this); // 触摸事件 element.ontouchstart = this.onTouchStart.bind(this); element.ontouchmove = this.onTouchMove.bind(this); element.ontouchend = this.onTouchEnd.bind(this); // 绑定窗口事件 element.onresize = this.onResize.bind(this); } // Function bindEvent() /** * 启动动画帧(即指定时间执行回调函数) * * @param callback 回调函数 */ private requestAnimationFrame(callback: FrameRequestCallback): number { let currTime = new Date().getTime(); let timeToCall = Math.max(0, 16 - (currTime - this._lastFrameTime)); let id = setTimeout(function(): void { callback(currTime + timeToCall); }, timeToCall); this._lastFrameTime = currTime + timeToCall; return id; } // Function requestAnimationFrame() /** * 缩放视图 * * @param event 触摸事件 */ private viewZoom(event: TouchEvent): boolean { if (this._touchState == STouchState.Zoom) { // 获取两点的距离 this._afterLength = this.getDistance(event); // 变化的长度; let gapLenght = this._afterLength - this._beforeLength; if (Math.abs(gapLenght) > 5 && this._beforeLength != 0.0) { // 求的缩放的比例 let tempScale = this._afterLength / this._beforeLength; let p = this.getMiddlePoint(event); // 重设置 this.scaleByPoint(tempScale, p.x, p.y); this._beforeLength = this._afterLength; } } return true; } // Function onTouchMove() /** * 获取两手指之间的距离 * * @param event 触摸事件 * @return 两手指之间的距离 */ private getDistance(event: TouchEvent): number { if (event.touches.length < 2) { return 0; } let dx = event.touches[0].clientX - event.touches[1].clientX; let dy = event.touches[0].clientY - event.touches[1].clientY; return Math.sqrt(dx * dx + dy * dy); } // Function getDistance() /** * 获得两个手指触摸点的中点坐标 * * @param event * @return 得到视图的x坐标中点 */ private getMiddlePoint(event: TouchEvent): SPoint { return new SPoint( (event.touches[0].clientX + event.touches[1].clientX) / 2, (event.touches[0].clientY + event.touches[1].clientY) / 2 ); } // Function getMiddlePoint() } // Class SCanvasView