382 lines
8.2 KiB
TypeScript
382 lines
8.2 KiB
TypeScript
import { Emitter } from "@Model/Emitter";
|
|
import { ResizeObserver as Polyfill } from '@juggle/resize-observer';
|
|
|
|
export { GLCanvas, GLCanvasOption };
|
|
|
|
/**
|
|
* GLCanvas 的设置
|
|
*/
|
|
interface GLCanvasOption {
|
|
|
|
/**
|
|
* 分辨率自适应
|
|
*/
|
|
autoResize?: boolean;
|
|
|
|
/**
|
|
* 是否监听鼠标事件
|
|
*/
|
|
mouseEvent?: boolean;
|
|
|
|
/**
|
|
* 调试时使用
|
|
* 打印事件
|
|
*/
|
|
eventLog?: boolean,
|
|
|
|
/**
|
|
* 节点类名
|
|
*/
|
|
className?: string
|
|
}
|
|
|
|
type GLCanvasEvent = {
|
|
mouseup: GLCanvas,
|
|
mousemove: GLCanvas,
|
|
mousedown: GLCanvas,
|
|
mousewheel: GLCanvas,
|
|
resize: GLCanvas,
|
|
};
|
|
|
|
/**
|
|
* 封装 GLCanvas
|
|
* 管理封装画布的功能属性
|
|
* 监听画布事件
|
|
*
|
|
* @event resize 画布缓冲区大小改变
|
|
* @event mousemove 鼠标移动
|
|
* @event mouseup 鼠标抬起
|
|
* @event mousedown 鼠标按下
|
|
* @event resize 大小变化
|
|
*/
|
|
class GLCanvas extends Emitter<GLCanvasEvent> {
|
|
|
|
/**
|
|
* HTML节点
|
|
*/
|
|
private readonly canvas:HTMLCanvasElement;
|
|
private readonly div:HTMLDivElement;
|
|
|
|
/**
|
|
* 获取节点
|
|
*/
|
|
public get dom(){
|
|
return this.div;
|
|
}
|
|
|
|
public get can(){
|
|
return this.canvas;
|
|
}
|
|
|
|
/**
|
|
* 像素分辨率
|
|
*/
|
|
public pixelRatio:number = devicePixelRatio ?? 1;
|
|
|
|
/**
|
|
* 帧缓冲区宽度
|
|
*/
|
|
public get width():number {
|
|
return this.canvas.width
|
|
}
|
|
|
|
/**
|
|
* 帧缓冲区高度
|
|
*/
|
|
public get height():number {
|
|
return this.canvas.height
|
|
}
|
|
|
|
/**
|
|
* 画布宽度
|
|
*/
|
|
public get offsetWidth():number {
|
|
return this.canvas.offsetWidth
|
|
}
|
|
|
|
/**
|
|
* 画布高度
|
|
*/
|
|
public get offsetHeight():number {
|
|
return this.canvas.offsetHeight
|
|
}
|
|
|
|
/**
|
|
* 缩放 X
|
|
*/
|
|
public get scaleX():number {
|
|
return this.canvas.width / this.canvas.offsetWidth
|
|
}
|
|
|
|
/**
|
|
* 缩放 Y
|
|
*/
|
|
public get scaleY():number {
|
|
return this.canvas.height / this.canvas.offsetHeight
|
|
}
|
|
|
|
/**
|
|
* 分辨率 (画布宽高比)
|
|
*/
|
|
public get ratio():number {
|
|
return this.canvas.offsetWidth / this.canvas.offsetHeight;
|
|
}
|
|
|
|
/**
|
|
* 缓存判断是否要设置 canvas 大小
|
|
*/
|
|
private readonly offsetFlg:[number,number] = [NaN, NaN];
|
|
|
|
/**
|
|
* 画布大小适应到 css 大小
|
|
*/
|
|
public resize(){
|
|
|
|
if (
|
|
this.offsetWidth !== this.offsetFlg[0] ||
|
|
this.offsetHeight !== this.offsetFlg[1]
|
|
) {
|
|
|
|
// 缓存记录
|
|
this.offsetFlg[0] = this.offsetWidth;
|
|
this.offsetFlg[1] = this.offsetHeight;
|
|
|
|
// 重置缓冲区
|
|
this.canvas.width = this.offsetWidth * this.pixelRatio;
|
|
this.canvas.height = this.offsetHeight * this.pixelRatio;
|
|
|
|
this.emit("resize", this);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 鼠标 X 坐标
|
|
*/
|
|
public mouseX:number = 0;
|
|
|
|
/**
|
|
* 鼠标 Y 坐标
|
|
*/
|
|
public mouseY:number = 0;
|
|
|
|
/**
|
|
* 鼠标相对 X 坐标
|
|
*/
|
|
public mouseUvX:number = 0;
|
|
|
|
/**
|
|
* 鼠标相对 Y 坐标
|
|
*/
|
|
public mouseUvY:number = 0;
|
|
|
|
/**
|
|
* 鼠标 GLX 坐标
|
|
*/
|
|
public mouseGlX:number = 0;
|
|
|
|
/**
|
|
* 鼠标 GLY 坐标
|
|
*/
|
|
public mouseGlY:number = 0;
|
|
|
|
/**
|
|
* 鼠标 X 变化量
|
|
*/
|
|
public mouseMotionX:number = 0;
|
|
|
|
/**
|
|
* 鼠标 Y 变化量
|
|
*/
|
|
public mouseMotionY:number = 0;
|
|
|
|
/**
|
|
* 缓存鼠标位置
|
|
*/
|
|
private readonly mouseFlg:[number, number] = [NaN, NaN];
|
|
|
|
/**
|
|
* 保存鼠标数据
|
|
*/
|
|
private calcMouseData(offsetX:number, offsetY:number):boolean {
|
|
|
|
if (
|
|
offsetX !== this.mouseFlg[0] ||
|
|
offsetY !== this.mouseFlg[1]
|
|
){
|
|
this.mouseX = offsetX;
|
|
this.mouseY = offsetY;
|
|
|
|
this.mouseUvX = offsetX / this.offsetWidth;
|
|
this.mouseUvY = offsetY / this.offsetHeight;
|
|
|
|
this.mouseGlX = this.mouseUvX * 2 - 1;
|
|
this.mouseGlY = - this.mouseUvY * 2 + 1;
|
|
|
|
this.mouseMotionX = offsetX - this.mouseFlg[0];
|
|
this.mouseMotionY = offsetY - this.mouseFlg[1];
|
|
|
|
this.mouseFlg[0] = offsetX;
|
|
this.mouseFlg[1] = offsetY;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private calcMouseDataFromTouchEvent(e:TouchEvent){
|
|
|
|
if (e.touches.length <= 0) return;
|
|
|
|
let offsetX = e.touches[0].clientX - this.canvas.offsetLeft;
|
|
let offsetY = e.touches[0].clientY - this.canvas.offsetTop;
|
|
|
|
return this.calcMouseData(offsetX, offsetY);
|
|
}
|
|
|
|
/**
|
|
* 鼠标触摸触发计数
|
|
*/
|
|
private touchCount:number = 0;
|
|
|
|
/**
|
|
* 鼠标是否按下
|
|
*/
|
|
public mouseDown:boolean = false;
|
|
|
|
/**
|
|
* 鼠标滚动参数
|
|
*/
|
|
public wheelDelta: number = 0;
|
|
|
|
/**
|
|
* 检测 canvas 变化
|
|
*/
|
|
private readonly obs?: ResizeObserver;
|
|
|
|
/**
|
|
* 使用 canvas 节点创建
|
|
* 不适用节点则自动创建
|
|
* @param ele 使用的 canvas节点
|
|
* @param o 设置
|
|
*/
|
|
public constructor(ele?:HTMLCanvasElement, o?:GLCanvasOption){
|
|
|
|
super();
|
|
|
|
let opt = o ?? {};
|
|
|
|
let autoResize = opt.autoResize ?? true;
|
|
let mouseEvent = opt.mouseEvent ?? true;
|
|
let eventLog = opt.eventLog ?? false;
|
|
|
|
|
|
// 获取/创建节点
|
|
this.canvas = ele ?? document.createElement("canvas");
|
|
|
|
this.div = document.createElement("div");
|
|
this.div.className = opt.className ?? "";
|
|
this.div.appendChild(this.canvas);
|
|
|
|
this.canvas.style.width = "100%";
|
|
this.canvas.style.height = "100%";
|
|
|
|
if (autoResize){
|
|
|
|
// 创建观察者
|
|
this.obs = new (window.ResizeObserver || Polyfill)
|
|
((entries:ResizeObserverEntry[])=>{
|
|
|
|
for (let entry of entries) {
|
|
if(entry.target === this.canvas) this.resize();
|
|
}
|
|
|
|
});
|
|
|
|
// 大小监听
|
|
this.obs.observe(this.canvas);
|
|
|
|
}
|
|
|
|
if (mouseEvent) {
|
|
|
|
this.canvas.addEventListener("mouseup",(e)=>{
|
|
|
|
if(this.touchCount >= 2) {
|
|
this.touchCount = 0;
|
|
return;
|
|
}
|
|
|
|
if (eventLog) console.log("GLCanvas: mouseup");
|
|
|
|
this.mouseDown = false;
|
|
this.calcMouseData(e.offsetX, e.offsetY);
|
|
this.emit("mouseup", this);
|
|
|
|
});
|
|
|
|
this.canvas.addEventListener("touchstart",(e)=>{
|
|
|
|
this.touchCount = 1;
|
|
|
|
if (eventLog) console.log("GLCanvas: touchstart");
|
|
|
|
this.mouseDown = true;
|
|
this.calcMouseDataFromTouchEvent(e);
|
|
this.emit("mousedown", this);
|
|
|
|
});
|
|
|
|
this.canvas.addEventListener("mousedown",(e)=>{
|
|
|
|
if(this.touchCount >= 2) return;
|
|
|
|
if (eventLog) console.log("GLCanvas: mousedown");
|
|
|
|
this.mouseDown = true;
|
|
this.calcMouseData(e.offsetX, e.offsetY);
|
|
this.emit("mousedown", this);
|
|
|
|
});
|
|
|
|
this.canvas.addEventListener("touchend",(e)=>{
|
|
|
|
this.touchCount ++;
|
|
|
|
if (eventLog) console.log("GLCanvas: touchend");
|
|
|
|
this.mouseDown = false;
|
|
this.calcMouseDataFromTouchEvent(e);
|
|
this.emit("mouseup", this);
|
|
|
|
});
|
|
|
|
this.canvas.addEventListener("mousemove",(e)=>{
|
|
|
|
if(this.touchCount >= 2) return;
|
|
|
|
if (eventLog) console.log("GLCanvas: mousemove");
|
|
|
|
if (this.calcMouseData(e.offsetX, e.offsetY)) this.emit("mousemove", this);
|
|
|
|
});
|
|
|
|
this.canvas.addEventListener("touchmove",(e)=>{
|
|
|
|
if (eventLog) console.log("GLCanvas: touchmove");
|
|
|
|
if (this.calcMouseDataFromTouchEvent(e)) this.emit("mousemove", this);
|
|
|
|
});
|
|
|
|
this.canvas.addEventListener("mousewheel", (e) => {
|
|
this.wheelDelta = (e as any)?.wheelDelta ?? 0;
|
|
this.emit("mousewheel", this);
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} |