three
Version:
JavaScript 3D library
171 lines (115 loc) • 5.58 kB
JavaScript
import { Style } from './Style.js';
export class Profiler {
constructor() {
this.tabs = {};
this.activeTabId = null;
this.isResizing = false;
this.lastHeight = 350;
Style.init();
this.setupShell();
this.setupResizing();
}
setupShell() {
this.domElement = document.createElement( 'div' );
this.domElement.id = 'profiler-shell';
this.toggleButton = document.createElement( 'button' );
this.toggleButton.id = 'profiler-toggle';
this.toggleButton.innerHTML = `
<span id="toggle-text">
<span id="fps-counter">-</span>
<span class="fps-label">FPS</span>
</span>
<!-- <span class="toggle-separator"></span> -->
<span id="toggle-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-device-ipad-horizontal-search"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M11.5 20h-6.5a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v5.5" /><path d="M9 17h2" /><path d="M18 18m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0" /><path d="M20.2 20.2l1.8 1.8" /></svg>
</span>
`;
this.toggleButton.onclick = () => this.togglePanel();
this.panel = document.createElement( 'div' );
this.panel.id = 'profiler-panel';
const header = document.createElement( 'div' );
header.className = 'profiler-header';
this.tabsContainer = document.createElement( 'div' );
this.tabsContainer.className = 'profiler-tabs';
const controls = document.createElement( 'div' );
controls.className = 'profiler-controls';
this.maximizeBtn = document.createElement( 'button' );
this.maximizeBtn.id = 'maximize-btn';
this.maximizeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>';
this.maximizeBtn.onclick = () => this.toggleMaximize();
const hideBtn = document.createElement( 'button' );
hideBtn.id = 'hide-panel-btn';
hideBtn.textContent = '-';
hideBtn.onclick = () => this.togglePanel();
controls.append( this.maximizeBtn, hideBtn );
header.append( this.tabsContainer, controls );
this.contentWrapper = document.createElement( 'div' );
this.contentWrapper.className = 'profiler-content-wrapper';
const resizer = document.createElement( 'div' );
resizer.className = 'panel-resizer';
this.panel.append( resizer, header, this.contentWrapper );
this.domElement.append( this.toggleButton, this.panel );
}
setupResizing() {
const resizer = this.panel.querySelector( '.panel-resizer' );
const onStart = ( e ) => {
this.isResizing = true;
this.panel.classList.add( 'resizing' );
const startY = e.clientY || e.touches[ 0 ].clientY;
const startHeight = this.panel.offsetHeight;
const onMove = ( moveEvent ) => {
if ( ! this.isResizing ) return;
moveEvent.preventDefault();
const currentY = moveEvent.clientY || moveEvent.touches[ 0 ].clientY;
const newHeight = startHeight - ( currentY - startY );
if ( newHeight > 100 && newHeight < window.innerHeight - 50 ) {
this.panel.style.height = `${newHeight}px`;
}
};
const onEnd = () => {
this.isResizing = false;
this.panel.classList.remove( 'resizing' );
document.removeEventListener( 'mousemove', onMove );
document.removeEventListener( 'mouseup', onEnd );
document.removeEventListener( 'touchmove', onMove );
document.removeEventListener( 'touchend', onEnd );
if ( ! this.panel.classList.contains( 'maximized' ) ) {
this.lastHeight = this.panel.offsetHeight;
}
};
document.addEventListener( 'mousemove', onMove );
document.addEventListener( 'mouseup', onEnd );
document.addEventListener( 'touchmove', onMove, { passive: false } );
document.addEventListener( 'touchend', onEnd );
};
resizer.addEventListener( 'mousedown', onStart );
resizer.addEventListener( 'touchstart', onStart );
}
toggleMaximize() {
if ( this.panel.classList.contains( 'maximized' ) ) {
this.panel.classList.remove( 'maximized' );
this.panel.style.height = `${ this.lastHeight }px`;
this.maximizeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>';
} else {
this.lastHeight = this.panel.offsetHeight;
this.panel.classList.add( 'maximized' );
this.panel.style.height = '100vh';
this.maximizeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="8" width="12" height="12" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>';
}
}
addTab( tab ) {
this.tabs[ tab.id ] = tab;
tab.button.onclick = () => this.setActiveTab( tab.id );
this.tabsContainer.appendChild( tab.button );
this.contentWrapper.appendChild( tab.content );
}
setActiveTab( id ) {
if ( this.activeTabId ) this.tabs[ this.activeTabId ].setActive( false );
this.activeTabId = id;
this.tabs[ id ].setActive( true );
}
togglePanel() {
this.panel.classList.toggle( 'visible' );
this.toggleButton.classList.toggle( 'hidden' );
}
}