334 lines
9.8 KiB
TypeScript
334 lines
9.8 KiB
TypeScript
import * as DrawShare from "./drawShare";
|
||
import {useAddSignal, useDispatchSignal, useRemoveSignal} from "@/hooks";
|
||
import OffScreenCanvasWorker from './offScreenCanvas.worker.ts?worker&url';
|
||
import App from "@/core/app/App.ts";
|
||
|
||
/** ----------------- 离屏渲染中的事件处理--------------------------- **/
|
||
const mouseEventHandler = makeSendPropertiesHandler( [
|
||
'ctrlKey',
|
||
'metaKey',
|
||
'shiftKey',
|
||
'button',
|
||
'pointerType',
|
||
'clientX',
|
||
'clientY',
|
||
'pageX',
|
||
'pageY',
|
||
] );
|
||
const wheelEventHandlerImpl = makeSendPropertiesHandler( [
|
||
'deltaX',
|
||
'deltaY',
|
||
] );
|
||
const keydownEventHandler = makeSendPropertiesHandler( [
|
||
'ctrlKey',
|
||
'metaKey',
|
||
'shiftKey',
|
||
'keyCode',
|
||
] );
|
||
function wheelEventHandler( event, sendFn ) {
|
||
event.preventDefault();
|
||
wheelEventHandlerImpl( event, sendFn );
|
||
}
|
||
|
||
function preventDefaultHandler( event ) {
|
||
event.preventDefault();
|
||
}
|
||
|
||
function copyProperties(src, properties, dst) {
|
||
for ( const name of properties ) {
|
||
dst[ name ] = src[ name ];
|
||
}
|
||
}
|
||
|
||
function makeSendPropertiesHandler( properties ) {
|
||
return function sendProperties(event, sendFn) {
|
||
const data = { type: event.type };
|
||
copyProperties( event, properties, data );
|
||
sendFn( data );
|
||
};
|
||
}
|
||
|
||
function touchEventHandler(event, sendFn) {
|
||
const touches: { pageX:number,pageY:number }[] = [];
|
||
const data = { type: event.type, touches };
|
||
for ( let i = 0; i < event.touches.length; ++ i ) {
|
||
const touch = event.touches[ i ];
|
||
touches.push({
|
||
pageX: touch.pageX,
|
||
pageY: touch.pageY,
|
||
});
|
||
}
|
||
|
||
sendFn( data );
|
||
}
|
||
|
||
// 四个方向键
|
||
const orbitKeys = {
|
||
'37': true, // left
|
||
'38': true, // up
|
||
'39': true, // right
|
||
'40': true, // down
|
||
};
|
||
function filteredKeydownEventHandler(event, sendFn) {
|
||
const { keyCode } = event;
|
||
if (orbitKeys[keyCode]) {
|
||
event.preventDefault();
|
||
keydownEventHandler( event, sendFn );
|
||
}
|
||
}
|
||
/** ----------------- 离屏渲染中的事件end --------------------------- **/
|
||
|
||
let nextProxyId = 0;
|
||
/**
|
||
* 转发 DOM 事件给Worker中的 ElementProxyReceiver
|
||
*/
|
||
class ElementProxy {
|
||
readonly id: number;
|
||
private worker: Worker;
|
||
private element: HTMLElement;
|
||
|
||
constructor(element, worker, eventHandlers) {
|
||
this.id = nextProxyId++;
|
||
this.worker = worker;
|
||
this.element = element;
|
||
|
||
// 注册一个响应元素id
|
||
worker.postMessage({
|
||
type: 'makeProxy',
|
||
data:{
|
||
id: this.id,
|
||
}
|
||
});
|
||
|
||
this.sendSize();
|
||
|
||
// 添加相应事件的监听
|
||
for (const [eventName, handler] of Object.entries(eventHandlers)) {
|
||
element.addEventListener(eventName, (event) => {
|
||
// @ts-ignore
|
||
handler(event, this.sendEvent.bind(this));
|
||
});
|
||
}
|
||
}
|
||
|
||
sendEvent(data){
|
||
this.worker.postMessage({
|
||
type: 'event',
|
||
data:{
|
||
id: this.id,
|
||
data,
|
||
}
|
||
});
|
||
};
|
||
|
||
sendSize() {
|
||
const rect = this.element.getBoundingClientRect();
|
||
this.sendEvent({
|
||
type: 'size',
|
||
left: rect.left,
|
||
top: rect.top,
|
||
width: this.element.clientWidth,
|
||
height: this.element.clientHeight,
|
||
});
|
||
}
|
||
}
|
||
|
||
let cadDialogMoveFn;
|
||
/**
|
||
* dxf对象的查看器类
|
||
* @param {any} data - dxf对象
|
||
* @param {HTMLCanvasElement} canvas - three 画布
|
||
* @param {Number} width - 渲染画布的宽度,以像素为单位
|
||
* @param {Number} height - 渲染画布的高度,以像素为单位
|
||
* @constructor
|
||
*/
|
||
class DxfViewer {
|
||
private worker:Worker | undefined;
|
||
private resizeObserver: ResizeObserver;
|
||
private proxy: ElementProxy | undefined;
|
||
private middleObject:any = new Proxy({},{
|
||
set(target, p: string | symbol, newValue: any): boolean {
|
||
target[p] = newValue;
|
||
|
||
// switch (p) {
|
||
// case "markList":
|
||
// break;
|
||
// }
|
||
|
||
return true;
|
||
}
|
||
})
|
||
|
||
constructor(data: any, canvas: HTMLCanvasElement, width: number, height: number, onComplete?:()=>void) {
|
||
//console.log('dxf data:', data);
|
||
canvas.style.width = `${width}px`;
|
||
canvas.style.height = `${height}px`;
|
||
|
||
const cadFontUrl = new URL(import.meta.env.BASE_URL + 'resource/fonts/Alimama_DongFangDaKai_Regular.ttf', import.meta.url).href;
|
||
|
||
// 检查是否支持离屏canvas
|
||
if (canvas.transferControlToOffscreen) {
|
||
// console.log('OffscreenCanvas supported');
|
||
// 创建Worker进行离屏渲染
|
||
this.worker = new Worker(OffScreenCanvasWorker, {type: 'module'});
|
||
const offscreen = canvas.transferControlToOffscreen();
|
||
offscreen.width = width;
|
||
offscreen.height = height;
|
||
|
||
// 添加鼠标事件的监听
|
||
const eventHandlers = {
|
||
contextmenu: preventDefaultHandler,
|
||
mousedown: mouseEventHandler,
|
||
mousemove: mouseEventHandler,
|
||
mouseup: mouseEventHandler,
|
||
pointerdown: mouseEventHandler,
|
||
pointermove: mouseEventHandler,
|
||
pointerup: mouseEventHandler,
|
||
touchstart: touchEventHandler,
|
||
//touchmove: touchEventHandler,
|
||
touchend: touchEventHandler,
|
||
wheel: wheelEventHandler,
|
||
keydown: filteredKeydownEventHandler,
|
||
};
|
||
this.proxy = new ElementProxy(canvas, this.worker, eventHandlers);
|
||
|
||
this.worker.postMessage({
|
||
type: 'start', data: {
|
||
canvas: offscreen,
|
||
canvasId: this.proxy.id,
|
||
data:data,
|
||
options: {
|
||
bgColor:0x000000,
|
||
contrastColor:0xffffff,
|
||
fontUrl:cadFontUrl
|
||
},
|
||
middleObject:{
|
||
markList: JSON.stringify(App.project.getKey("drawing.markList")),
|
||
selectedRectIndex: App.project.getKey("drawing.selectedRectIndex"),
|
||
}
|
||
}
|
||
}, [offscreen]);
|
||
|
||
this.worker.onmessage = (event) => {
|
||
const data = event.data;
|
||
switch (data.type) {
|
||
case 'complete':
|
||
onComplete && onComplete();
|
||
break;
|
||
case "signal":
|
||
this.handleSignal(data.data)
|
||
break;
|
||
case "style":
|
||
canvas.style[data.data.key] = data.data.value;
|
||
break;
|
||
case "middle":
|
||
this.middleObject[data.data.key] = data.data.value;
|
||
break;
|
||
}
|
||
}
|
||
|
||
cadDialogMoveFn = this.proxy.sendSize.bind(this.proxy);
|
||
useAddSignal("cadViewerResize",cadDialogMoveFn)
|
||
}else{
|
||
canvas.width = width;
|
||
canvas.height = height;
|
||
|
||
//不支持离屏渲染
|
||
DrawShare.main({
|
||
canvas,
|
||
inputElement:canvas,
|
||
data:data,
|
||
onComplete:onComplete,
|
||
signal:this.handleSignal,
|
||
options: {
|
||
bgColor:0x000000,
|
||
contrastColor:0xffffff,
|
||
fontUrl:cadFontUrl
|
||
},
|
||
middleObject:this.middleObject
|
||
});
|
||
}
|
||
|
||
// 监听窗口变化
|
||
this.resizeObserver = new ResizeObserver(entries => {
|
||
canvas.style.width = `${entries[0].contentRect.width}px`;
|
||
canvas.style.height = `${entries[0].contentRect.height}px`;
|
||
|
||
const data = {
|
||
width:entries[0].contentRect.width,
|
||
height:entries[0].contentRect.height
|
||
}
|
||
|
||
if(this.worker){
|
||
this.worker.postMessage({
|
||
type:"resize",
|
||
data
|
||
})
|
||
|
||
this.proxy?.sendSize();
|
||
}else{
|
||
DrawShare.resize(data)
|
||
}
|
||
});
|
||
this.resizeObserver.observe(canvas.parentElement as HTMLDivElement);
|
||
}
|
||
|
||
// 触发signal
|
||
handleSignal(args){
|
||
const {type,name,data} = args;
|
||
switch (type) {
|
||
case "dispatch":
|
||
useDispatchSignal(name,...data)
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 调用drawShare中的方法,data为传入的参数,对象展示
|
||
callMethod(methodName:string, data:any = {}){
|
||
if(this.worker){
|
||
this.worker.postMessage({
|
||
type:methodName,
|
||
data,
|
||
})
|
||
}else{
|
||
DrawShare[methodName](data);
|
||
}
|
||
}
|
||
|
||
dispose() {
|
||
if(this.worker){
|
||
this.worker.terminate();
|
||
useRemoveSignal("cadViewerResize",cadDialogMoveFn)
|
||
}else{
|
||
DrawShare.dispose();
|
||
}
|
||
|
||
this.resizeObserver.disconnect();
|
||
}
|
||
|
||
/* ------------------- 需要共同实现的标记相关方法 ------------------------- */
|
||
// 获取选中的标记
|
||
get selectRectIndex(){
|
||
return this.middleObject.selectRectIndex;
|
||
}
|
||
|
||
// 删除选中的标记
|
||
deleteRect(){
|
||
const id = this.middleObject.selectRect?.id;
|
||
if(!id) return;
|
||
|
||
const elementId = this.middleObject.selectRect?.elementId;
|
||
this.callMethod('callModuleMethod',{
|
||
moduleName: "drawRect", methodName: "deleteRect",elementId:elementId
|
||
})
|
||
}
|
||
|
||
// 根据模型选中对应标记
|
||
selectRect(uuid:string){
|
||
this.callMethod('callModuleMethod',{
|
||
moduleName: "drawRect", methodName: "setSelect",elementId:uuid
|
||
})
|
||
}
|
||
}
|
||
|
||
export {DxfViewer} |