You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Svelte应用中用Threlte实现Perspective Camera平滑复位

解决Threlte中相机位置平滑复位的问题

核心问题分析

你遇到的Cannot assign to read only property 'position'错误,本质是Three.js的Vector3对象(即$camera.position)属于不可直接替换的引用类型,直接赋值新对象会触发只读限制,必须通过修改其内部属性或调用set()方法更新。另外,cameraStart仅在组件初始化时生效,无法触发动态更新,需要结合Svelte的响应式和动画工具实现平滑过渡。

完整修复代码

<script>
    // @ts-nocheck
    import { tweened } from 'svelte/motion';
    import { cubicOut } from 'svelte/easing';
    import FurnaceScene from '$lib/furnaceScene.svelte';
    import PlateScene from '$lib/plateScene.svelte';
    import { browser } from '$app/environment';
    import * as Threlte from '@threlte/core';
    import * as Three from 'three';
    import { onMount } from 'svelte';
    import { useThrelte } from '@threlte/core';

    const { camera } = useThrelte();
    let plateData;

    // 初始化带动画的相机目标位置变量
    const cameraTarget = tweened(
        { x: 0, y: 30, z: 0 },
        { duration: 1000, easing: cubicOut }
    );

    // 响应式同步动画变量到相机位置
    $: {
        if ($camera) {
            $camera.position.set($cameraTarget.x, $cameraTarget.y, $cameraTarget.z);
            $camera.updateProjectionMatrix();
        }
    }

    onMount(() => {
        getPlateLocation();
    });

    // 平滑复位相机
    async function resetCamera() {
        await $cameraTarget.set({ x: 0, y: 30, z: 0 });
    }

    // 处理OrbitControls拖动结束时同步动画变量
    function handleControlsEnd() {
        $cameraTarget.set({
            x: $camera.position.x,
            y: $camera.position.y,
            z: $camera.position.z
        });
    }

    // 全局复位函数
    if (browser) {
        window.handleResetFromComponent = resetCamera;
    }

    async function getPlateLocation() {
        const response = await fetch('api/plates');
        plateData = await response.json();
    }

    // 开发用辅助网格
    const gridHelper = new Three.GridHelper(40, 40);
    const axesHelper = new Three.AxesHelper(10);
</script>

{#if plateData}
    <Threlte.PerspectiveCamera lookAt={{ y: 0 }}>
        <Threlte.OrbitControls
            maxDistance="30"
            minDistance="10"
            enableDamping
            on:end={handleControlsEnd}
        />
    </Threlte.PerspectiveCamera>

    <Threlte.AmbientLight color="white" intensity={0.2} />
    <Threlte.DirectionalLight color="white" intensity={2} position={{ x: 10, y: 8, z: 6 }} />

    <Threlte.Object3DInstance object={gridHelper} />
    <Threlte.Object3DInstance object={axesHelper} />

    <FurnaceScene />
    {#each plateData as plate}
        <PlateScene {plate} />
    {/each}
{/if}

关键修改说明

  • 用tweened管理动画状态:创建cameraTarget作为动画驱动的响应式变量,控制相机位置的平滑过渡,支持自定义时长和缓动函数。
  • 响应式同步相机位置:通过$:响应式语句,使用Three.js的set()方法将动画变量值同步到相机位置,避免直接赋值触发只读错误。
  • 同步OrbitControls状态:在拖动结束时更新cameraTarget,确保手动调整相机后,动画变量与实际位置保持一致。
  • 简化复位逻辑:直接调用$cameraTarget.set()即可触发平滑复位动画,无需手动操作相机对象。
  • 移除无效静态绑定:相机位置由动画变量动态控制,不再依赖初始化时的cameraStart对象。

内容的提问来源于stack exchange,提问作者Avi4nFLu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 16:47:52