mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-14 04:08:46 +00:00
Merge 1507390e1f into ed8c744e77
This commit is contained in:
commit
3ecd50ac2c
2 changed files with 101 additions and 3 deletions
|
|
@ -35,6 +35,52 @@
|
|||
}
|
||||
const dispatch = createEventDispatcher();
|
||||
let canvas: HTMLCanvasElement;
|
||||
const pointers = new Map<number, { x: number; y: number }>();
|
||||
let moved = 0,
|
||||
velocity = 0,
|
||||
lastMove = 0,
|
||||
lastTap = 0;
|
||||
const pointerDown = (event: PointerEvent) => {
|
||||
if (!renderer) return;
|
||||
canvas.setPointerCapture(event.pointerId);
|
||||
pointers.set(event.pointerId, { x: event.clientX, y: event.clientY });
|
||||
if (pointers.size === 1) moved = velocity = 0;
|
||||
};
|
||||
const pointerMove = (event: PointerEvent) => {
|
||||
const point = pointers.get(event.pointerId);
|
||||
if (!point || !renderer) return;
|
||||
const dx = event.clientX - point.x,
|
||||
dy = event.clientY - point.y;
|
||||
const other = [...pointers.values()].find((p) => p !== point);
|
||||
const before = other ? Math.hypot(point.x - other.x, point.y - other.y) : 0;
|
||||
point.x = event.clientX;
|
||||
point.y = event.clientY;
|
||||
moved += Math.hypot(dx, dy);
|
||||
if (other) {
|
||||
renderer.panView(dx / 2, dy / 2);
|
||||
renderer.zoomView(before / Math.max(1, Math.hypot(point.x - other.x, point.y - other.y)));
|
||||
velocity = 0;
|
||||
} else if (event.buttons & 6 || event.shiftKey) {
|
||||
renderer.panView(dx, dy);
|
||||
velocity = 0;
|
||||
} else {
|
||||
renderer.rotateView(dx, dy);
|
||||
velocity = velocity * 0.5 + (dx / Math.max(1, event.timeStamp - lastMove)) * 1000 * 0.5;
|
||||
}
|
||||
lastMove = event.timeStamp;
|
||||
};
|
||||
const pointerUp = (event: PointerEvent) => {
|
||||
if (!pointers.delete(event.pointerId) || !renderer || pointers.size) return;
|
||||
if (moved < 5) {
|
||||
if (event.timeStamp - lastTap < 300) renderer.resetView();
|
||||
lastTap = event.timeStamp;
|
||||
} else if (velocity && event.timeStamp - lastMove < 50) renderer.spinView(velocity);
|
||||
};
|
||||
const wheel = (event: WheelEvent) => {
|
||||
if (!renderer) return;
|
||||
event.preventDefault();
|
||||
renderer.zoomView(Math.exp(event.deltaY * (event.deltaMode ? 0.05 : 0.001)));
|
||||
};
|
||||
|
||||
// The parent keys this component by file, keeping settings changes inexpensive.
|
||||
onMount(() => {
|
||||
|
|
@ -162,13 +208,30 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<canvas bind:this={canvas} aria-hidden="true" class="avatar-canvas"></canvas>
|
||||
<canvas
|
||||
bind:this={canvas}
|
||||
aria-hidden="true"
|
||||
class="avatar-canvas"
|
||||
on:pointerdown={pointerDown}
|
||||
on:pointermove={pointerMove}
|
||||
on:pointerup={pointerUp}
|
||||
on:pointercancel={pointerUp}
|
||||
on:click={(event) => {
|
||||
if (moved >= 5) event.stopPropagation();
|
||||
}}
|
||||
on:contextmenu|preventDefault
|
||||
on:wheel|nonpassive={wheel}
|
||||
></canvas>
|
||||
|
||||
<style>
|
||||
.avatar-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
}
|
||||
.avatar-canvas:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -219,6 +219,11 @@ export async function createAvatarRenderer(
|
|||
gestureElapsed = 0;
|
||||
let enabled = true;
|
||||
let cameraDistance = 0;
|
||||
let yaw = 0,
|
||||
pitch = 0,
|
||||
spin = 0,
|
||||
zoom = 1;
|
||||
const pan = new THREE.Vector2();
|
||||
const switchClip = (clip: THREE.AnimationClip, once: boolean) => {
|
||||
if (outgoing) {
|
||||
outgoing.stop();
|
||||
|
|
@ -282,6 +287,30 @@ export async function createAvatarRenderer(
|
|||
return 'started' as const;
|
||||
},
|
||||
cancelGesture,
|
||||
rotateView(dx: number, dy: number) {
|
||||
const scale = (2 * Math.PI) / Math.max(height, 1);
|
||||
yaw -= dx * scale;
|
||||
pitch = THREE.MathUtils.clamp(pitch + dy * scale, -1.3, 1.3);
|
||||
spin = 0;
|
||||
},
|
||||
spinView(velocity: number) {
|
||||
spin = (-velocity * 2 * Math.PI) / Math.max(height, 1);
|
||||
},
|
||||
panView(dx: number, dy: number) {
|
||||
const scale =
|
||||
(2 * cameraDistance * zoom * Math.tan(THREE.MathUtils.degToRad(camera.fov / 2))) /
|
||||
Math.max(height, 1);
|
||||
pan.x -= dx * scale;
|
||||
pan.y += dy * scale;
|
||||
},
|
||||
zoomView(scale: number) {
|
||||
zoom = THREE.MathUtils.clamp(zoom * scale, 0.3, 3);
|
||||
},
|
||||
resetView() {
|
||||
yaw = pitch = spin = 0;
|
||||
zoom = 1;
|
||||
pan.set(0, 0);
|
||||
},
|
||||
render(delta: number, config: VoiceAvatarConfig, input: AvatarSignals, reducedMotion: boolean) {
|
||||
const dt = Math.min(delta, 0.05);
|
||||
elapsed += dt;
|
||||
|
|
@ -420,8 +449,14 @@ export async function createAvatarRenderer(
|
|||
(Math.max(fitHeight, fitWidth / camera.aspect) * 1.2) / (2 * Math.tan(Math.PI / 12));
|
||||
// Widen before a limb reaches the edge; never zoom in/out on each gesture.
|
||||
cameraDistance = Math.max(distance, cameraDistance);
|
||||
camera.position.set(center.x, center.y, center.z + cameraDistance);
|
||||
spin = reducedMotion || Math.abs(spin) < 0.01 ? 0 : spin * Math.exp(-dt * 3);
|
||||
yaw += spin * dt;
|
||||
camera.position
|
||||
.setFromSphericalCoords(cameraDistance * zoom, Math.PI / 2 - pitch, yaw)
|
||||
.add(center);
|
||||
camera.lookAt(center);
|
||||
camera.translateX(pan.x);
|
||||
camera.translateY(pan.y);
|
||||
renderer.render(scene, camera);
|
||||
},
|
||||
dispose() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue