[TOC]
继 “可视化图表与源代码显示的动态调整” 文章后的功能更新及功能代码分析
完整功能展示如下:
1.分析图表源代码
首先我们先看一下在ECharts
下载的图表示例中拿取到的源代码,我对其进行了详细的注释
<!DOCTYPE html>
<html lang="en" style="height: 100%"> <!-- 设置html元素的高度为100%,以便其子元素可以继承并使用百分比高度 -->
<head>
<meta charset="utf-8"> <!-- 设置文档的字符编码为UTF-8 -->
</head>
<body style="height: 100%; margin: 0"> <!-- 设置body的高度为100%,并移除默认的边距 -->
<div id="container" style="height: 100%"></div> <!-- 创建一个div元素,用于容纳ECharts图表,并设置其高度为100% -->
<!-- 引入ECharts的JavaScript库 -->
<script type="text/javascript" src="https://registry.npmmirror.com/echarts/5.5.1/files/dist/echarts.min.js"></script>
<script type="text/javascript">
var dom = document.getElementById('container'); // 获取用于容纳ECharts图表的div元素
var myChart = echarts.init(dom, null, {
// 初始化ECharts实例
renderer: 'canvas', // 设置渲染器为canvas
useDirtyRect: false // 关闭脏矩形渲染优化
});
var app = {
}; // 这里的代码没有什么作用
var option; // 声明一个变量用于存储图表的配置项
// 定义图表的配置项
option = {
xAxis: {
// X轴配置
type: 'category', // 设置X轴类型为类目轴
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] // 设置X轴数据
},
yAxis: {
// Y轴配置
type: 'value' // 设置Y轴类型为数值轴
},
series: [ // 系列列表配置
{
data: [120, 200, 150, 80, 70, 110, 130], // 设置系列中的数据
type: 'line', // 设置系列类型为折线图
symbol: 'triangle', // 设置系列中数据点的标记形状为三角形
symbolSize: 20, // 设置数据点标记的大小
lineStyle: {
// 设置线条的样式
color: '#5470C6', // 设置线条颜色
width: 4, // 设置线条宽度
type: 'dashed' // 设置线条类型为虚线
},
itemStyle: {
// 设置数据点标记的样式
borderWidth: 3, // 设置标记边框的宽度
borderColor: '#EE6666', // 设置标记边框的颜色
color: 'yellow' // 设置标记的填充颜色
}
}
]
};
// 如果存在配置项且配置项是一个对象,则使用配置项初始化图表
if (option && typeof option === 'object') {
myChart.setOption(option);
}
// 监听窗口大小变化事件,以便在窗口大小变化时更新图表大小
window.addEventListener('resize', myChart.resize);
</script>
</body>
</html>
它的运行效果图是这样的:
2.分析源代码显示功能
在图表显示的基础上,我增添了对图表源代码的显示
源代码显示功能主要依赖于以下几个技术功能点
- 使用
document.documentElement.outerHTML
这个JavaScript
表达式获取到当前页面的完整HTML源代码;将这个源代码字符串设置为#source-code
元素的textContent
属性,这样就会在页面上显示出来。
以下是实现源代码显示功能的代码及解析:
<!-- 显示HTML源码 -->
<pre id="source-code"></pre>
// 设置源代码显示
document.getElementById('source-code').textContent=document.documentElement.outerHTML;
- 用户可以通过点击“下载”按钮,触发
downloadSource()
函数,将源代码保存为一个HTML文件。这个函数创建了一个Blob
对象,并将其URL
设置为一个<a>
标签的href
属性,然后模拟点击这个标签以下载文件
以下是实现源代码下载功能的代码及解析:
<!-- 下载按钮 -->
<button id="downloadButton" onclick="downloadSourceCode()">下载</button>
// 定义一个名为downloadSourceCode的函数,用于下载当前页面的源代码
function downloadSourceCode() {
// 创建一个新的<a>元素,用于模拟点击下载
var element = document.createElement('a');
// 设置<a>元素的href属性,设置其href属性为一个包含当前页面HTML源代码的data URL
// data URL的格式为:data:[<mediatype>][;base64],<data>
// 这里mediatype是text/plain,charset设置为utf-8,data是当前页面的outerHTML,经过encodeURIComponent编码
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(document.documentElement.outerHTML));
// 设置<a>元素的download属性,指定下载文件的名称
element.setAttribute('download', '折线图.html');
// 设置<a>元素的display样式为none,使其在页面上不可见
element.style.display = 'none';
// 将<a>元素添加到文档的body中
document.body.appendChild(element);
// 模拟点击<a>元素,触发下载
element.click();
// 从文档的body中移除<a>元素
document.body.removeChild(element);
}
完整代码参考:
<!DOCTYPE html>
<html lang="en" style="height: 100%"> <!-- 设置html元素的高度为100%,以便其子元素可以继承并使用百分比高度 -->
<head>
<meta charset="utf-8"> <!-- 设置文档的字符编码为UTF-8 -->
<style>
body {
display: flex; /* 使用Flexbox布局 */
height: 100%; /* 设置body的高度为100% */
margin: 0; /* 移除body的默认边距 */
}
#container{
flex: 1; /* 分配相等的空间给容器 */
height: 100%; /* 设置容器的高度为100% */
}
#source-container {
background-color: rgba(111, 231, 35, 0.36); /* 设置背景色以便区分 */
padding: 20px; /* 设置内边距 */
overflow: auto; /* 设置溢出内容为自动滚动 */
flex: 1; /* 分配相等的空间给容器 */
}
#downloadButton {
padding: 10px 20px; /* 设置按钮的内边距 */
background-color: #007bff; /* 设置按钮的背景色 */
color: #1ed5cf; /* 设置按钮的文字颜色 */
border: none; /* 移除按钮的边框 */
border-radius: 5px; /* 设置按钮的边框圆角 */
cursor: pointer; /* 设置鼠标悬停时的光标样式为指针 */
font-size: 16px; /* 设置按钮的文字大小 */
position: absolute; /* 设置按钮的定位方式为绝对定位 */
top: 20px; /* 设置按钮距离容器顶部的距离 */
right: 20px; /* 设置按钮距离容器右侧的距离 */
z-index: 1000; /* 设置按钮的堆叠顺序 */
}
</style>
</head>
<body style="height: 100%; margin: 0"> <!-- 设置body的高度为100%,并移除默认的边距 -->
<div id="container" style="height: 100%"></div> <!-- 创建一个div元素,用于容纳ECharts图表,并设置其高度为100% -->
<div id="source-container">
<pre id="source-code"></pre> <!-- 创建一个pre元素,用于显示页面的HTML源代码 -->
<button id="downloadButton" onclick="downloadSourceCode()">下载</button> <!-- 创建一个按钮,点击时调用downloadSourceCode函数下载页面源代码 -->
</div>
<!-- 引入ECharts的JavaScript库 -->
<script type="text/javascript" src="https://registry.npmmirror.com/echarts/5.5.1/files/dist/echarts.min.js"></script>
<script type="text/javascript">
var dom = document.getElementById('container'); // 获取用于容纳ECharts图表的div元素
var myChart = echarts.init(dom, null, {
// 初始化ECharts实例
renderer: 'canvas', // 设置渲染器为canvas
useDirtyRect: false // 关闭脏矩形渲染优化
});
var option; // 声明一个变量用于存储图表的配置项
// 定义图表的配置项
option = {
xAxis: {
// X轴配置
type: 'category', // 设置X轴类型为类目轴
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] // 设置X轴数据
},
yAxis: {
// Y轴配置
type: 'value' // 设置Y轴类型为数值轴
},
series: [ // 系列列表配置
{
data: [120, 200, 150, 80, 70, 110, 130], // 设置系列中的数据
type: 'line', // 设置系列类型为折线图
symbol: 'triangle', // 设置系列中数据点的标记形状为三角形
symbolSize: 20, // 设置数据点标记的大小
lineStyle: {
// 设置线条的样式
color: '#5470C6', // 设置线条颜色
width: 4, // 设置线条宽度
type: 'dashed' // 设置线条类型为虚线
},
itemStyle: {
// 设置数据点标记的样式
borderWidth: 3, // 设置标记边框的宽度
borderColor: '#EE6666', // 设置标记边框的颜色
color: 'yellow' // 设置标记的填充颜色
}
}
]
};
// 如果存在配置项且配置项是一个对象,则使用配置项初始化图表
if (option && typeof option === 'object') {
myChart.setOption(option);
}
// 监听窗口大小变化事件,以便在窗口大小变化时更新图表大小
window.addEventListener('resize', myChart.resize);
// 设置源代码显示
document.getElementById('source-code').textContent = document.documentElement.outerHTML;
// 下载源代码功能
function downloadSourceCode() {
var element = document.createElement('a'); // 创建一个a元素
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(document.documentElement.outerHTML)); // 设置href属性为包含页面HTML源代码的data URL
element.setAttribute('download', '折线图.html'); // 设置下载文件的名称
element.style.display = 'none'; // 设置a元素不可见
document.body.appendChild(element); // 将a元素添加到文档中
element.click(); // 模拟点击a元素以下载文件
document.body.removeChild(element); // 从文档中移除a元素
}
</script>
</body>
</html>
它的运行效果图是这样的:
3.分析源代码显示及动态调整
在源代码显示的基础上,为了更加方便显示数据量过多的图表,我对右页面的代码显示区域进行了可动态式调整对图表显示区域进行覆盖的功能
右侧代码显示区域的动态化调整主要依赖于以下几个技术功能点:
Flex
布局:#main-container
使用了Flex
布局,这使得其子元素(#chart-container
和#source-container
)能够灵活地分配空间。
以下是实现
Flex
布局的代码及解析:
<div id="main-container">
<div id="chart-container">
<div id="container" style="height: 100%;"></div> <!-- ECharts图表容器 -->
<div id="splitter"></div> <!-- 分割线,用于拖动调整大小 -->
</div>
<div id="source-container">
<pre id="source-code"></pre> <!-- 显示HTML源码 -->
<button id="downloadButton" onclick="downloadSourceCode()">下载</button> <!-- 下载按钮 -->
</div>
</div>
/* 主容器,使用flex布局,占据剩余空间 */
#main-container {
display: flex; /* 设置为flex布局 */
flex: 1; /* flex属性设置为1,表示该元素将占据剩余的全部空间 */
overflow: hidden; /* 当内容溢出容器时,隐藏溢出的内容 */
}
/* 图表容器,初始时占据全部剩余空间 */
#chart-container {
flex: 1; /* flex属性设置为1,表示该元素将占据其父元素中的剩余空间 */
height: 100%; /* 高度设置为100%,使其充满父容器的高度 */
overflow: hidden; /* 当内容溢出容器时,隐藏溢出的内容 */
position: relative; /* 设置定位方式为相对定位,便于内部元素的绝对定位 */
}
/* 数据源容器,初始时不占据空间,根据需要调整 */
#source-container {
flex: 0; /* flex属性设置为0,表示该元素初始时不占据空间 */
background-color: rgba(111, 231, 35, 0.36); /* 设置背景颜色,使用rgba表示半透明 */
padding: 30px; /* 设置内边距为30px */
overflow: auto; /* 当内容溢出容器时,显示滚动条 */
}
- 拖动事件监听:通过监听
mousedown
、mousemove
和mouseup
事件,实现了拖动分割线(#splitter
)来调整#chart-container
和#source-container
的大小。
以下是实现分割线的拖动的代码及解析:
/* 分割线,用于调整#chart-container和#source-container的大小 */
#splitter {
background-color: #ccc; /* 设置分割线的背景颜色为灰色 */
cursor: ew-resize; /* 设置鼠标悬停在分割线上时的光标样式为东西向调整大小 */
width: 5px; /* 设置分割线的宽度为5像素 */
position: absolute; /* 设置分割线的定位方式为绝对定位,便于控制其位置 */
right: 0; /* 设置分割线距离其父容器右边的距离为0,即贴紧父容器的右边 */
top: 0; /* 设置分割线距离其父容器顶部的距离为0,即贴紧父容器的顶部 */
bottom: 0; /* 设置分割线距离其父容器底部的距离为0,即分割线的高度会撑满父容器的高度 */
z-index: 1; /* 设置分割线的层叠顺序为1,确保它位于其他内容之上 */
}
- 动态计算宽度:在拖动过程中,动态计算
#chart-container
的新宽度,并相应地更新其flex
属性以及#source-container
的flex
属性,以实现两部分的宽度调整。
以下是实现动态调整的代码及解析:
// 分割线拖动调整大小的逻辑
let startX, startWidth, isResizing = false; // 初始化变量,startX记录鼠标按下时的X坐标,startWidth记录#chart-container的初始宽度,isResizing表示是否正在调整大小
document.getElementById('splitter').addEventListener('mousedown', function(e) {
// 为分割线添加mousedown事件监听器
isResizing = true; // 设置正在调整大小的状态为true
startX = e.clientX; // 记录鼠标按下时的X坐标
startWidth = document.getElementById('chart-container').offsetWidth; // 记录当前#chart-container的宽度
document.addEventListener('mousemove', onMouseMove); // 为document添加mousemove事件监听器,以便在鼠标移动时调整大小
document.addEventListener('mouseup', onMouseUp); // 为document添加mouseup事件监听器,以便在鼠标松开时停止调整大小
document.addEventListener('mouseleave', onMouseUp); // 为document添加mouseleave事件监听器,以便在鼠标离开时停止调整大小
});
/* 拖动时调整大小的逻辑 */
function onMouseMove(e) {
// 定义鼠标移动时的处理函数
if (!isResizing) return; // 如果不是正在调整大小,则直接返回
let newWidth = startWidth + (e.clientX - startX); // 计算新的宽度
newWidth = Math.max(100, Math.min(document.getElementById('main-container').offsetWidth - 5, newWidth)); // 限制新宽度的范围,不小于100,不大于主容器宽度减5
document.getElementById('chart-container').style.flex = `${
newWidth / document.getElementById('main-container').offsetWidth * 100}%`; // 更新#chart-container的flex属性
document.getElementById('source-container').style.flex = `calc(100% - ${
newWidth}px)`; // 更新#source-container的flex属性,使其占据剩余空间
}
/* 停止拖动时的清理逻辑 */
function onMouseUp() {
// 定义鼠标松开或离开时的处理函数
isResizing = false; // 设置正在调整大小的状态为false
document.removeEventListener('mousemove', onMouseMove); // 移除mousemove事件监听器
document.removeEventListener('mouseup', onMouseUp); // 移除mouseup事件监听器
document.removeEventListener('mouseleave', onMouseUp); // 移除mouseleave事件监听器
}
完整代码参考:
<!DOCTYPE html>
<html lang="en" style="height: 100%"> <!-- 设置html元素的高度为100%,以便其子元素可以继承并使用百分比高度 -->
<head>
<meta charset="utf-8"> <!-- 设置文档的字符编码为UTF-8 -->
<style>
/* 设置html和body元素高度为100%,并启用flex布局 */
body, html {
height: 100%;
margin: 0;
display: flex;
flex-direction: column;
}
/* 主容器,flex布局,占据剩余空间 */
#main-container {
display: flex;
flex: 1; /* 占据剩余空间 */
overflow: hidden;
}
/* 图表容器,初始时占据全部剩余空间 */
#chart-container {
flex: 1;
height: 100%;
overflow: hidden;
position: relative;
}
#source-container {
flex: 0; /* 初始时不占据空间,根据需要调整 */
background-color: rgba(111, 231, 35, 0.36);
padding: 30px;
overflow: auto;
}
/* 分割线,用于调整#chart-container和#source-container的大小 */
#splitter {
background-color: #ccc;
cursor: ew-resize;
width: 5px;
position: absolute;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
}
/* 下载按钮样式 */
#downloadButton {
padding: 10px 20px;
background-color: #007bff;
color: #1ed5cf;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
position: absolute;
top: 20px;
right: 20px;
z-index: 1000;
}
</style>
</head>
<body style="height: 100%; margin: 0"> <!-- 设置body的高度为100%,并移除默认的边距 -->
<div id="main-container">
<div id="chart-container">
<div id="container" style="height: 100%;"></div> <!-- ECharts图表容器 -->
<div id="splitter"></div> <!-- 分割线,用于拖动调整大小 -->
</div>
<div id="source-container">
<pre id="source-code"></pre> <!-- 显示HTML源码 -->
<button id="downloadButton" onclick="downloadSourceCode()">下载</button> <!-- 下载按钮 -->
</div>
</div>
<!-- 引入ECharts的JavaScript库 -->
<script type="text/javascript" src="https://registry.npmmirror.com/echarts/5.5.1/files/dist/echarts.min.js"></script>
<script type="text/javascript">
var dom = document.getElementById('container'); // 获取用于容纳ECharts图表的div元素
var myChart = echarts.init(dom, null, {
// 初始化ECharts实例
renderer: 'canvas', // 设置渲染器为canvas
useDirtyRect: false // 关闭脏矩形渲染优化
});
var option; // 声明一个变量用于存储图表的配置项
// 定义图表的配置项
option = {
xAxis: {
// X轴配置
type: 'category', // 设置X轴类型为类目轴
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] // 设置X轴数据
},
yAxis: {
// Y轴配置
type: 'value' // 设置Y轴类型为数值轴
},
series: [ // 系列列表配置
{
data: [120, 200, 150, 80, 70, 110, 130], // 设置系列中的数据
type: 'line', // 设置系列类型为折线图
symbol: 'triangle', // 设置系列中数据点的标记形状为三角形
symbolSize: 20, // 设置数据点标记的大小
lineStyle: {
// 设置线条的样式
color: '#5470C6', // 设置线条颜色
width: 4, // 设置线条宽度
type: 'dashed' // 设置线条类型为虚线
},
itemStyle: {
// 设置数据点标记的样式
borderWidth: 3, // 设置标记边框的宽度
borderColor: '#EE6666', // 设置标记边框的颜色
color: 'yellow' // 设置标记的填充颜色
}
}
]
};
// 如果存在配置项且配置项是一个对象,则使用配置项初始化图表
if (option && typeof option === 'object') {
myChart.setOption(option);
}
// 监听窗口大小变化以调整图表大小
window.addEventListener('resize', myChart.resize);
// 设置源代码显示
document.getElementById('source-code').textContent = document.documentElement.outerHTML;
// 分割线拖动调整大小的逻辑
let startX, startWidth, isResizing = false;
document.getElementById('splitter').addEventListener('mousedown', function(e) {
isResizing = true;
startX = e.clientX;
startWidth = document.getElementById('chart-container').offsetWidth;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
document.addEventListener('mouseleave', onMouseUp);
});
/* 拖动时调整大小的逻辑 */
function onMouseMove(e) {
if (!isResizing) return;
let newWidth = startWidth + (e.clientX - startX);
newWidth = Math.max(100, Math.min(document.getElementById('main-container').offsetWidth - 5, newWidth));
document.getElementById('chart-container').style.flex =
`${
newWidth / document.getElementById('main-container').offsetWidth * 100}%`;
document.getElementById('source-container').style.flex = `calc(100% - ${
newWidth}px)`;
}
/* 停止拖动时的清理逻辑 */
function onMouseUp() {
isResizing = false;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
document.removeEventListener('mouseleave', onMouseUp);
}
// 下载源代码功能
function downloadSourceCode() {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(document.documentElement.outerHTML));
element.setAttribute('download', '图表.html');
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
</script>
</body>
</html>
它的运行效果图是这样的:(右侧是可以动态拖动的)
4.分析代码编辑器及运行效果显示
根据上面所有功能的基础上,由于具体的实际需求,数据也是会发生变化的,因此,在原页面中添加了一个 “修改” 按钮,用于跳转到另一个页面进行配置项编译及运行效果展示这样的一个功能;同时也添加了对图表保存为图片的工具。
代码编辑器及运行效果显示功能主要依赖于以下几个技术功能点
- 使用
HTML
和CSS
来布局和样式化代码编辑器、图表容器和运行按钮
<div class="container">
<textarea id="codeEditor" style="height: 495px; resize: none;"></textarea>
<div id="chartContainer" style="height: 500px;"></div>
</div>
<button onclick="runCode()">运行</button>
JavaScript
代码的动态执行
function runCode() {
// 获取页面中ID为'codeEditor'的元素(代码编辑器),并读取其内的值(即用户编写的代码)
var code = document.getElementById('codeEditor').value;
try {
// 创建一个新的函数实例,该函数接受一个名为'option'的参数,并执行用户提供的代码
// 用户代码执行完毕后,返回一个'option'对象,该对象将被用于配置ECharts图表
var userFunction = new Function('option', code + '; return option;');
// 调用上一步创建的函数,不传递任何实际参数(因为ECharts的option将在函数内部定义并返回)
// 函数执行的结果(即用户定义的图表配置)被存储在userOption变量中
var userOption = userFunction();
// 检查userOption是否存在且为对象类型
// 如果是,则使用ECharts实例的setOption方法更新图表配置
if (userOption && typeof userOption === 'object') {
myChart.setOption(userOption);
}
} catch (error) {
// 如果在尝试执行用户代码的过程中发生错误,则捕获该错误
// 在控制台输出错误信息,并向用户显示一个警告框,提示检查自己的代码
console.error('代码执行出错:', error);
alert('代码执行出错,请检查您的代码。');
}
}
完整代码参考:
(1)主页面.html
:
<!DOCTYPE html> <!-- 声明这是一个HTML5文档 -->
<html lang="en" style="height: 100%"> <!-- HTML文档的根元素,lang="en"表示文档的主要语言是英语;style="height: 100%" 设置整个页面的高度为浏览器窗口的100% -->
<head>
<meta charset="utf-8"> <!-- 设置文档的字符编码为UTF-8 -->
<style>
/* 设置html和body元素高度为100%,并启用flex布局 */
body, html {
height: 100%; /* 设置高度为100% */
margin: 0; /* 移除默认的边距 */
display: flex; /* 启用Flex布局 */
flex-direction: column; /* 设置主轴方向为垂直方向 */
overflow: hidden; /* 隐藏溢出内容 */
}
/* 主容器,flex布局,占据剩余空间 */
#main-container {
display: flex; /* 启用Flex布局 */
flex: 1; /* 占据剩余空间 */
overflow: hidden; /* 隐藏溢出内容 */
}
/* 图表容器,初始时占据全部剩余空间 */
#chart-container {
flex: 1; /* 占据剩余空间 */
height: 100%; /* 设置高度为100% */
overflow: hidden; /* 隐藏溢出内容 */
position: relative; /* 设置定位方式为相对定位 */
}
#source-container {
flex: 0; /* 初始时不占据空间,根据需要调整 */
background-color: rgba(111, 231, 35, 0.36); /* 设置背景颜色 */
padding: 30px; /* 设置内边距 */
overflow: auto; /* 设置溢出内容自动显示滚动条 */
}
/* 分割线,用于调整#chart-container和#source-container的大小 */
#splitter {
background-color: #ccc; /* 设置背景颜色 */
cursor: ew-resize; /* 设置鼠标悬停时的形状为水平调整大小 */
width: 5px; /* 设置宽度 */
position: absolute; /* 设置定位方式为绝对定位 */
right: 0; /* 设置右边界为0 */
top: 0; /* 设置上边界为0 */
bottom: 0; /* 设置下边界为0 */
z-index: 1; /* 设置层叠顺序 */
}
/* 下载按钮样式 */
#downloadButton {
padding: 10px 20px; /* 设置内边距 */
background-color: #007bff; /* 设置背景颜色 */
color: #1ed5cf; /* 设置文本颜色 */
border: none; /* 移除边框 */
border-radius: 5px; /* 设置边框圆角 */
cursor: pointer; /* 设置鼠标悬停时的形状为指针 */
font-size: 16px; /* 设置字体大小 */
position: absolute; /* 设置定位方式为绝对定位 */
top: 20px; /* 设置上边界 */
right: 20px; /* 设置右边界 */
z-index: 1000; /* 设置层叠顺序 */
}
#modifyButton {
padding: 10px 20px; /* 设置内边距 */
background-color: #007bff; /* 设置背景颜色 */
color: #1ed5cf; /* 设置文本颜色 */
border: none; /* 移除边框 */
border-radius: 5px; /* 设置边框圆角 */
cursor: pointer; /* 设置鼠标悬停时的形状为指针 */
font-size: 16px; /* 设置字体大小 */
position: absolute; /* 设置定位方式为绝对定位 */
top: 10px; /* 设置上边界 */
left: 10px; /* 设置左边界 */
z-index: 1000; /* 设置层叠顺序 */
}
</style>
</head>
<body style="height: 100%; margin: 0; overflow: hidden;">
<!-- 添加一个按钮,并设置其样式以便将其定位到页面的左上角 -->
<button id="modifyButton" style="position: absolute; top: 10px; left: 10px;">修改</button>
<a id="hiddenLink" href="配置项编辑.html" style="display: none;"></a> <!-- 一个隐藏的链接,用于页面跳转 -->
<div id="main-container">
<div id="chart-container">
<div id="container" style="height: calc(100% - 10px); margin-top: 25px;"></div> <!-- 添加了上边距和计算高度 -->
<div id="splitter"></div> <!-- 分割线,用于拖动调整大小 -->
</div>
<div id="source-container">
<pre id="source-code"></pre> <!-- 显示HTML源码 -->
<button id="downloadButton" onclick="downloadSourceCode()">下载</button> <!-- 下载按钮 -->
</div>
</div>
<!-- 引入ECharts库 -->
<script type="text/javascript" src="echarts.min.js"></script>
<!-- 开始一个JavaScript脚本块 -->
<script type="text/javascript">
// 获取id为container的DOM元素,这个元素将作为ECharts图表的容器
var dom = document.getElementById('container');
// 使用echarts.init方法初始化一个ECharts实例,指定容器、主题和配置项。这里使用canvas渲染器,并关闭useDirtyRect优化
var myChart = echarts.init(dom, null, {
renderer: 'canvas',
useDirtyRect: false
});
// 声明一个变量option,用于存储图表的配置项
var option;
option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] // X轴数据
},
yAxis: {
type: 'value' // Y轴类型
},
series: [ // 系列列表
{
data: [120, 200, 150, 80, 70, 110, 130], // 数据
type: 'line', // 图表类型
symbol: 'triangle', // 标记的图形
symbolSize: 20, // 标记的大小
lineStyle: {
// 线条样式
color: '#5470C6', // 线条颜色
width: 4, // 线条宽度
type: 'dashed' // 线条类型
},
itemStyle: {
// 图形样式
borderWidth: 3, // 描边宽度
borderColor: '#EE6666', // 描边颜色
color: 'yellow' // 填充颜色
}
}
],
toolbox: {
// 工具栏
show: true, // 显示工具栏
feature: {
saveAsImage: {
} // 保存为图片
},
right: '100', // 控制工具箱距离容器右侧的距离
top: '20' // 控制工具箱距离容器顶部的距离
},
};
// 检查option是否存在且为对象类型,如果是,则使用setOption方法将配置项应用到ECharts实例上
if (option && typeof option === 'object') {
myChart.setOption(option);
}
// 给窗口添加一个resize事件监听器,当窗口大小改变时,调用myChart.resize方法使图表自适应新的容器大小
window.addEventListener('resize', myChart.resize);
</script>
<!-- 按钮的点击事件监听器 -->
<script type="text/javascript">
document.getElementById('modifyButton').addEventListener('click', function() {
// 触发隐藏 <a> 标签的点击事件,实现页面跳转
document.getElementById('hiddenLink').click();
});
// 设置源代码显示
document.getElementById('source-code').textContent = document.documentElement.outerHTML;
// 分割线拖动调整大小的逻辑
let startX, startWidth, isResizing = false;
document.getElementById('splitter').addEventListener('mousedown', function(e) {
isResizing = true;
startX = e.clientX;
startWidth = document.getElementById('chart-container').offsetWidth;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
document.addEventListener('mouseleave', onMouseUp);
});
/* 拖动时调整大小的逻辑 */
function onMouseMove(e) {
if (!isResizing) return;
let newWidth = startWidth + (e.clientX - startX);
newWidth = Math.max(100, Math.min(document.getElementById('main-container').offsetWidth - 5, newWidth));
document.getElementById('chart-container').style.flex =
`${
newWidth / document.getElementById('main-container').offsetWidth * 100}%`;
document.getElementById('source-container').style.flex = `calc(100% - ${
newWidth}px)`;
}
/* 停止拖动时的清理逻辑 */
function onMouseUp() {
isResizing = false;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
document.removeEventListener('mouseleave', onMouseUp);
}
// 下载源代码功能
function downloadSourceCode() {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(document.documentElement.outerHTML));
element.setAttribute('download', '图表.html');
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
</script>
</body>
</html>
(2)配置项编辑.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>动态代码运行</title>
<!-- 引入ECharts文件 -->
<script src="echarts.min.js"></script>
<style>
/* 设置容器样式 */
.container {
margin-top: 25px;
display: flex;
justify-content: space-between;
}
/* 设置代码编辑器和图表容器的样式 */
#codeEditor, #chartContainer {
flex: 1; /* 使两个元素占据相等的空间 */
margin: 5px; /* 添加一些外边距来分隔元素 */
background-image: linear-gradient(to right, rgba(154, 89, 168, 0.67), rgba(30, 145, 199, 0.67), rgba(0, 255, 153, 0.67));
}
/* 设置按钮样式 */
button
{
/* 设置按钮为绝对定位,使其能够相对于其最近的已定位(非static)祖先元素进行定位 */
position: absolute;
/* 设置按钮的顶部位置为父元素高度的50% */
top: 50%;
/* 设置按钮的左侧位置为父元素宽度的50% */
left: 50%;
/* 使用transform属性将按钮向左和向上移动其自身宽度和高度的50%,以实现居中效果 */
transform: translate(-50%, -50%);
/* 设置按钮的内边距 */
padding: 2px 10px;
/* 设置按钮的背景颜色 */
background-color: #007bff;
/* 设置按钮的文字颜色 */
color: #1ed5cf;
/* 去除按钮的边框 */
border: none;
/* 设置按钮的边框圆角 */
border-radius: 7px;
/* 设置鼠标悬停在按钮上时的光标样式为指针 */
cursor: pointer;
/* 设置按钮的文字大小 */
font-size: 15px;
/* 设置按钮的堆叠顺序,确保它位于其他元素之上 */
z-index: 1000;
}
</style>
</head>
<body>
<div class="container">
<!-- 代码编辑器 -->
<textarea id="codeEditor" style="height: 495px; resize: none;">
// 定义图表配置项
var option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] // X轴数据
},
yAxis: {
type: 'value' // Y轴为数值轴
},
series: [ // 数据系列
{
data: [120, 200, 150, 80, 70, 110, 130], // 数据
type: 'line', // 图表类型为折线图
symbol: 'triangle', // 数据点形状为三角形
symbolSize: 20, // 数据点大小
lineStyle: {
color: '#5470C6', // 线条颜色
width: 4, // 线条宽度
type: 'dashed' // 线条类型为虚线
},
itemStyle: {
borderWidth: 3, // 数据点边框宽度
borderColor: '#EE6666', // 数据点边框颜色
color: 'yellow' // 数据点填充颜色
}
}
]
};
</textarea>
<!-- 图表容器 -->
<div id="chartContainer" style="height: 500px;"></div>
</div>
<!-- 运行按钮 -->
<button onclick="runCode()">运行</button>
<script>
// 初始化ECharts实例
var myChart = echarts.init(document.getElementById('chartContainer'));
// 运行代码的函数
function runCode() {
// 获取代码编辑器中的代码
var code = document.getElementById('codeEditor').value;
try {
// 将获取的代码转换为函数,并执行,返回option对象
var userFunction = new Function('option', code + '; return option;');
var userOption = userFunction();
// 如果返回的userOption是对象,则使用setOption方法更新图表
if (userOption && typeof userOption === 'object') {
myChart.setOption(userOption);
}
} catch (error) {
// 如果代码执行出错,则在控制台输出错误信息,并弹出提示
console.error('代码执行出错:', error);
alert('代码执行出错,请检查您的代码。');
}
}
</script>
</body>
</html>
它的运行效果图是这样的: