@deck.gl/widgets
Version:
UI widgets for deck.gl
121 lines • 5.49 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "preact/jsx-runtime";
// deck.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import { Widget, LinearInterpolator } from '@deck.gl/core';
import { render } from 'preact';
export class GimbalWidget extends Widget {
constructor(props = {}) {
super(props);
this.className = 'deck-widget-gimbal';
this.placement = 'top-left';
this.viewports = {};
this.setProps(this.props);
}
setProps(props) {
this.placement = props.placement ?? this.placement;
this.viewId = props.viewId ?? this.viewId;
super.setProps(props);
}
onRenderHTML(rootElement) {
const viewId = this.viewId || Object.values(this.viewports)[0]?.id || 'default-view';
const widgetViewport = this.viewports[viewId];
const { rotationOrbit, rotationX } = this.getNormalizedRotation(widgetViewport);
// Note - we use CSS 3D transforms instead of SVG 2D transforms
const ui = (_jsx("div", { className: "deck-widget-button", style: { perspective: 100, pointerEvents: 'auto' }, children: _jsxs("button", { type: "button", onClick: () => {
for (const viewport of Object.values(this.viewports)) {
this.resetOrbitView(viewport);
}
}, title: this.props.label, style: { position: 'relative', width: 26, height: 26 }, children: [_jsx("svg", { className: "gimbal-outer-ring", width: "100%", height: "100%", viewBox: "0 0 26 26", style: {
position: 'absolute',
top: 0,
left: 0,
transform: `rotateY(${rotationOrbit}deg)`
}, children: _jsx("circle", { cx: "13", cy: "13", r: "10", stroke: "var(--icon-gimbal-outer-color, rgb(68, 92, 204))", strokeWidth: this.props.strokeWidth, fill: "none" }) }), _jsx("svg", { className: "gimbal-inner-ring", width: "100%", height: "100%", viewBox: "0 0 26 26", style: {
position: 'absolute',
top: 0,
left: 0,
transform: `rotateX(${rotationX}deg)`
}, children: _jsx("circle", { cx: "13", cy: "13", r: "7", stroke: "var(--icon-gimbal-inner-color, rgb(240, 92, 68))", strokeWidth: this.props.strokeWidth, fill: "none" }) })] }) }));
render(ui, rootElement);
}
onViewportChange(viewport) {
this.viewports[viewport.id] = viewport;
this.updateHTML();
}
resetOrbitView(viewport) {
const viewId = this.getViewId(viewport);
const viewState = this.getViewState(viewId);
if ('rotationOrbit' in viewState || 'rotationX' in viewState) {
const nextViewState = {
...viewState,
rotationOrbit: 0,
rotationX: 0,
transitionDuration: this.props.transitionDuration,
transitionInterpolator: new LinearInterpolator({
transitionProps: ['rotationOrbit', 'rotationX']
})
};
// @ts-ignore Using private method temporary until there's a public one
this.deck._onViewStateChange({ viewId, viewState: nextViewState, interactionState: {} });
}
}
getNormalizedRotation(viewport) {
const viewState = this.getViewState(this.getViewId(viewport));
const [rz, rx] = this.getRotation(viewState);
const rotationOrbit = normalizeAndClampAngle(rz);
const rotationX = normalizeAndClampAngle(rx);
return { rotationOrbit, rotationX };
}
getRotation(viewState) {
if (viewState && ('rotationOrbit' in viewState || 'rotationX' in viewState)) {
return [-(viewState.rotationOrbit || 0), viewState.rotationX || 0];
}
return [0, 0];
}
// Move to Widget/WidgetManager?
getViewId(viewport) {
const viewId = this.viewId || viewport?.id || 'OrbitView';
return viewId;
}
getViewState(viewId) {
const viewManager = this.getViewManager();
const viewState = (viewId && viewManager.getViewState(viewId)) || viewManager.viewState;
return viewState;
}
getViewManager() {
// @ts-expect-error protected
const viewManager = this.deck?.viewManager;
if (!viewManager) {
throw new Error('wigdet must be added to a deck instance');
}
return viewManager;
}
}
GimbalWidget.defaultProps = {
...Widget.defaultProps,
id: 'gimbal',
placement: 'top-left',
viewId: null,
label: 'Gimbal',
strokeWidth: 1.5,
transitionDuration: 200
};
function normalizeAndClampAngle(angle) {
// Bring angle into [-180, 180]
let normalized = ((((angle + 180) % 360) + 360) % 360) - 180;
// Avoid rotating the gimbal rings to close to 90 degrees as they will visually disappear
const AVOID_ANGLE_DELTA = 10;
const distanceFrom90 = normalized - 90;
if (Math.abs(distanceFrom90) < AVOID_ANGLE_DELTA) {
if (distanceFrom90 < AVOID_ANGLE_DELTA) {
normalized = 90 + AVOID_ANGLE_DELTA;
}
else if (distanceFrom90 > -AVOID_ANGLE_DELTA) {
normalized = 90 - AVOID_ANGLE_DELTA;
}
}
// Clamp to [-80, 80]
return normalized; // Math.max(-80, Math.min(80, normalized));
}
//# sourceMappingURL=gimbal-widget.js.map