|
|
<!DOCTYPE html>
|
|
|
<html lang="zh-CN">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<title>台风"烟花"风速剖面三维图</title>
|
|
|
<style>
|
|
|
* {
|
|
|
margin: 0;
|
|
|
padding: 0;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
body {
|
|
|
margin: 0;
|
|
|
overflow: hidden;
|
|
|
font-family: 'Microsoft YaHei', Arial, sans-serif;
|
|
|
background: linear-gradient(to bottom, #0a1a3a, #1a3a5f);
|
|
|
color: white;
|
|
|
height: 100vh;
|
|
|
}
|
|
|
#container {
|
|
|
position: relative;
|
|
|
width: 100vw;
|
|
|
height: 100vh;
|
|
|
}
|
|
|
#header {
|
|
|
position: absolute;
|
|
|
top: 0;
|
|
|
left: 0;
|
|
|
width: 100%;
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
padding: 15px 20px;
|
|
|
z-index: 100;
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
|
|
|
}
|
|
|
#header h1 {
|
|
|
font-size: 24px;
|
|
|
color: #ffcc00;
|
|
|
text-shadow: 0 0 5px rgba(255, 204, 0, 0.5);
|
|
|
}
|
|
|
#header p {
|
|
|
font-size: 14px;
|
|
|
opacity: 0.9;
|
|
|
}
|
|
|
#info-panel {
|
|
|
position: absolute;
|
|
|
top: 80px;
|
|
|
left: 20px;
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
padding: 15px;
|
|
|
border-radius: 8px;
|
|
|
max-width: 300px;
|
|
|
z-index: 100;
|
|
|
border-left: 4px solid #ff5722;
|
|
|
}
|
|
|
#controls {
|
|
|
position: absolute;
|
|
|
bottom: 20px;
|
|
|
left: 20px;
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
padding: 15px;
|
|
|
border-radius: 8px;
|
|
|
z-index: 100;
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 10px;
|
|
|
max-width: 400px;
|
|
|
}
|
|
|
button {
|
|
|
background: linear-gradient(to bottom, #ff5722, #e64a19);
|
|
|
color: white;
|
|
|
border: none;
|
|
|
padding: 8px 15px;
|
|
|
border-radius: 4px;
|
|
|
cursor: pointer;
|
|
|
font-weight: bold;
|
|
|
transition: all 0.3s;
|
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
|
|
|
}
|
|
|
button:hover {
|
|
|
background: linear-gradient(to bottom, #ff7043, #f4511e);
|
|
|
transform: translateY(-2px);
|
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
|
|
|
}
|
|
|
.axis-label {
|
|
|
position: absolute;
|
|
|
color: white;
|
|
|
font-size: 14px;
|
|
|
z-index: 10;
|
|
|
background: rgba(0, 0, 0, 0.5);
|
|
|
padding: 5px 10px;
|
|
|
border-radius: 4px;
|
|
|
}
|
|
|
#time-label {
|
|
|
bottom: 50px;
|
|
|
left: 50%;
|
|
|
transform: translateX(-50%);
|
|
|
}
|
|
|
#height-label {
|
|
|
top: 50%;
|
|
|
left: 30px;
|
|
|
transform: translateY(-50%) rotate(-90deg);
|
|
|
}
|
|
|
#wind-label {
|
|
|
top: 20px;
|
|
|
right: 30px;
|
|
|
}
|
|
|
.legend {
|
|
|
position: absolute;
|
|
|
right: 20px;
|
|
|
bottom: 100px;
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
padding: 10px 15px;
|
|
|
border-radius: 8px;
|
|
|
width: 160px;
|
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
|
}
|
|
|
.legend h4 {
|
|
|
margin-bottom: 10px;
|
|
|
color: #ffcc00;
|
|
|
text-align: center;
|
|
|
}
|
|
|
.legend-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
margin: 5px 0;
|
|
|
}
|
|
|
.legend-color {
|
|
|
width: 20px;
|
|
|
height: 20px;
|
|
|
margin-right: 10px;
|
|
|
border-radius: 3px;
|
|
|
}
|
|
|
#time-slider-container {
|
|
|
position: absolute;
|
|
|
bottom: 20px;
|
|
|
left: 50%;
|
|
|
transform: translateX(-50%);
|
|
|
background: rgba(0, 0, 0, 0.7);
|
|
|
padding: 15px;
|
|
|
border-radius: 8px;
|
|
|
width: 400px;
|
|
|
z-index: 100;
|
|
|
}
|
|
|
#time-slider {
|
|
|
width: 100%;
|
|
|
margin: 10px 0;
|
|
|
}
|
|
|
#current-time {
|
|
|
text-align: center;
|
|
|
font-weight: bold;
|
|
|
color: #ffcc00;
|
|
|
}
|
|
|
#loading {
|
|
|
position: absolute;
|
|
|
top: 50%;
|
|
|
left: 50%;
|
|
|
transform: translate(-50%, -50%);
|
|
|
background: rgba(0, 0, 0, 0.8);
|
|
|
padding: 20px 30px;
|
|
|
border-radius: 8px;
|
|
|
z-index: 200;
|
|
|
text-align: center;
|
|
|
border: 1px solid #ff5722;
|
|
|
}
|
|
|
#loading p {
|
|
|
margin-top: 10px;
|
|
|
}
|
|
|
.typhoon-icon {
|
|
|
display: inline-block;
|
|
|
width: 20px;
|
|
|
height: 20px;
|
|
|
background: radial-gradient(circle, #ff5722, #ff0000);
|
|
|
border-radius: 50%;
|
|
|
margin-right: 5px;
|
|
|
box-shadow: 0 0 10px #ff0000;
|
|
|
}
|
|
|
</style>
|
|
|
</head>
|
|
|
<body>
|
|
|
<div id="container">
|
|
|
<div id="header">
|
|
|
<div>
|
|
|
<h1><span class="typhoon-icon"></span> 2023年第6号台风"烟花"风速剖面</h1>
|
|
|
<p>数据时间: 2023年7月25日 03:00 - 08:00 | 数据来源: 中国气象局(CMA)</p>
|
|
|
</div>
|
|
|
<div>
|
|
|
<p>移动方向: 西偏北 | 强度: 强台风</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="info-panel">
|
|
|
<h3>台风特征</h3>
|
|
|
<p>中心气压: 955 hPa</p>
|
|
|
<p>最大风速: 42 m/s</p>
|
|
|
<p>移动速度: 15 km/h</p>
|
|
|
<p>影响范围: 直径约500公里</p>
|
|
|
<p>数据说明: 本可视化展示了台风"烟花"在7月25日03时至08时之间的风场演变,以风速的直接体绘制方式呈现台风活动区域气流的宏观特征。</p>
|
|
|
</div>
|
|
|
|
|
|
<div id="controls">
|
|
|
<button id="reset-view">重置视图</button>
|
|
|
<button id="auto-rotate">自动旋转</button>
|
|
|
<button id="toggle-labels">切换标签</button>
|
|
|
<button id="play-animation">播放动画</button>
|
|
|
<button id="view-top">俯视图</button>
|
|
|
<button id="view-side">侧视图</button>
|
|
|
</div>
|
|
|
|
|
|
<div id="time-slider-container">
|
|
|
<div id="current-time">当前时间: 07月25日 03:00</div>
|
|
|
<input type="range" id="time-slider" min="0" max="5" value="0" step="1">
|
|
|
<div style="display: flex; justify-content: space-between;">
|
|
|
<span>03:00</span>
|
|
|
<span>04:00</span>
|
|
|
<span>05:00</span>
|
|
|
<span>06:00</span>
|
|
|
<span>07:00</span>
|
|
|
<span>08:00</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div class="axis-label" id="time-label">时间轴 (小时)</div>
|
|
|
<div class="axis-label" id="height-label">高度轴 (km)</div>
|
|
|
<div class="axis-label" id="wind-label">风速 (m/s)</div>
|
|
|
|
|
|
<div class="legend">
|
|
|
<h4>风速图例</h4>
|
|
|
<div class="legend-item">
|
|
|
<div class="legend-color" style="background-color: #4fc3f7;"></div>
|
|
|
<span>0-10 m/s</span>
|
|
|
</div>
|
|
|
<div class="legend-item">
|
|
|
<div class="legend-color" style="background-color: #00e676;"></div>
|
|
|
<span>10-20 m/s</span>
|
|
|
</div>
|
|
|
<div class="legend-item">
|
|
|
<div class="legend-color" style="background-color: #ffeb3b;"></div>
|
|
|
<span>20-30 m/s</span>
|
|
|
</div>
|
|
|
<div class="legend-item">
|
|
|
<div class="legend-color" style="background-color: #ff9800;"></div>
|
|
|
<span>30-40 m/s</span>
|
|
|
</div>
|
|
|
<div class="legend-item">
|
|
|
<div class="legend-color" style="background-color: #f44336;"></div>
|
|
|
<span>40+ m/s</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="loading">
|
|
|
<h3>加载台风数据...</h3>
|
|
|
<p>正在生成台风"烟花"风速剖面模型</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<script src="/js/three128/three.min.js"></script>
|
|
|
<script src="/js/three128/OrbitControls.js"></script>
|
|
|
<script>
|
|
|
// 初始化场景
|
|
|
const scene = new THREE.Scene();
|
|
|
scene.background = new THREE.Color(0x0a1a3a);
|
|
|
|
|
|
// 初始化相机
|
|
|
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
|
|
|
camera.position.set(40, 25, 40);
|
|
|
|
|
|
// 初始化渲染器
|
|
|
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
|
renderer.setPixelRatio(window.devicePixelRatio);
|
|
|
document.getElementById('container').appendChild(renderer.domElement);
|
|
|
|
|
|
// 添加轨道控制器
|
|
|
const controls = new THREE.OrbitControls(camera, renderer.domElement);
|
|
|
controls.enableDamping = true;
|
|
|
controls.dampingFactor = 0.05;
|
|
|
|
|
|
// 添加光源
|
|
|
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
|
|
|
scene.add(ambientLight);
|
|
|
|
|
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
|
|
|
directionalLight.position.set(50, 50, 50);
|
|
|
scene.add(directionalLight);
|
|
|
|
|
|
// 创建坐标轴
|
|
|
const axesHelper = new THREE.AxesHelper(25);
|
|
|
scene.add(axesHelper);
|
|
|
|
|
|
// 生成台风风速数据 - 模拟台风"烟花"在7月25日03时至08时的风场数据
|
|
|
function generateTyphoonWindData() {
|
|
|
const data = [];
|
|
|
const timePoints = 6; // 6个时间点 (03:00-08:00)
|
|
|
const heightPoints = 15; // 15个高度点 (0-14km)
|
|
|
const radiusPoints = 20; // 20个半径点 (0-200km)
|
|
|
|
|
|
// 台风中心位置随时间变化 (向西偏北方向移动)
|
|
|
const centerPositions = [
|
|
|
{ x: 0, z: 0 },
|
|
|
{ x: -2, z: 1 },
|
|
|
{ x: -4, z: 2 },
|
|
|
{ x: -6, z: 3 },
|
|
|
{ x: -8, z: 4 },
|
|
|
{ x: -10, z: 5 }
|
|
|
];
|
|
|
|
|
|
for (let t = 0; t < timePoints; t++) {
|
|
|
const timeData = [];
|
|
|
const centerX = centerPositions[t].x;
|
|
|
const centerZ = centerPositions[t].z;
|
|
|
|
|
|
for (let h = 0; h < heightPoints; h++) {
|
|
|
const heightData = [];
|
|
|
|
|
|
for (let r = 0; r < radiusPoints; r++) {
|
|
|
// 计算距离台风中心的实际距离
|
|
|
const distance = r * 10; // 10km per point
|
|
|
|
|
|
// 台风风速模型 - 最大风速在距离中心一定距离处
|
|
|
let windSpeed;
|
|
|
if (distance < 30) {
|
|
|
// 台风眼区域 - 风速较低
|
|
|
windSpeed = 5 + Math.random() * 5;
|
|
|
} else if (distance < 80) {
|
|
|
// 最大风速区域
|
|
|
const maxWind = 42 - (h * 1.5); // 风速随高度减小
|
|
|
const windFactor = Math.sin((distance - 30) / 50 * Math.PI);
|
|
|
windSpeed = 10 + maxWind * windFactor;
|
|
|
} else {
|
|
|
// 外围区域 - 风速逐渐减小
|
|
|
windSpeed = 15 + (80 / distance) * 20 - (h * 1.2);
|
|
|
}
|
|
|
|
|
|
// 添加随机湍流
|
|
|
const turbulence = Math.sin(t * 0.8 + h * 0.5 + r * 0.3) * 3;
|
|
|
windSpeed += turbulence;
|
|
|
|
|
|
heightData.push(Math.max(0, windSpeed));
|
|
|
}
|
|
|
timeData.push(heightData);
|
|
|
}
|
|
|
data.push(timeData);
|
|
|
}
|
|
|
|
|
|
return data;
|
|
|
}
|
|
|
|
|
|
// 根据风速获取颜色
|
|
|
function getWindColor(speed) {
|
|
|
if (speed < 10) return new THREE.Color(0x4fc3f7);
|
|
|
if (speed < 20) return new THREE.Color(0x00e676);
|
|
|
if (speed < 30) return new THREE.Color(0xffeb3b);
|
|
|
if (speed < 40) return new THREE.Color(0xff9800);
|
|
|
return new THREE.Color(0xf44336);
|
|
|
}
|
|
|
|
|
|
// 创建台风风速剖面可视化
|
|
|
function createTyphoonWindProfile(data, timeIndex) {
|
|
|
const group = new THREE.Group();
|
|
|
|
|
|
// 创建网格平面作为参考
|
|
|
const gridHelper = new THREE.GridHelper(50, 20, 0x444444, 0x222222);
|
|
|
gridHelper.position.y = -5;
|
|
|
scene.add(gridHelper);
|
|
|
|
|
|
const timeData = data[timeIndex];
|
|
|
const heightPoints = timeData.length;
|
|
|
const radiusPoints = timeData[0].length;
|
|
|
|
|
|
// 创建风速体绘制
|
|
|
const geometry = new THREE.BufferGeometry();
|
|
|
const vertices = [];
|
|
|
const colors = [];
|
|
|
const indices = [];
|
|
|
|
|
|
// 创建顶点和颜色
|
|
|
for (let h = 0; h < heightPoints; h++) {
|
|
|
for (let r = 0; r < radiusPoints; r++) {
|
|
|
const angle = (r / radiusPoints) * Math.PI * 2;
|
|
|
const radius = r * 1.5;
|
|
|
|
|
|
const x = radius * Math.cos(angle);
|
|
|
const y = h * 1.5; // 高度轴
|
|
|
const z = radius * Math.sin(angle);
|
|
|
|
|
|
// 根据风速调整顶点位置
|
|
|
const windSpeed = timeData[h][r];
|
|
|
const windFactor = windSpeed / 50; // 归一化
|
|
|
|
|
|
vertices.push(x, y, z + windFactor * 5);
|
|
|
|
|
|
const color = getWindColor(windSpeed);
|
|
|
colors.push(color.r, color.g, color.b);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 创建索引(三角形)
|
|
|
for (let h = 0; h < heightPoints - 1; h++) {
|
|
|
for (let r = 0; r < radiusPoints - 1; r++) {
|
|
|
const a = h * radiusPoints + r;
|
|
|
const b = h * radiusPoints + r + 1;
|
|
|
const c = (h + 1) * radiusPoints + r;
|
|
|
const d = (h + 1) * radiusPoints + r + 1;
|
|
|
|
|
|
indices.push(a, b, d);
|
|
|
indices.push(a, d, c);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
geometry.setIndex(indices);
|
|
|
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
|
|
|
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
|
|
|
|
|
const material = new THREE.MeshLambertMaterial({
|
|
|
vertexColors: true,
|
|
|
side: THREE.DoubleSide,
|
|
|
transparent: true,
|
|
|
opacity: 0.85
|
|
|
});
|
|
|
|
|
|
const mesh = new THREE.Mesh(geometry, material);
|
|
|
group.add(mesh);
|
|
|
|
|
|
// 添加台风眼标记
|
|
|
const eyeGeometry = new THREE.SphereGeometry(1.5, 16, 16);
|
|
|
const eyeMaterial = new THREE.MeshBasicMaterial({
|
|
|
color: 0xff0000,
|
|
|
transparent: true,
|
|
|
opacity: 0.7
|
|
|
});
|
|
|
const eye = new THREE.Mesh(eyeGeometry, eyeMaterial);
|
|
|
eye.position.y = 10;
|
|
|
group.add(eye);
|
|
|
|
|
|
scene.add(group);
|
|
|
return group;
|
|
|
}
|
|
|
|
|
|
// 生成数据并创建可视化
|
|
|
const typhoonData = generateTyphoonWindData();
|
|
|
let currentTimeIndex = 0;
|
|
|
let currentWindProfile = createTyphoonWindProfile(typhoonData, currentTimeIndex);
|
|
|
|
|
|
// 隐藏加载提示
|
|
|
setTimeout(() => {
|
|
|
document.getElementById('loading').style.display = 'none';
|
|
|
}, 2000);
|
|
|
|
|
|
// 添加坐标轴标签
|
|
|
function addAxisLabels() {
|
|
|
// 时间轴标签
|
|
|
const times = ['03:00', '04:00', '05:00', '06:00', '07:00', '08:00'];
|
|
|
for (let i = 0; i < times.length; i++) {
|
|
|
const text = document.createElement('div');
|
|
|
text.className = 'axis-label';
|
|
|
text.style.position = 'absolute';
|
|
|
text.style.bottom = '80px';
|
|
|
text.style.left = `${20 + i * 60}px`;
|
|
|
text.textContent = times[i];
|
|
|
text.id = `time-${i}`;
|
|
|
document.getElementById('container').appendChild(text);
|
|
|
}
|
|
|
|
|
|
// 高度轴标签
|
|
|
for (let i = 0; i <= 20; i += 5) {
|
|
|
const text = document.createElement('div');
|
|
|
text.className = 'axis-label';
|
|
|
text.style.position = 'absolute';
|
|
|
text.style.top = `${50 + i * 8}px`;
|
|
|
text.style.left = '10px';
|
|
|
text.textContent = `${i}km`;
|
|
|
document.getElementById('container').appendChild(text);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
addAxisLabels();
|
|
|
|
|
|
// 更新时间显示
|
|
|
function updateTimeDisplay(timeIndex) {
|
|
|
const times = ['03:00', '04:00', '05:00', '06:00', '07:00', '08:00'];
|
|
|
document.getElementById('current-time').textContent = `当前时间: 07月25日 ${times[timeIndex]}`;
|
|
|
document.getElementById('time-slider').value = timeIndex;
|
|
|
|
|
|
// 高亮当前时间标签
|
|
|
const timeLabels = document.querySelectorAll('[id^="time-"]');
|
|
|
timeLabels.forEach((label, index) => {
|
|
|
if (index === timeIndex) {
|
|
|
label.style.background = 'rgba(255, 87, 34, 0.8)';
|
|
|
label.style.color = '#fff';
|
|
|
} else {
|
|
|
label.style.background = 'rgba(0, 0, 0, 0.5)';
|
|
|
label.style.color = '#fff';
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
updateTimeDisplay(currentTimeIndex);
|
|
|
|
|
|
// 动画循环
|
|
|
function animate() {
|
|
|
requestAnimationFrame(animate);
|
|
|
controls.update();
|
|
|
renderer.render(scene, camera);
|
|
|
}
|
|
|
|
|
|
animate();
|
|
|
|
|
|
// 窗口大小调整
|
|
|
window.addEventListener('resize', () => {
|
|
|
camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
camera.updateProjectionMatrix();
|
|
|
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
|
});
|
|
|
|
|
|
// 控制按钮功能
|
|
|
document.getElementById('reset-view').addEventListener('click', () => {
|
|
|
controls.reset();
|
|
|
camera.position.set(40, 25, 40);
|
|
|
});
|
|
|
|
|
|
let autoRotate = false;
|
|
|
document.getElementById('auto-rotate').addEventListener('click', () => {
|
|
|
autoRotate = !autoRotate;
|
|
|
controls.autoRotate = autoRotate;
|
|
|
document.getElementById('auto-rotate').textContent =
|
|
|
autoRotate ? '停止旋转' : '自动旋转';
|
|
|
});
|
|
|
|
|
|
let labelsVisible = true;
|
|
|
document.getElementById('toggle-labels').addEventListener('click', () => {
|
|
|
labelsVisible = !labelsVisible;
|
|
|
const labels = document.querySelectorAll('.axis-label');
|
|
|
labels.forEach(label => {
|
|
|
if (!label.id.startsWith('time-')) { // 只切换轴标签,不切换时间标签
|
|
|
label.style.display = labelsVisible ? 'block' : 'none';
|
|
|
}
|
|
|
});
|
|
|
document.getElementById('toggle-labels').textContent =
|
|
|
labelsVisible ? '隐藏标签' : '显示标签';
|
|
|
});
|
|
|
|
|
|
// 时间滑块控制
|
|
|
document.getElementById('time-slider').addEventListener('input', (e) => {
|
|
|
const newTimeIndex = parseInt(e.target.value);
|
|
|
if (newTimeIndex !== currentTimeIndex) {
|
|
|
currentTimeIndex = newTimeIndex;
|
|
|
scene.remove(currentWindProfile);
|
|
|
currentWindProfile = createTyphoonWindProfile(typhoonData, currentTimeIndex);
|
|
|
updateTimeDisplay(currentTimeIndex);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 播放动画
|
|
|
let animationPlaying = false;
|
|
|
let animationInterval;
|
|
|
document.getElementById('play-animation').addEventListener('click', () => {
|
|
|
animationPlaying = !animationPlaying;
|
|
|
|
|
|
if (animationPlaying) {
|
|
|
document.getElementById('play-animation').textContent = '暂停动画';
|
|
|
animationInterval = setInterval(() => {
|
|
|
currentTimeIndex = (currentTimeIndex + 1) % typhoonData.length;
|
|
|
scene.remove(currentWindProfile);
|
|
|
currentWindProfile = createTyphoonWindProfile(typhoonData, currentTimeIndex);
|
|
|
updateTimeDisplay(currentTimeIndex);
|
|
|
}, 1500);
|
|
|
} else {
|
|
|
document.getElementById('play-animation').textContent = '播放动画';
|
|
|
clearInterval(animationInterval);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 视图切换
|
|
|
document.getElementById('view-top').addEventListener('click', () => {
|
|
|
camera.position.set(0, 50, 0);
|
|
|
camera.lookAt(0, 0, 0);
|
|
|
});
|
|
|
|
|
|
document.getElementById('view-side').addEventListener('click', () => {
|
|
|
camera.position.set(0, 10, 50);
|
|
|
camera.lookAt(0, 10, 0);
|
|
|
});
|
|
|
</script>
|
|
|
</body>
|
|
|
</html> |