星驰编程网

免费编程资源分享平台_编程教程_代码示例_开发技术文章

ECharts 三维轨迹旋转的暂停与继续功能实现

概述

本笔记将详细记录如何使用 ECharts 和 ECharts-GL 创建一个具有旋转控制功能的三维轨迹图表。

环境准备

1. 引入必要的库

首先需要引入 ECharts 核心库和 ECharts-GL 扩展库:

<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts-gl@2.0.9/dist/echarts-gl.min.js"></script>

2. HTML 结构

创建基本的 HTML 结构:

<div class="container">
    <div id="chart" style="width: 100%; height: 600px;"></div>
    <div class="controls">
        <!-- 控制按钮 -->
    </div>
</div>

核心技术解析

1. 三维坐标系统

ECharts-GL 使用右手坐标系:

oX轴:水平方向(左右)oY轴:垂直方向(上下)oZ轴:深度方向(前后)

2. 视角控制参数

viewControl: {
    alpha: 20,    // 上下旋转角度
    beta: 40,     // 左右旋转角度
    distance: 200, // 视角距离
    autoRotate: true,      // 自动旋转
    autoRotateSpeed: 10    // 旋转速度
}

3. 动画实现原理

使用 requestAnimationFrame 实现平滑动画:

function rotateChart() {
    if (isRotating) {
        currentAngle += rotationSpeed;
        // 更新视角
        chart.setOption({...});
    }
    animationId = requestAnimationFrame(rotateChart);
}

代码实现详解

1. 初始化图表

// 创建 ECharts 实例
const chart = echarts.init(document.getElementById('chart'));


// 基础配置
const option = {
    backgroundColor: '#000',
    grid3D: {
        boxWidth: 100,
        boxHeight: 100,
        boxDepth: 100,
        environment: 'auto',
        light: {
            main: {
                intensity: 1.2,
                shadow: true
            },
            ambient: {
                intensity: 0.3
            }
        }
    }
};

2. 数据生成算法

螺旋线轨迹生成

function generateSpiralData() {
    const data = [];
    for (let i = 0; i <= 1000; i++) {
        const t = i * 0.01;
        const x = Math.cos(t) * (10 + t * 0.1);  // 螺旋半径递增
        const y = Math.sin(t) * (10 + t * 0.1);  // 螺旋半径递增
        const z = t * 0.5;                       // 高度线性增长
        data.push([x, y, z]);
    }
    return data;
}

数学原理

o使用参数方程 [x = r(t) \cos(t)],[y = r(t) \sin(t)],[z = h(t)]o[r(t) = 10 + t \times 0.1] 实现螺旋半径递增o[h(t) = t \times 0.5] 实现高度线性增长

随机散点生成

function generateScatterData() {
    const data = [];
    for (let i = 0; i < 200; i++) {
        const x = (Math.random() - 0.5) * 50;
        const y = (Math.random() - 0.5) * 50;
        const z = (Math.random() - 0.5) * 50;
        const value = Math.sqrt(x*x + y*y + z*z);  // 计算到原点距离
        data.push([x, y, z, value]);
    }
    return data;
}

3. 旋转控制实现

核心旋转函数

let isRotating = true;
let rotationSpeed = 1;
let currentAngle = 0;


function rotateChart() {
    if (isRotating) {
        currentAngle += rotationSpeed;
        chart.setOption({
            grid3D: {
                viewControl: {
                    alpha: 20 + Math.sin(currentAngle * 0.01) * 10,  // 上下摆动
                    beta: currentAngle,                               // 水平旋转
                    autoRotate: false
                }
            }
        });
    }
    animationId = requestAnimationFrame(rotateChart);
}

事件处理

// 图表点击事件
chart.on('click', function(params) {
    isRotating = !isRotating;
    updateStatus();
});


// 按钮控制
document.getElementById('toggleBtn').addEventListener('click', function() {
    isRotating = !isRotating;
    updateStatus();
});

4. 系列配置详解

线条系列(螺旋轨迹)

{
    name: '螺旋轨迹',
    type: 'line3D',
    data: generateSpiralData(),
    lineStyle: {
        width: 4,
        color: '#ff6b6b'
    }
}

散点系列

{
    name: '散点数据',
    type: 'scatter3D',
    data: generateScatterData(),
    symbolSize: 8,
    itemStyle: {
        color: function(params) {
            const value = params.data[3];
            const colors = ['#4ecdc4', '#45b7d1', '#96ceb4', '#feca57', '#ff9ff3'];
            return colors[Math.floor(value / 10) % colors.length];
        },
        opacity: 0.8
    }
}

5. 状态管理

function updateStatus() {
    document.getElementById('statusText').textContent = 
        isRotating ? '旋转中' : '已暂停';
    document.getElementById('speedText').textContent = rotationSpeed + 'x';
    document.getElementById('toggleBtn').textContent = 
        isRotating ? '暂停旋转' : '继续旋转';
}

功能扩展

1. 添加轨迹点动画

function animateTrajectoryPoint() {
    let pointIndex = 0;
    const trajectoryData = generateSpiralData();


    setInterval(() => {
        if (pointIndex < trajectoryData.length) {
            // 更新当前点位置
            chart.setOption({
                series: [{
                    // ... 其他配置
                    markPoint: {
                        data: [{
                            coord: trajectoryData[pointIndex],
                            symbol: 'circle',
                            symbolSize: 15,
                            itemStyle: { color: 'yellow' }
                        }]
                    }
                }]
            });
            pointIndex++;
        } else {
            pointIndex = 0; // 循环播放
        }
    }, 50);
}

2. 添加数据筛选功能

function filterDataByRange(data, minValue, maxValue) {
    return data.filter(item => {
        const value = item[3]; // 假设第4个值是筛选依据
        return value >= minValue && value <= maxValue;
    });
}


// 使用滑块控制数据筛选
document.getElementById('rangeSlider').addEventListener('input', function(e) {
    const range = e.target.value;
    const filteredData = filterDataByRange(originalData, 0, range);


    chart.setOption({
        series: [{
            data: filteredData
        }]
    });
});

3. 添加轨迹回放功能

class TrajectoryPlayer {
    constructor(chart, data) {
        this.chart = chart;
        this.data = data;
        this.currentIndex = 0;
        this.isPlaying = false;
        this.playSpeed = 100; // ms
    }


    play() {
        this.isPlaying = true;
        this.playInterval = setInterval(() => {
            if (this.currentIndex < this.data.length) {
                this.updateTrajectory();
                this.currentIndex++;
            } else {
                this.stop();
            }
        }, this.playSpeed);
    }


    pause() {
        this.isPlaying = false;
        clearInterval(this.playInterval);
    }


    reset() {
        this.currentIndex = 0;
        this.updateTrajectory();
    }


    updateTrajectory() {
        const currentData = this.data.slice(0, this.currentIndex + 1);
        this.chart.setOption({
            series: [{
                data: currentData
            }]
        });
    }
}

性能优化

1. 数据量优化

// 对大数据集进行采样
function sampleData(data, sampleRate = 0.1) {
    return data.filter((_, index) => index % Math.floor(1 / sampleRate) === 0);
}


// 使用 Web Worker 处理大量数据
const worker = new Worker('dataProcessor.js');
worker.postMessage({ data: largeDataSet });
worker.onmessage = function(e) {
    const processedData = e.data;
    chart.setOption({
        series: [{ data: processedData }]
    });
};

2. 渲染优化

// 使用 ECharts 的渐进渲染
const option = {
    progressive: 1000,        // 渐进渲染阈值
    progressiveThreshold: 3000, // 启用渐进渲染的数据量阈值
    // ... 其他配置
};

3. 内存管理

// 清理动画资源
function cleanup() {
    if (animationId) {
        cancelAnimationFrame(animationId);
    }
    if (chart) {
        chart.dispose();
    }
}


// 页面卸载时清理
window.addEventListener('beforeunload', cleanup);

常见问题与解决方案

1. 图表不显示

问题:图表容器没有内容显示

解决方案

// 确保容器有明确的宽高
#chart {
    width: 800px;
    height: 600px;
}


// 确保在 DOM 加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
    const chart = echarts.init(document.getElementById('chart'));
    // ... 其他代码
});

2. 旋转不流畅

问题:旋转动画卡顿或不连续

解决方案

// 使用 requestAnimationFrame 替代 setInterval
function smoothRotate() {
    if (isRotating) {
        currentAngle += rotationSpeed * 0.5; // 调整步长
        updateViewAngle();
    }
    requestAnimationFrame(smoothRotate);
}


