|
@@ -1,16 +1,116 @@
|
|
<template>
|
|
<template>
|
|
<div class="home">
|
|
<div class="home">
|
|
|
|
+ <div style="position: fixed; z-index: 1; padding: 5px;" class="compact-control">
|
|
|
|
+ <button class="roam-btn">
|
|
|
|
+ <i class="fas fa-play"></i>
|
|
|
|
+ <span @click="startAnimation">开始模拟</span>
|
|
|
|
+ </button>
|
|
|
|
+ <button class="roam-btn">
|
|
|
|
+ <i class="fas fa-play"></i>
|
|
|
|
+ <span @click="stopAnimation">结束模拟</span>
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
+ <div v-if="endPanelVisible" style="position: fixed; top: 50%; left:50%;transform: translate(-50%, -50%);z-index: 1;" class="compact-control">
|
|
|
|
+ <div class="control-header">
|
|
|
|
+ <i class="fas fa-tasks"></i>
|
|
|
|
+ <span>任务完成提示</span>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="completion-message">
|
|
|
|
+ <h3>事件处置报告。</h3>
|
|
|
|
+ <p>事件处置完成!</p>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="completion-actions">
|
|
|
|
+ <button class="tile-btn" @click="() => { endPanelVisible = false; }">确定</button>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
<div id="skysceneryContainer"></div>
|
|
<div id="skysceneryContainer"></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
|
+
|
|
export default {
|
|
export default {
|
|
mounted() {
|
|
mounted() {
|
|
window.SkySceneryConfig = {};
|
|
window.SkySceneryConfig = {};
|
|
|
|
+ this.staticInitData();
|
|
this.getToken();
|
|
this.getToken();
|
|
},
|
|
},
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ //init 数据
|
|
|
|
+ carModelPath: undefined,
|
|
|
|
+ humanAModelPath: undefined,
|
|
|
|
+ humanBModelPath: undefined,
|
|
|
|
+ carRouteNodes: [],
|
|
|
|
+ humanARouteNodes: undefined,
|
|
|
|
+ humanBLocation: undefined,
|
|
|
|
+ humanASpeakText: [],
|
|
|
|
+ humanBSpeakText: [],
|
|
|
|
+ tileProviderUrl: undefined,
|
|
|
|
+ //运行时各种引用的Holder
|
|
|
|
+ runningClockTickEventRemover: undefined,
|
|
|
|
+ //运行时数据
|
|
|
|
+ isRunning: false,
|
|
|
|
+ carStartTime: null,
|
|
|
|
+ carStopTime: null,
|
|
|
|
+ humanAStartTime: null,
|
|
|
|
+ humanAStopTime: null,
|
|
|
|
+ talkStartTime: null,
|
|
|
|
+ talkStopTime: null,
|
|
|
|
+ endPanelVisible: false
|
|
|
|
+ };
|
|
|
|
+ },
|
|
methods: {
|
|
methods: {
|
|
|
|
+ staticInitData() {
|
|
|
|
+ this.carModelPath = "http://121.43.55.7:65456/model/车辆/警车.gltf";
|
|
|
|
+ this.humanAModelPath = "http://121.43.55.7:65456/model/人物/巡逻人员.glb";
|
|
|
|
+ this.humanBModelPath = "http://121.43.55.7:65456/model/人物/行人.gltf";
|
|
|
|
+ this.carRouteNodes = [
|
|
|
|
+
|
|
|
|
+ [121.1055674641875, 31.14948997298628],
|
|
|
|
+ [121.1049455557126, 31.149625603510888],
|
|
|
|
+ [121.10453307504021, 31.148607047068204],
|
|
|
|
+ [121.1042215514566, 31.148643139863402]
|
|
|
|
+ ];
|
|
|
|
+ this.humanARouteNodes = [[121.10422192119506, 31.14864943768712], [121.10422766810703, 31.14874032312098]];
|
|
|
|
+ this.humanBLocation = [121.10422908795015, 31.148759090201995];
|
|
|
|
+ this.humanASpeakText = [
|
|
|
|
+ {
|
|
|
|
+ startTime: 2.5,
|
|
|
|
+ endTime: 5,
|
|
|
|
+ text: "您好,我们是市城管局的巡查人员。观察到您在此处设摊经营,该区域属于禁止摆摊区域。"
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ startTime: 10.5,
|
|
|
|
+ endTime: 16,
|
|
|
|
+ text: "理解您的困难,但人行道摆摊会影响市民通行,也存在食品安全隐患。我们可以引导您到正规市场经营。"
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ startTime: 25,
|
|
|
|
+ endTime: 29,
|
|
|
|
+ text: "这是本市便民摊点分布图,东区市场目前有免费入驻政策,我们现在就可以帮您联系管理方。"
|
|
|
|
+ }
|
|
|
|
+ ];
|
|
|
|
+ this.humanBSpeakText = [
|
|
|
|
+ {
|
|
|
|
+ startTime: 6,
|
|
|
|
+ endTime: 8.5,
|
|
|
|
+ text: "同志您好,我就是临时卖点自家水果,不知道这里不能摆摊,最近家里确实比较困难..."
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ startTime: 17.5,
|
|
|
|
+ endTime: 21.5,
|
|
|
|
+ text: "正规市场摊位费太贵了,我们小本生意实在承担不起。能不能通融一下?我保证不影响通行。"
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ startTime: 31,
|
|
|
|
+ endTime: 35,
|
|
|
|
+ text: "谢谢您!太好了,我这就收拾东西。以后一定按规定经营,给您添麻烦了。"
|
|
|
|
+ }];
|
|
|
|
+ this.tileProviderUrl = "https://szlszxdt.qpservice.org.cn/internal_map/?servertype=shmap_blue_web&proxyToken=C4BCA7C6-DF66-4A7D-2931-A258DFEFF8AB"
|
|
|
|
+ },
|
|
getToken() {
|
|
getToken() {
|
|
let that = this;
|
|
let that = this;
|
|
let loginInfo = new FormData();
|
|
let loginInfo = new FormData();
|
|
@@ -105,88 +205,276 @@ export default {
|
|
|
|
|
|
// 添加地图服务
|
|
// 添加地图服务
|
|
viewer.imageryLayers.addImageryProvider(
|
|
viewer.imageryLayers.addImageryProvider(
|
|
- // 加载 上海2000坐标系 瓦片服务
|
|
|
|
- // new SkyScenery.CGCS2000ArcGisMapServerImageryProvider({
|
|
|
|
- // url:
|
|
|
|
- // "http://10.235.245.174:10092/proxy/?servertype=air_2023&proxyToken=" +
|
|
|
|
- // SkySceneryConfig.token
|
|
|
|
- // })
|
|
|
|
new SkyScenery.ArcGisMapServerImageryProvider({
|
|
new SkyScenery.ArcGisMapServerImageryProvider({
|
|
- url: "https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer"
|
|
|
|
|
|
+ url: this.tileProviderUrl,
|
|
})
|
|
})
|
|
);
|
|
);
|
|
|
|
+ // 添加点
|
|
|
|
+ this.initAnimal();
|
|
|
|
+ },
|
|
|
|
+ initAnimal() {
|
|
|
|
+ //init route points
|
|
|
|
+ const carRoutePoints = this.carRouteNodes.map(item => {
|
|
|
|
+ return SkyScenery.Cartesian3.fromDegrees(item[0], item[1], item[2]);
|
|
|
|
+ });
|
|
|
|
+ const humanARoutePoints = this.humanARouteNodes.map(item => {
|
|
|
|
+ return SkyScenery.Cartesian3.fromDegrees(item[0], item[1], item[2] || 0);
|
|
|
|
+ });
|
|
|
|
+ const humanBPosition = SkyScenery.Cartesian3.fromDegrees(this.humanBLocation[0], this.humanBLocation[1], this.humanBLocation[2] || 0);
|
|
|
|
+
|
|
|
|
+ //定义一些方向计算用的量
|
|
|
|
+
|
|
|
|
+ const fix90 = SkyScenery.Quaternion.fromAxisAngle(SkyScenery.Cartesian3.UNIT_Z, Math.PI / 2);
|
|
|
|
+ const fix270 = SkyScenery.Quaternion.conjugate(fix90, new SkyScenery.Quaternion());
|
|
|
|
+
|
|
|
|
+ //generate time line
|
|
|
|
+ const startTime = SkyScenery.JulianDate.fromIso8601('2000-01-01T12:00:00+08:00');
|
|
|
|
+ const carDuration = 10; //seconds
|
|
|
|
+ const humanAWalkDuration = 4; //seconds
|
|
|
|
+ const talkDuration = [...this.humanASpeakText, ...this.humanBSpeakText].reduce((total, text) => {
|
|
|
|
+ return total > text.endTime ? total : text.endTime;
|
|
|
|
+ }, 0) + 3; //seconds
|
|
|
|
+ const totalDuration = carDuration + 1 + humanAWalkDuration + talkDuration;
|
|
|
|
+
|
|
|
|
+ this.carStartTime = SkyScenery.JulianDate.clone(startTime);
|
|
|
|
+ this.carStopTime = SkyScenery.JulianDate.addSeconds(this.carStartTime, carDuration, new SkyScenery.JulianDate());
|
|
|
|
+ this.humanAStartTime = SkyScenery.JulianDate.addSeconds(this.carStopTime, 1, new SkyScenery.JulianDate());
|
|
|
|
+ this.humanAStopTime = SkyScenery.JulianDate.addSeconds(this.humanAStartTime, humanAWalkDuration, new SkyScenery.JulianDate());
|
|
|
|
+ this.talkStartTime = SkyScenery.JulianDate.clone(this.humanAStopTime);
|
|
|
|
+ this.talkStopTime = SkyScenery.JulianDate.addSeconds(this.talkStartTime, talkDuration, new SkyScenery.JulianDate());
|
|
|
|
+
|
|
|
|
+ const stopTime = SkyScenery.JulianDate.clone(this.talkStopTime);
|
|
|
|
+
|
|
|
|
+ //init clock
|
|
|
|
+ viewer.clock.startTime = startTime.clone();
|
|
|
|
+ viewer.clock.stopTime = stopTime.clone();
|
|
|
|
+
|
|
|
|
+ viewer.clock.multiplier = 1; //播放速度
|
|
|
|
+ viewer.clock.clockRange = SkyScenery.ClockRange.CLAMPED; //到达终点后停止
|
|
|
|
|
|
- // 定位
|
|
|
|
- viewer.camera.setView({
|
|
|
|
- destination: SkyScenery.Cartesian3.fromDegrees(121, 31, 30000.0), // 设置位置
|
|
|
|
- orientation: {
|
|
|
|
- heading: SkyScenery.Math.toRadians(0.0), // 方向
|
|
|
|
- pitch: SkyScenery.Math.toRadians(-90.0), // 倾斜角度
|
|
|
|
- roll: 0
|
|
|
|
|
|
+ viewer.clock.currentTime = viewer.clock.startTime.clone();
|
|
|
|
+ viewer.clock.shouldAnimate = false; //是否自动播放
|
|
|
|
+ //part 1: car
|
|
|
|
+ //计算车的路线点距离和时间
|
|
|
|
+ let routePointdistaces = [0];
|
|
|
|
+ let totalDistance = 0;
|
|
|
|
+ for (let i = 1; i < carRoutePoints.length; i++) {
|
|
|
|
+ const distance = SkyScenery.Cartesian3.distance(carRoutePoints[i - 1], carRoutePoints[i]);
|
|
|
|
+ routePointdistaces.push(distance);
|
|
|
|
+ totalDistance += distance;
|
|
|
|
+ }
|
|
|
|
+ let unitDistanceTime = carDuration / totalDistance;
|
|
|
|
+ //生成车的路线采样点
|
|
|
|
+ const carPosition = new SkyScenery.SampledPositionProperty();
|
|
|
|
+ let carTimeAdder = 0;
|
|
|
|
+ carRoutePoints.forEach((point, index) => {
|
|
|
|
+ let timesecend = routePointdistaces[index] * unitDistanceTime;
|
|
|
|
+ carTimeAdder += timesecend;
|
|
|
|
+ const time = SkyScenery.JulianDate.addSeconds(this.carStartTime, carTimeAdder, new SkyScenery.JulianDate());
|
|
|
|
+ carPosition.addSample(time, point);
|
|
|
|
+ });
|
|
|
|
+ carPosition.forwardExtrapolationType = SkyScenery.ExtrapolationType.HOLD;
|
|
|
|
+ carPosition.backwardExtrapolationType = SkyScenery.ExtrapolationType.HOLD;
|
|
|
|
+ carPosition.setInterpolationOptions({
|
|
|
|
+ interpolationDegree: 1,
|
|
|
|
+ interpolationAlgorithm: SkyScenery.LinearApproximation
|
|
|
|
+ });
|
|
|
|
+ const carEntity = viewer.entities.add({
|
|
|
|
+ id: 'car',
|
|
|
|
+ name: 'car',
|
|
|
|
+ position: carPosition,
|
|
|
|
+ viewFrom: new SkyScenery.Cartesian3(24.0, -20.0, 12.0),
|
|
|
|
+ model: {
|
|
|
|
+ uri: this.carModelPath,
|
|
|
|
+ minimumPixelSize: 64,
|
|
|
|
+ maximumScale: 200
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ const carVelOrient = new SkyScenery.VelocityOrientationProperty(carPosition);
|
|
|
|
+ let carLastOrient = SkyScenery.Quaternion.IDENTITY.clone();
|
|
|
|
+ carEntity.orientation = new SkyScenery.CallbackProperty(function (time) {
|
|
|
|
+ let qVel;
|
|
|
|
+ qVel = carVelOrient.getValue(time);
|
|
|
|
+ if (qVel != null) { // 有速度,正常算
|
|
|
|
+ SkyScenery.Quaternion.clone(qVel, carLastOrient); // 留一份
|
|
|
|
+ } else { // 零速,用缓存
|
|
|
|
+ qVel = carLastOrient;
|
|
}
|
|
}
|
|
|
|
+ return SkyScenery.Quaternion.multiply(qVel, fix270, new SkyScenery.Quaternion());
|
|
|
|
+ }, false);
|
|
|
|
+ //初始即跟踪车辆
|
|
|
|
+ viewer.trackedEntity = viewer.entities.getById('car');
|
|
|
|
+
|
|
|
|
+ //part 2: human A walk
|
|
|
|
+ //计算人A的路线点距离和时间
|
|
|
|
+ routePointdistaces = [0];
|
|
|
|
+ totalDistance = 0;
|
|
|
|
+ for (let i = 1; i < humanARoutePoints.length; i++) {
|
|
|
|
+ const distance = SkyScenery.Cartesian3.distance(humanARoutePoints[i - 1], humanARoutePoints[i]);
|
|
|
|
+ routePointdistaces.push(distance);
|
|
|
|
+ totalDistance += distance;
|
|
|
|
+ }
|
|
|
|
+ unitDistanceTime = humanAWalkDuration / totalDistance;
|
|
|
|
+ //生成人A的路线采样点
|
|
|
|
+ const humanAPosition = new SkyScenery.SampledPositionProperty();
|
|
|
|
+ let humanATimeAdder = 0;
|
|
|
|
+ humanARoutePoints.forEach((point, index) => {
|
|
|
|
+ let timesecend = routePointdistaces[index] * unitDistanceTime;
|
|
|
|
+ humanATimeAdder += timesecend;
|
|
|
|
+ const time = SkyScenery.JulianDate.addSeconds(this.humanAStartTime, humanATimeAdder, new SkyScenery.JulianDate());
|
|
|
|
+ humanAPosition.addSample(time, point);
|
|
});
|
|
});
|
|
- // 添加点
|
|
|
|
- viewer.entities.add({
|
|
|
|
- name: "点",
|
|
|
|
- position: SkyScenery.Cartesian3.fromDegrees(121.1, 31), //经纬度转世界坐标
|
|
|
|
- point: {
|
|
|
|
- show: true,
|
|
|
|
- color: SkyScenery.Color.GREEN,
|
|
|
|
- pixelSize: 20,
|
|
|
|
- outlineColor: SkyScenery.Color.YELLOW,
|
|
|
|
- outlineWidth: 3
|
|
|
|
|
|
+
|
|
|
|
+ humanAPosition.forwardExtrapolationType = SkyScenery.ExtrapolationType.HOLD;
|
|
|
|
+ humanAPosition.backwardExtrapolationType = SkyScenery.ExtrapolationType.NONE;
|
|
|
|
+ humanAPosition.setInterpolationOptions({
|
|
|
|
+ interpolationDegree: 1,
|
|
|
|
+ interpolationAlgorithm: SkyScenery.LinearApproximation
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const humanAEntity = viewer.entities.add({
|
|
|
|
+ id: 'humanA',
|
|
|
|
+ name: 'humanA',
|
|
|
|
+ position: humanAPosition,
|
|
|
|
+ model: {
|
|
|
|
+ uri: this.humanAModelPath,
|
|
|
|
+ minimumPixelSize: 32,
|
|
|
|
+ maximumScale: 20,
|
|
|
|
+ runAnimations: false
|
|
},
|
|
},
|
|
label: {
|
|
label: {
|
|
- text: "这里是标签", //设置文字内容
|
|
|
|
font: "normal 18px 楷体", //设置文字大小和字体
|
|
font: "normal 18px 楷体", //设置文字大小和字体
|
|
- fillColor: SkyScenery.Color.fromCssColorString("#00ff00"), //设置文字填充的颜色
|
|
|
|
|
|
+ fillColor: SkyScenery.Color.fromCssColorString("#f0f0f0"), //设置文字填充的颜色
|
|
horizontalOrigin: SkyScenery.HorizontalOrigin.CENTER,
|
|
horizontalOrigin: SkyScenery.HorizontalOrigin.CENTER,
|
|
verticalOrigin: SkyScenery.VerticalOrigin.BOTTOM,
|
|
verticalOrigin: SkyScenery.VerticalOrigin.BOTTOM,
|
|
- pixelOffset: new SkyScenery.Cartesian2(0, -20)
|
|
|
|
- }
|
|
|
|
|
|
+ pixelOffset: new SkyScenery.Cartesian2(0, -120),
|
|
|
|
+ showBackground: true,
|
|
|
|
+ backgroundColor: SkyScenery.Color.fromCssColorString("#00000080"),
|
|
|
|
+ },
|
|
|
|
+ viewFrom: new SkyScenery.Cartesian3(24.0, -20.0, 12.0)
|
|
});
|
|
});
|
|
- // 添加图片点
|
|
|
|
- viewer.entities.add({
|
|
|
|
- position: SkyScenery.Cartesian3.fromDegrees(121.2, 31),
|
|
|
|
- billboard: {
|
|
|
|
- image: "/static/image/point.png", // 图片地址
|
|
|
|
- width: 48, // 图片宽
|
|
|
|
- height: 48, // 图片高
|
|
|
|
- show: true,
|
|
|
|
- horizontalOrigin: SkyScenery.HorizontalOrigin.CENTER,
|
|
|
|
- verticalOrigin: SkyScenery.VerticalOrigin.BOTTOM
|
|
|
|
|
|
+ const humanAVelOrient = new SkyScenery.VelocityOrientationProperty(humanAPosition);
|
|
|
|
+ let humanALastOrient = SkyScenery.Quaternion.IDENTITY.clone();
|
|
|
|
+ humanAEntity.orientation = new SkyScenery.CallbackProperty(function (time) {
|
|
|
|
+ let qVel;
|
|
|
|
+ qVel = humanAVelOrient.getValue(time);
|
|
|
|
+ if (qVel != null) { // 有速度,正常算
|
|
|
|
+ SkyScenery.Quaternion.clone(qVel, humanALastOrient); // 留一份
|
|
|
|
+ } else { // 零速,用缓存
|
|
|
|
+ qVel = humanALastOrient;
|
|
|
|
+ }
|
|
|
|
+ return qVel;
|
|
|
|
+ }, false);
|
|
|
|
+ //part 3: human B stand
|
|
|
|
+ const position = SkyScenery.Cartesian3.fromDegrees(this.humanBLocation[0], this.humanBLocation[1], this.humanBLocation[2] || 0);
|
|
|
|
+
|
|
|
|
+ const humanBEntity = viewer.entities.add({
|
|
|
|
+ id: 'humanB',
|
|
|
|
+ name: 'humanB',
|
|
|
|
+ position: position,
|
|
|
|
+ orientation: SkyScenery.Transforms.headingPitchRollQuaternion(position, new SkyScenery.HeadingPitchRoll(SkyScenery.Math.toRadians(90), 0, 0)),
|
|
|
|
+ model: {
|
|
|
|
+ uri: this.humanBModelPath,
|
|
|
|
+ minimumPixelSize: 32,
|
|
|
|
+ maximumScale: 20,
|
|
|
|
+ runAnimations: false
|
|
},
|
|
},
|
|
label: {
|
|
label: {
|
|
- text: "这是自定义图片", // 设置文字内容
|
|
|
|
- font: "normal 18px 楷体", // 设置文字大小和字体
|
|
|
|
- fillColor: SkyScenery.Color.fromCssColorString("#00ff00"), //设置文字填充的颜色
|
|
|
|
|
|
+ font: "normal 18px 楷体", //设置文字大小和字体
|
|
|
|
+ fillColor: SkyScenery.Color.fromCssColorString("#f0f0f0"), //设置文字填充的颜色
|
|
horizontalOrigin: SkyScenery.HorizontalOrigin.CENTER,
|
|
horizontalOrigin: SkyScenery.HorizontalOrigin.CENTER,
|
|
verticalOrigin: SkyScenery.VerticalOrigin.BOTTOM,
|
|
verticalOrigin: SkyScenery.VerticalOrigin.BOTTOM,
|
|
- pixelOffset: new SkyScenery.Cartesian2(0, -50)
|
|
|
|
|
|
+ pixelOffset: new SkyScenery.Cartesian2(0, -120),
|
|
|
|
+ showBackground: true,
|
|
|
|
+ backgroundColor: SkyScenery.Color.fromCssColorString("#00000080"),
|
|
}
|
|
}
|
|
});
|
|
});
|
|
- let positions = [
|
|
|
|
- [121.04829640102727, 31.12735759260756],
|
|
|
|
- [121.05219953077487, 31.126058264888133],
|
|
|
|
- [121.0556573133586, 31.125143117515066],
|
|
|
|
- [121.0585577885634, 31.12489903339664],
|
|
|
|
- [121.05855842602281, 31.117437091290032],
|
|
|
|
- [121.05755469646111, 31.116865171387122],
|
|
|
|
- [121.05855867110577, 31.114567660254956],
|
|
|
|
- [121.05716503781966, 31.111412856109656],
|
|
|
|
- [121.05169919616742, 31.109414349328368],
|
|
|
|
- [121.04846566530048, 31.10731617798831],
|
|
|
|
- [121.04523257218045, 31.105074455848328],
|
|
|
|
- [121.04372646220537, 31.108713847196935],
|
|
|
|
- [121.04238895034425, 31.11239990163383],
|
|
|
|
- [121.04216536839769, 31.11503097997496],
|
|
|
|
- [121.03965751482633, 31.117141773623022],
|
|
|
|
- [121.0392664980893, 31.119247569299414],
|
|
|
|
- [121.0401584271992, 31.124267692010815],
|
|
|
|
- [121.04534263782213, 31.124207732078528],
|
|
|
|
- [121.04813004860797, 31.124919266167907],
|
|
|
|
- [121.04829640102727, 31.12735759260756]
|
|
|
|
- ];
|
|
|
|
|
|
+ },
|
|
|
|
+ startAnimation() {
|
|
|
|
+ if (this.isRunning) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ this.isRunning = true;
|
|
|
|
+
|
|
|
|
+ const that = this;
|
|
|
|
+
|
|
|
|
+ viewer.clock.currentTime = viewer.clock.startTime.clone();
|
|
|
|
+ viewer.clock.shouldAnimate = true; //开始动画
|
|
|
|
+
|
|
|
|
+ this.runningClockTickEventRemover = viewer.clock.onTick.addEventListener(function (clock) {
|
|
|
|
+
|
|
|
|
+ //set stopper
|
|
|
|
+ if (SkyScenery.JulianDate.greaterThanOrEquals(clock.currentTime, clock.stopTime)) {
|
|
|
|
+ that.stopAnimation(true);
|
|
|
|
+ that.nomalyEnd();
|
|
|
|
+ }
|
|
|
|
+ const car = viewer.entities.getById('car');
|
|
|
|
+ //track entity
|
|
|
|
+ if (SkyScenery.JulianDate.greaterThan(clock.currentTime, clock.startTime) && SkyScenery.JulianDate.lessThan(clock.currentTime, that.carStopTime)) {//fast then car stop
|
|
|
|
+ viewer.trackedEntity = car;
|
|
|
|
+ car.model.runAnimations = true;
|
|
|
|
+ } else if (SkyScenery.JulianDate.lessThan(clock.currentTime, that.humanAStopTime)) {//fast then human A stop
|
|
|
|
+ viewer.trackedEntity = viewer.entities.getById('humanA');
|
|
|
|
+
|
|
|
|
+ car.model.runAnimations = false;
|
|
|
|
+ } else if (SkyScenery.JulianDate.lessThan(clock.currentTime, that.talkStopTime)) {//fast then talk stop
|
|
|
|
+ viewer.trackedEntity = viewer.entities.getById('humanA');
|
|
|
|
+
|
|
|
|
+ car.model.runAnimations = false;
|
|
|
|
+ } else {
|
|
|
|
+ viewer.trackedEntity = undefined;
|
|
|
|
+
|
|
|
|
+ car.model.runAnimations = false;
|
|
|
|
+ }
|
|
|
|
+ //show talk
|
|
|
|
+ const talkerA = viewer.entities.getById('humanA');
|
|
|
|
+ const talkerB = viewer.entities.getById('humanB');
|
|
|
|
+ if (SkyScenery.JulianDate.greaterThan(clock.currentTime, that.talkStartTime) && SkyScenery.JulianDate.lessThan(clock.currentTime, that.talkStopTime)) {
|
|
|
|
+ const talkTimeSec = SkyScenery.JulianDate.secondsDifference(clock.currentTime, that.talkStartTime);
|
|
|
|
+ let talkTextA = "";
|
|
|
|
+ let talkTextB = "";
|
|
|
|
+ that.humanASpeakText.forEach(text => {
|
|
|
|
+ if (talkTimeSec >= text.startTime && talkTimeSec <= text.endTime) {
|
|
|
|
+ talkTextA = text.text;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ that.humanBSpeakText.forEach(text => {
|
|
|
|
+ if (talkTimeSec >= text.startTime && talkTimeSec <= text.endTime) {
|
|
|
|
+ talkTextB = text.text;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ if (talkTextA) {
|
|
|
|
+ talkerA.label.text = talkTextA;
|
|
|
|
+ } else {
|
|
|
|
+ talkerA.label.text = "";
|
|
|
|
+ }
|
|
|
|
+ if (talkTextB) {
|
|
|
|
+ talkerB.label.text = talkTextB;
|
|
|
|
+ } else {
|
|
|
|
+ talkerB.label.text = "";
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ talkerA.label.text = "";
|
|
|
|
+ talkerB.label.text = "";
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ stopAnimation(force) {
|
|
|
|
+ if (!(force || this.isRunning)) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ this.isRunning = false;
|
|
|
|
+
|
|
|
|
+ viewer.clock.shouldAnimate = false; //停止动画
|
|
|
|
+
|
|
|
|
+ if (this.runningClockTickEventRemover) {
|
|
|
|
+ this.runningClockTickEventRemover();
|
|
|
|
+ this.runningClockTickEventRemover = undefined;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ nomalyEnd() {
|
|
|
|
+ this.endPanelVisible = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
@@ -201,4 +489,141 @@ export default {
|
|
height: 100%;
|
|
height: 100%;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+.compact-control {
|
|
|
|
+ background: rgba(255, 255, 255, 0.95);
|
|
|
|
+ border-radius: 10px;
|
|
|
|
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
|
|
|
|
+ padding: 12px;
|
|
|
|
+ width: 360px;
|
|
|
|
+ display: inline-block;
|
|
|
|
+ backdrop-filter: blur(5px);
|
|
|
|
+ border: 1px solid rgba(255, 255, 255, 0.2);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.control-header {
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
+ padding-bottom: 8px;
|
|
|
|
+ border-bottom: 1px solid #eaeaea;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.control-header i {
|
|
|
|
+ font-size: 16px;
|
|
|
|
+ color: #3498db;
|
|
|
|
+ margin-right: 8px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.control-header span {
|
|
|
|
+ font-weight: 600;
|
|
|
|
+ color: #2c3e50;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.tiles-row {
|
|
|
|
+ display: flex;
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
+ gap: 6px;
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
+ max-width: 300px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.tile-btn {
|
|
|
|
+ display: inline-flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ background: #3498db;
|
|
|
|
+ color: white;
|
|
|
|
+ border: none;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ padding: 6px 10px;
|
|
|
|
+ font-size: 13px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ transition: all 0.2s ease;
|
|
|
|
+ min-width: 40px;
|
|
|
|
+ height: 28px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.tile-btn:hover {
|
|
|
|
+ background: #2980b9;
|
|
|
|
+ transform: translateY(-1px);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.tile-btn:active {
|
|
|
|
+ transform: translateY(0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.roam-btn {
|
|
|
|
+ display: inline-flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ background: #2ecc71;
|
|
|
|
+ color: white;
|
|
|
|
+ border: none;
|
|
|
|
+ border-radius: 4px;
|
|
|
|
+ padding: 6px 10px;
|
|
|
|
+ font-size: 13px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ transition: all 0.2s ease;
|
|
|
|
+ height: 28px;
|
|
|
|
+ width: 50%;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.roam-btn:hover {
|
|
|
|
+ background: #27ae60;
|
|
|
|
+ transform: translateY(-1px);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.roam-btn:active {
|
|
|
|
+ transform: translateY(0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.roam-btn i {
|
|
|
|
+ margin-right: 5px;
|
|
|
|
+ font-size: 12px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.completion-check {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ margin: 15px 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.check-circle {
|
|
|
|
+ width: 60px;
|
|
|
|
+ height: 60px;
|
|
|
|
+ border-radius: 50%;
|
|
|
|
+ background: #2ecc71;
|
|
|
|
+ display: flex;
|
|
|
|
+ align-items: center;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ color: white;
|
|
|
|
+ font-size: 28px;
|
|
|
|
+ box-shadow: 0 4px 10px rgba(46, 204, 113, 0.3);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.completion-message {
|
|
|
|
+ text-align: center;
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.completion-message h3 {
|
|
|
|
+ color: #2c3e50;
|
|
|
|
+ margin: 0 0 8px 0;
|
|
|
|
+ font-size: 18px;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.completion-message p {
|
|
|
|
+ color: #7f8c8d;
|
|
|
|
+ margin: 0;
|
|
|
|
+ font-size: 14px;
|
|
|
|
+ line-height: 1.5;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+.completion-actions {
|
|
|
|
+ display: flex;
|
|
|
|
+ justify-content: center;
|
|
|
|
+ gap: 10px;
|
|
|
|
+}
|
|
</style>
|
|
</style>
|