第13章 - 动画与时间系统

第13章:动画与时间系统

13.1 时钟系统

13.1.1 Clock 配置

const clock = viewer.clock;

// 时间范围
clock.startTime = Cesium.JulianDate.fromIso8601('2024-01-01T00:00:00Z');
clock.stopTime = Cesium.JulianDate.fromIso8601('2024-01-07T00:00:00Z');
clock.currentTime = Cesium.JulianDate.fromIso8601('2024-01-01T12:00:00Z');

// 播放速度
clock.multiplier = 60;        // 60倍速
clock.shouldAnimate = true;   // 开始播放

// 时钟范围模式
clock.clockRange = Cesium.ClockRange.LOOP_STOP;  // 循环
// Cesium.ClockRange.UNBOUNDED   // 无限制
// Cesium.ClockRange.CLAMPED     // 限制在范围内

// 时钟步进
clock.clockStep = Cesium.ClockStep.SYSTEM_CLOCK_MULTIPLIER;
// Cesium.ClockStep.TICK_DEPENDENT        // 依赖tick
// Cesium.ClockStep.SYSTEM_CLOCK          // 系统时钟

13.1.2 时间事件

// 监听时钟tick
clock.onTick.addEventListener(function(clock) {
    const currentTime = clock.currentTime;
    console.log('当前时间:', Cesium.JulianDate.toIso8601(currentTime));
});

// 监听时钟停止
clock.onStop.addEventListener(function(clock) {
    console.log('时钟停止');
});

13.2 Timeline 控件

const timeline = viewer.timeline;

// 缩放时间轴
timeline.zoomTo(
    Cesium.JulianDate.fromIso8601('2024-01-01'),
    Cesium.JulianDate.fromIso8601('2024-01-31')
);

// 监听时间轴事件
timeline.addEventListener('settime', function(e) {
    console.log('时间轴时间改变');
}, false);

13.3 时间动态属性

13.3.1 SampledProperty

// 创建采样位置属性
const positionProperty = new Cesium.SampledPositionProperty();

// 添加采样点
const times = [
    Cesium.JulianDate.fromIso8601('2024-01-01T00:00:00Z'),
    Cesium.JulianDate.fromIso8601('2024-01-01T01:00:00Z'),
    Cesium.JulianDate.fromIso8601('2024-01-01T02:00:00Z')
];
const positions = [
    Cesium.Cartesian3.fromDegrees(116.0, 39.0, 1000),
    Cesium.Cartesian3.fromDegrees(117.0, 39.5, 2000),
    Cesium.Cartesian3.fromDegrees(118.0, 40.0, 1500)
];

for (let i = 0; i < times.length; i++) {
    positionProperty.addSample(times[i], positions[i]);
}

// 设置插值
positionProperty.setInterpolationOptions({
    interpolationDegree: 5,
    interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
    // Cesium.LinearApproximation
    // Cesium.HermitePolynomialApproximation
});

// 创建动态实体
const movingEntity = viewer.entities.add({
    name: '移动对象',
    availability: new Cesium.TimeIntervalCollection([
        new Cesium.TimeInterval({
            start: times[0],
            stop: times[times.length - 1]
        })
    ]),
    position: positionProperty,
    orientation: new Cesium.VelocityOrientationProperty(positionProperty),
    model: {
        uri: 'aircraft.glb',
        scale: 100
    },
    path: {
        material: Cesium.Color.YELLOW,
        width: 2,
        leadTime: 0,
        trailTime: 3600
    }
});

// 跟踪实体
viewer.trackedEntity = movingEntity;

13.3.2 其他动态属性

// 采样颜色属性
const colorProperty = new Cesium.SampledProperty(Cesium.Color);
colorProperty.addSample(times[0], Cesium.Color.RED);
colorProperty.addSample(times[1], Cesium.Color.GREEN);
colorProperty.addSample(times[2], Cesium.Color.BLUE);

// 采样数值属性
const scaleProperty = new Cesium.SampledProperty(Number);
scaleProperty.addSample(times[0], 1.0);
scaleProperty.addSample(times[1], 2.0);
scaleProperty.addSample(times[2], 1.5);

// 时间区间颜色
const timeIntervalColor = new Cesium.TimeIntervalCollectionProperty();
timeIntervalColor.intervals.addInterval(new Cesium.TimeInterval({
    start: times[0],
    stop: times[1],
    data: Cesium.Color.RED
}));
timeIntervalColor.intervals.addInterval(new Cesium.TimeInterval({
    start: times[1],
    stop: times[2],
    data: Cesium.Color.BLUE
}));

13.4 场景动画

13.4.1 属性动画

