@inweb/viewer-visualize
Version:
JavaScript library for rendering CAD and BIM files in a browser using VisualizeJS
223 lines (199 loc) • 7.67 kB
text/typescript
///////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2002-2025, Open Design Alliance (the "Alliance").
// All rights reserved.
//
// This software and its documentation and related materials are owned by
// the Alliance. The software may only be incorporated into application
// programs owned by members of the Alliance, subject to a signed
// Membership Agreement and Supplemental Software License Agreement with the
// Alliance. The structure and organization of this software are the valuable
// trade secrets of the Alliance and its suppliers. The software is also
// protected by copyright law and international treaty provisions. Application
// programs incorporating this software must include the following statement
// with their copyright notices:
//
// This application incorporates Open Design Alliance software pursuant to a
// license agreement with Open Design Alliance.
// Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance.
// All rights reserved.
//
// By use of this software, its documentation or related materials, you
// acknowledge and accept the above terms.
///////////////////////////////////////////////////////////////////////////////
// ===================== AI-CODE-FILE ======================
// Source: Claude Sonnet 4.5
// Date: 2025-10-07
// Reviewer: vitaly.ivanov@opendesign.com
// Issue: CLOUD-5851
// Notes: Originally AI-generated, modified manually
// =========================================================
interface JoyStickStatus {
x: number;
y: number;
global: any;
}
export class OdJoyStickDragger {
private onMouseDown: (event?: any) => void;
private onMouseMove: (event: any) => void;
private onMouseUp: (event?: any) => void;
private drawExternal: () => void;
private drawInternal: () => void;
private canvas: HTMLCanvasElement;
private onResize: () => void;
private hasEventListeners: boolean = false;
private container: HTMLElement;
constructor(
global: any,
container: HTMLElement,
callback: (status: JoyStickStatus) => void,
canvasElement?: HTMLCanvasElement
) {
const internalLineWidth = 2;
const internalStrokeColor = "#003300";
const externalLineWidth = 2;
const externalStrokeColor = "#35436E";
this.container = container;
this.container.style.touchAction = "none";
this.canvas = document.createElement("canvas");
this.canvas.id = "odJoyStickCanvas";
this.canvas.width = 200;
this.canvas.height = 200;
this.container.appendChild(this.canvas);
const context = this.canvas.getContext("2d")!;
let pressed = 0;
const circumference = 2 * Math.PI;
const internalRadius = (this.canvas.width - (this.canvas.width / 2 + 10)) / 2;
const maxMoveStick = internalRadius + 5;
const externalRadius = internalRadius + 30;
const centerX = this.canvas.width / 2;
const centerY = this.canvas.height / 2;
let movedX = centerX;
let movedY = centerY;
this.onMouseDown = () => {
event.preventDefault();
pressed = 1;
};
this.onMouseMove = (event) => {
event.preventDefault();
if (pressed === 1) {
movedX = event.pageX;
movedY = event.pageY;
if (this.canvas.offsetParent && (this.canvas.offsetParent as HTMLElement).tagName.toUpperCase() === "BODY") {
movedX -= this.canvas.offsetLeft;
movedY -= this.canvas.offsetTop;
} else if (this.canvas.offsetParent) {
movedX -= (this.canvas.offsetParent as HTMLElement).offsetLeft;
movedY -= (this.canvas.offsetParent as HTMLElement).offsetTop;
}
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawExternal();
this.drawInternal();
callback({
x: 100 * ((movedX - centerX) / maxMoveStick),
y: 100 * ((movedY - centerY) / maxMoveStick) * -1,
global,
});
}
};
this.onMouseUp = () => {
event.preventDefault();
pressed = 0;
movedX = centerX;
movedY = centerY;
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawExternal();
this.drawInternal();
callback({
x: 100 * ((movedX - centerX) / maxMoveStick),
y: 100 * ((movedY - centerY) / maxMoveStick) * -1,
global,
});
};
this.drawExternal = () => {
context.beginPath();
context.arc(centerX, centerY, externalRadius, 0, circumference, false);
context.lineWidth = externalLineWidth;
context.strokeStyle = externalStrokeColor;
context.globalAlpha = 0.5;
context.stroke();
};
this.drawInternal = () => {
context.beginPath();
if (movedX < internalRadius) {
movedX = maxMoveStick;
}
if (movedX + internalRadius > this.canvas.width) {
movedX = this.canvas.width - maxMoveStick;
}
if (movedY < internalRadius) {
movedY = maxMoveStick;
}
if (movedY + internalRadius > this.canvas.height) {
movedY = this.canvas.height - maxMoveStick;
}
context.arc(movedX, movedY, internalRadius, 0, circumference, false);
context.fillStyle = externalStrokeColor;
context.lineWidth = internalLineWidth;
context.strokeStyle = internalStrokeColor;
context.globalAlpha = 0.5;
context.fill();
context.stroke();
};
const addEventListeners = () => {
if (!this.hasEventListeners) {
this.canvas.addEventListener("pointerdown", this.onMouseDown, false);
document.addEventListener("pointermove", this.onMouseMove, false);
document.addEventListener("pointerup", this.onMouseUp, false);
this.hasEventListeners = true;
}
};
const removeEventListeners = () => {
if (this.hasEventListeners) {
this.canvas.removeEventListener("pointerdown", this.onMouseDown, false);
document.removeEventListener("pointermove", this.onMouseMove, false);
document.removeEventListener("pointerup", this.onMouseUp, false);
this.hasEventListeners = false;
}
};
const updateContainerPosition = () => {
if (canvasElement) {
const rect = canvasElement.getBoundingClientRect();
this.container.style.top = `${rect.height - 200}px`;
this.container.style.left = `${rect.left}px`;
this.container.style.width = `200px`;
this.container.style.height = `200px`;
}
};
const updateVisibility = () => {
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const isNarrowScreen = window.innerWidth < 1024;
const shouldShow = isMobile || isNarrowScreen;
if (shouldShow) {
this.container.style.display = "block";
addEventListeners();
} else {
this.container.style.display = "none";
removeEventListeners();
}
};
this.onResize = () => {
updateVisibility();
setTimeout(updateContainerPosition, 500);
};
updateVisibility();
updateContainerPosition();
window.addEventListener("resize", this.onResize, false);
this.drawExternal();
this.drawInternal();
}
cleanup(): void {
window.removeEventListener("resize", this.onResize, false);
if (this.hasEventListeners) {
this.canvas.removeEventListener("pointerdown", this.onMouseDown, false);
document.removeEventListener("pointermove", this.onMouseMove, false);
document.removeEventListener("pointerup", this.onMouseUp, false);
this.hasEventListeners = false;
}
this.canvas.remove();
}
}