Project

General

Profile

Actions

Web tech » History » Revision 5

« Previous | Revision 5/29 (diff) | Next »
jun chen, 02/12/2025 10:44 AM


Web tech

How to visualize data:

D3 ref: https://observablehq.com/@d3/gallery , https://johan.github.io/d3/ex/
Plotly ref: https://plotly.com/javascript/

js d3 内嵌数据显示折线

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>分段填充折线图</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        .tooltip {
            position: absolute;
            background-color: rgba(255, 255, 255, 0.9);
            border: 1px solid #ccc;
            padding: 5px;
            font-size: 12px;
            pointer-events: none;
            opacity: 0;
        }
        .line {
            fill: none;
            stroke: black;
            stroke-width: 2;
        }
    </style>
</head>
<body>
    <div id="chart"></div>
    <div class="tooltip" id="tooltip"></div>

    <script>
        // 示例数据
        const data = [
            { time: "2025-01-01", value: 10, description: "说明1" },
            { time: "2025-01-02", value: 15, description: "说明2" },
            { time: "2025-01-03", value: 20, description: "说明3" },
            { time: "2025-01-04", value: 25, description: "说明4" },
            { time: "2025-01-05", value: 30, description: "说明5" },
            { time: "2025-01-06", value: 35, description: "说明6" },
            { time: "2025-01-07", value: 40, description: "说明7" },
            { time: "2025-01-08", value: 45, description: "说明8" },
            { time: "2025-01-09", value: 50, description: "说明9" },
            { time: "2025-01-10", value: 55, description: "说明10" },
            { time: "2025-01-11", value: 60, description: "说明11" },
            { time: "2025-01-12", value: 65, description: "说明12" },
            { time: "2025-01-13", value: 70, description: "说明13" },
            { time: "2025-01-14", value: 75, description: "说明14" },
            { time: "2025-01-15", value: 80, description: "说明15" },
            { time: "2025-01-16", value: 85, description: "说明16" },
            { time: "2025-01-17", value: 90, description: "说明17" },
            { time: "2025-01-18", value: 95, description: "说明18" },
            { time: "2025-01-19", value: 100, description: "说明19" },
            { time: "2025-01-20", value: 105, description: "说明20" }
        ];

        // 设置图表尺寸
        const margin = { top: 20, right: 30, bottom: 30, left: 40 };
        const width = 800 - margin.left - margin.right;
        const height = 400 - margin.top - margin.bottom;

        // 创建 SVG 容器
        const svg = d3.select("#chart")
            .append("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", `translate(${margin.left},${margin.top})`);

        // 解析时间格式
        const parseTime = d3.timeParse("%Y-%m-%d");

        // 格式化数据
        data.forEach(d => {
            d.time = parseTime(d.time);
            d.value = +d.value;
        });

        // 设置比例尺
        const x = d3.scaleTime()
            .domain(d3.extent(data, d => d.time))
            .range([0, width]);

        const y = d3.scaleLinear()
            .domain([0, d3.max(data, d => d.value)])
            .range([height, 0]);

        // 添加 X 轴
        svg.append("g")
            .attr("transform", `translate(0,${height})`)
            .call(d3.axisBottom(x));

        // 添加 Y 轴
        svg.append("g")
            .call(d3.axisLeft(y));

        // 创建折线生成器
        const line = d3.line()
            .x(d => x(d.time))
            .y(d => y(d.value));

        // 绘制折线
        svg.append("path")
            .datum(data)
            .attr("class", "line")
            .attr("d", line);

        // 分段填充颜色
        const first10 = data.slice(0, 10);
        const last10 = data.slice(-10);
        const middle = data.slice(10, -10);

        // 填充前十个时间段的绿色区域
        svg.append("path")
            .datum(first10)
            .attr("fill", "green")
            .attr("opacity", 0.3)
            .attr("d", d3.area()
                .x(d => x(d.time))
                .y0(height)
                .y1(d => y(d.value))
            );

        // 填充中间时间段的蓝色区域
        svg.append("path")
            .datum(middle)
            .attr("fill", "blue")
            .attr("opacity", 0.3)
            .attr("d", d3.area()
                .x(d => x(d.time))
                .y0(height)
                .y1(d => y(d.value))
            );

        // 填充后十个时间段的红色区域
        svg.append("path")
            .datum(last10)
            .attr("fill", "red")
            .attr("opacity", 0.3)
            .attr("d", d3.area()
                .x(d => x(d.time))
                .y0(height)
                .y1(d => y(d.value))
            );

        // 添加悬停交互
        const tooltip = d3.select("#tooltip");

        svg.selectAll(".dot")
            .data(data)
            .enter()
            .append("circle")
            .attr("class", "dot")
            .attr("cx", d => x(d.time))
            .attr("cy", d => y(d.value))
            .attr("r", 5)
            .attr("fill", "steelblue")
            .on("mouseover", (event, d) => {
                tooltip.style("opacity", 1)
                    .html(`时间: ${d3.timeFormat("%Y-%m-%d")(d.time)}<br>数值: ${d.value}<br>说明: ${d.description}`)
                    .style("left", `${event.pageX + 5}px`)
                    .style("top", `${event.pageY - 20}px`);
            })
            .on("mouseout", () => {
                tooltip.style("opacity", 0);
            });
    </script>