// 优化更新频率
let lastUpdateTime = 0;
function throttledUpdate(timestamp) {
    if (timestamp - lastUpdateTime > 16) { // 约60fps
        updateChart();
        lastUpdateTime = timestamp;
    }
    requestAnimationFrame(throttledUpdate);
}

3. 内存泄漏

问题:长时间运行后页面变慢

解决方案

// 正确清理事件监听器
function removeEventListeners() {
    chart.off('click');
    window.removeEventListener('resize', resizeHandler);
}


// 使用 WeakMap 存储临时数据
const chartData = new WeakMap();
chartData.set(chart, { rotationState: isRotating });

4. 响应式适配

问题:在不同屏幕尺寸下显示异常

解决方案

// 响应式调整
function resizeChart() {
    if (chart) {
        chart.resize();


        // 根据屏幕尺寸调整配置
        const isMobile = window.innerWidth < 768;
        chart.setOption({
            grid3D: {
                boxWidth: isMobile ? 50 : 100,
                boxHeight: isMobile ? 50 : 100,
                boxDepth: isMobile ? 50 : 100
            }
        });
    }
}


window.addEventListener('resize', debounce(resizeChart, 300));


// 防抖函数
function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
        const later = () => {
            clearTimeout(timeout);
            func(...args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

总结

1.基础知识:ECharts-GL 的三维图表基础概念和配置2.数据处理:如何生成和处理三维轨迹数据3.动画控制:实现流畅的旋转动画和交互控制4.性能优化:处理大数据量和提升渲染性能的方法5.问题解决:常见问题的诊断和解决方案

综上代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ECharts 三维轨迹旋转控制演示</title>
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/echarts-gl@2.0.9/dist/echarts-gl.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 20px;
            font-family: Arial, sans-serif;
            background-color: #f5f5f5;
        }
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            padding: 20px;
        }
        .title {
            text-align: center;
            color: #333;
            margin-bottom: 20px;
        }
        .chart-container {
            width: 100%;
            height: 600px;
            border: 1px solid #ddd;
            border-radius: 4px;
            position: relative;
        }
        .controls {
            margin-top: 20px;
            text-align: center;
        }
        .btn {
            background: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            margin: 0 10px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        }
        .btn:hover {
            background: #0056b3;
        }
        .btn.pause {
            background: #dc3545;
        }
        .btn.pause:hover {
            background: #c82333;
        }
        .status {
            margin-top: 10px;
            font-size: 14px;
            color: #666;
        }
        .info {
            margin-top: 20px;
            padding: 15px;
            background: #e9ecef;
            border-radius: 4px;
            font-size: 14px;
            line-height: 1.6;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="title">ECharts 三维轨迹旋转控制演示</h1>
        
        <div class="chart-container">
            <div id="chart" style="width: 100%; height: 100%;"></div>
        </div>
        
        <div class="controls">
            <button id="toggleBtn" class="btn">暂停旋转</button>
            <button id="resetBtn" class="btn">重置视角</button>
            <button id="speedUpBtn" class="btn">加速</button>
            <button id="speedDownBtn" class="btn">减速</button>
        </div>
        
        <div class="status">
            <div>状态: <span id="statusText">旋转中</span></div>
            <div>旋转速度: <span id="speedText">1x</span></div>
        </div>
        
        <div class="info">
            <strong>操作说明:</strong><br>
            o 点击图表任意位置可暂停/继续旋转<br>
            o 使用鼠标拖拽可手动调整视角<br>
            o 滚轮可缩放图表<br>
            o 按钮可控制旋转状态和速度
        </div>
    </div>

    <script>
        // 初始化 ECharts 实例
        const chart = echarts.init(document.getElementById('chart'));
        
        // 旋转控制变量
        let isRotating = true;
        let rotationSpeed = 1;
        let currentAngle = 0;
        let animationId;
        
        // 生成三维轨迹数据(螺旋线)
        function generateSpiralData() {
            const data = [];
            for (let i = 0; i <= 1000; i++) {
                const t = i * 0.01;
                const x = Math.cos(t) * (10 + t * 0.1);
                const y = Math.sin(t) * (10 + t * 0.1);
                const z = t * 0.5;
                data.push([x, y, z]);
            }
            return data;
        }
        
        // 生成随机散点数据
        function generateScatterData() {
            const data = [];
            for (let i = 0; i < 200; i++) {
                const x = (Math.random() - 0.5) * 50;
                const y = (Math.random() - 0.5) * 50;
                const z = (Math.random() - 0.5) * 50;
                const value = Math.sqrt(x*x + y*y + z*z);
                data.push([x, y, z, value]);
            }
            return data;
        }
        
        // 图表配置
        const option = {
            backgroundColor: '#000',
            grid3D: {
                boxWidth: 100,
                boxHeight: 100,
                boxDepth: 100,
                environment: 'auto',
                light: {
                    main: {
                        intensity: 1.2,
                        shadow: true
                    },
                    ambient: {
                        intensity: 0.3
                    }
                },
                viewControl: {
                    alpha: 20,
                    beta: 40,
                    autoRotate: true,
                    autoRotateSpeed: 10
                }
            },
            xAxis3D: {
                type: 'value',
                name: 'X轴',
                nameTextStyle: { color: '#fff' },
                axisLabel: { color: '#fff' }
            },
            yAxis3D: {
                type: 'value',
                name: 'Y轴',
                nameTextStyle: { color: '#fff' },
                axisLabel: { color: '#fff' }
            },
            zAxis3D: {
                type: 'value',
                name: 'Z轴',
                nameTextStyle: { color: '#fff' },
                axisLabel: { color: '#fff' }
            },
            series: [
                {
                    name: '螺旋轨迹',
                    type: 'line3D',
                    data: generateSpiralData(),
                    lineStyle: {
                        width: 4,
                        color: '#ff6b6b'
                    }
                },
                {
                    name: '散点数据',
                    type: 'scatter3D',
                    data: generateScatterData(),
                    symbolSize: 8,
                    itemStyle: {
                        color: function(params) {
                            const value = params.data[3];
                            const colors = ['#4ecdc4', '#45b7d1', '#96ceb4', '#feca57', '#ff9ff3'];
                            return colors[Math.floor(value / 10) % colors.length];
                        },
                        opacity: 0.8
                    }
                }
            ]
        };
        
        // 设置图表配置
        chart.setOption(option);
        
        // 旋转函数
        function rotateChart() {
            if (isRotating) {
                currentAngle += rotationSpeed;
                chart.setOption({
                    grid3D: {
                        viewControl: {
                            alpha: 20 + Math.sin(currentAngle * 0.01) * 10,
                            beta: currentAngle,
                            autoRotate: false
                        }
                    }
                });
            }
            animationId = requestAnimationFrame(rotateChart);
        }
        
        // 开始旋转
        rotateChart();
        
        // 更新状态显示
        function updateStatus() {
            document.getElementById('statusText').textContent = isRotating ? '旋转中' : '已暂停';
            document.getElementById('speedText').textContent = rotationSpeed + 'x';
            document.getElementById('toggleBtn').textContent = isRotating ? '暂停旋转' : '继续旋转';
            document.getElementById('toggleBtn').className = isRotating ? 'btn pause' : 'btn';
        }
        
        // 图表点击事件
        chart.on('click', function(params) {
            isRotating = !isRotating;
            updateStatus();
        });
        
        // 按钮事件
        document.getElementById('toggleBtn').addEventListener('click', function() {
            isRotating = !isRotating;
            updateStatus();
        });
        
        document.getElementById('resetBtn').addEventListener('click', function() {
            currentAngle = 0;
            chart.setOption({
                grid3D: {
                    viewControl: {
                        alpha: 20,
                        beta: 40,
                        autoRotate: false
                    }
                }
            });
        });
        
        document.getElementById('speedUpBtn').addEventListener('click', function() {
            rotationSpeed = Math.min(rotationSpeed + 0.5, 5);
            updateStatus();
        });
        
        document.getElementById('speedDownBtn').addEventListener('click', function() {
            rotationSpeed = Math.max(rotationSpeed - 0.5, 0.5);
            updateStatus();
        });
        
        // 窗口大小改变时重新调整图表
        window.addEventListener('resize', function() {
            chart.resize();
        });
        
        // 初始化状态显示
        updateStatus();
    </script>
</body>
</html>
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言