// 实体属性动画
function animateEntityScale(entity, fromScale, toScale, duration) {
    const startTime = performance.now();
    
    function animate() {
        const elapsed = performance.now() - startTime;
        const progress = Math.min(elapsed / (duration * 1000), 1);
        
        // 缓动函数
        const easedProgress = Cesium.EasingFunction.QUADRATIC_IN_OUT(progress);
        const currentScale = fromScale + (toScale - fromScale) * easedProgress;
        
        entity.model.scale = currentScale;
        
        if (progress < 1) {
            requestAnimationFrame(animate);
        }
    }
    
    animate();
}

// 颜色渐变动画
function animateColor(entity, fromColor, toColor, duration) {
    const startTime = performance.now();
    
    function animate() {
        const elapsed = performance.now() - startTime;
        const progress = Math.min(elapsed / (duration * 1000), 1);
        
        const currentColor = Cesium.Color.lerp(fromColor, toColor, progress, new Cesium.Color());
        
        if (entity.polygon) {
            entity.polygon.material = currentColor;
        }
        
        if (progress < 1) {
            requestAnimationFrame(animate);
        }
    }
    
    animate();
}

13.4.2 轨迹动画

// 创建轨迹动画
class TrajectoryAnimation {
    constructor(viewer, waypoints, options = {}) {
        this.viewer = viewer;
        this.waypoints = waypoints;
        this.duration = options.duration || 60;  // 秒
        this.entity = null;
    }
    
    create() {
        const startTime = Cesium.JulianDate.now();
        const stopTime = Cesium.JulianDate.addSeconds(startTime, this.duration, new Cesium.JulianDate());
        
        // 配置时钟
        this.viewer.clock.startTime = startTime.clone();
        this.viewer.clock.stopTime = stopTime.clone();
        this.viewer.clock.currentTime = startTime.clone();
        this.viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
        this.viewer.clock.multiplier = 1;
        
        // 创建位置属性
        const positionProperty = new Cesium.SampledPositionProperty();
        const interval = this.duration / (this.waypoints.length - 1);
        
        this.waypoints.forEach((wp, i) => {
            const time = Cesium.JulianDate.addSeconds(startTime, i * interval, new Cesium.JulianDate());
            const position = Cesium.Cartesian3.fromDegrees(wp.lon, wp.lat, wp.height || 0);
            positionProperty.addSample(time, position);
        });
        
        positionProperty.setInterpolationOptions({
            interpolationDegree: 5,
            interpolationAlgorithm: Cesium.LagrangePolynomialApproximation
        });
        
        // 创建实体
        this.entity = this.viewer.entities.add({
            name: '动画对象',
            availability: new Cesium.TimeIntervalCollection([
                new Cesium.TimeInterval({ start: startTime, stop: stopTime })
            ]),
            position: positionProperty,
            orientation: new Cesium.VelocityOrientationProperty(positionProperty),
            point: { pixelSize: 12, color: Cesium.Color.RED },
            path: {
                material: Cesium.Color.YELLOW,
                width: 2,
                trailTime: this.duration
            }
        });
        
        return this.entity;
    }
    
    play() {
        this.viewer.clock.shouldAnimate = true;
    }
    
    pause() {
        this.viewer.clock.shouldAnimate = false;
    }
    
    setSpeed(multiplier) {
        this.viewer.clock.multiplier = multiplier;
    }
    
    track() {
        this.viewer.trackedEntity = this.entity;
    }
    
    destroy() {
        if (this.entity) {
            this.viewer.entities.remove(this.entity);
            this.entity = null;
        }
    }
}

// 使用
const animation = new TrajectoryAnimation(viewer, [
    { lon: 116.0, lat: 39.0, height: 1000 },
    { lon: 117.0, lat: 39.5, height: 2000 },
    { lon: 118.0, lat: 40.0, height: 1500 },
    { lon: 119.0, lat: 39.5, height: 1000 }
], { duration: 120 });

animation.create();
animation.track();
animation.play();

13.5 本章小结

本章介绍了动画与时间系统:

  1. 时钟系统:Clock 配置、时间事件
  2. Timeline 控件:时间轴控制
  3. 动态属性:SampledProperty、插值
  4. 场景动画:属性动画、轨迹动画

在下一章中,我们将详细介绍交互与事件处理。

13.6 思考与练习

  1. 创建一个卫星轨道动画。
  2. 实现动画播放控制面板。
  3. 开发多对象同步运动效果。
  4. 实现时间倒放功能。
  5. 创建历史轨迹回放工具。
posted @ 2026-01-08 11:13  我才是银古  阅读(12)  评论(0)    收藏  举报