</body>
</html>

以下是使用 JavaScriptPython 分别实现交互式网页图表的两种方法,包含完整代码和步骤说明:


方法一:JavaScript + Plotly(纯前端实现)

特点:直接在浏览器中运行,无需后端,适合快速展示。

<!DOCTYPE html>
<html>
<head>
    <title>交互式图表</title>
    <!-- 引入 Plotly.js -->
    <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
</head>
<body>
    <div id="chart"></div>

    <script>
        // 读取CSV文件(假设文件名为 data.csv)
        fetch('data.csv')
            .then(response => response.text())
            .then(csvText => {
                // 解析CSV数据
                const rows = csvText.split('\n');
                const x = [], y = [];
                rows.forEach((row, index) => {
                    if (index === 0) return; // 跳过标题行
                    const [xVal, yVal] = row.split(',');
                    x.push(parseFloat(xVal));
                    y.push(parseFloat(yVal));
                });

                // 绘制图表
                Plotly.newPlot('chart', [{
                    x: x,
                    y: y,
                    type: 'scatter',
                    mode: 'lines+markers',
                    marker: { color: 'blue' },
                    line: { shape: 'spline' }
                }], {
                    title: '交互式数据图表',
                    xaxis: { title: 'X轴' },
                    yaxis: { title: 'Y轴' },
                    hovermode: 'closest'
                });
            });
    </script>
</body>
</html>

使用步骤:

  1. 将CSV文件命名为 data.csv,格式如下:
    x,y
    1,5
    2,3
    3,7
    4,2
    5,8
    
  2. 将HTML文件和 data.csv 放在同一目录下,用浏览器打开HTML文件。
  3. 效果:支持缩放、悬停显示数值、拖拽平移等交互。

方法二:Python + Plotly(生成独立HTML文件)

特点:适合Python用户,自动化生成图表文件。

import pandas as pd
import plotly.express as px

# 1. 读取CSV文件
df = pd.read_csv("data.csv")

# 2. 创建交互式图表
fig = px.line(
    df, x='x', y='y',
    title='Python生成的交互式图表',
    markers=True,  # 显示数据点
    line_shape='spline'  # 平滑曲线
)

# 3. 自定义悬停效果和样式
fig.update_traces(
    hoverinfo='x+y',  # 悬停显示x和y值
    line=dict(width=2, color='royalblue'),
    marker=dict(size=8, color='firebrick')
)

# 4. 保存为HTML文件
fig.write_html("interactive_chart.html")

使用步骤:

  1. 安装依赖:
    pip install pandas plotly
    
  2. 运行代码后,生成 interactive_chart.html,用浏览器打开即可看到图表。

交互功能对比

功能 JavaScript/Plotly Python/Plotly
缩放/平移 ✔️ ✔️
悬停显示数值 ✔️ ✔️
数据点高亮 ✔️ ✔️
导出为图片(PNG/JPEG) ✔️ ✔️
动态更新数据 ✔️(需额外代码)

进阶方案(可选)

  1. 动态数据加载(JavaScript):

    <input type="file" id="csvFile" accept=".csv">
    <div id="chart"></div>
    <script>
      document.getElementById('csvFile').addEventListener('change', function(e) {
        const file = e.target.files[0];
        const reader = new FileReader();
        reader.onload = function(e) {
          // 解析并绘制图表(代码同方法一)
        };
        reader.readAsText(file);
      });
    </script>
    
    • 用户可上传任意CSV文件,实时生成图表。
  2. 添加控件(Python + Dash):

    from dash import Dash, dcc, html
    import pandas as pd
    import plotly.express as px
    
    app = Dash(__name__)
    df = pd.read_csv("data.csv")
    
    app.layout = html.Div([
        dcc.Graph(
            id='live-chart',
            figure=px.scatter(df, x='x', y='y', title='Dash动态图表')
        ),
        html.Button('更新数据', id='update-button')
    ])
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    
    • 运行后访问 http://localhost:8050,支持动态交互和按钮触发操作。

最终效果示例

交互式图表示例

选择适合你的场景快速实现吧!

Updated by jun chen 4 months ago · 29 revisions