Project

General

Profile

Web tech » History » Version 3

jun chen, 02/11/2025 04:12 PM

1 1 jun chen
# Web tech
2
3 3 jun chen
{{toc}}
4
5
## 直接用javascript
6 1 jun chen
7
``` 
8
<!DOCTYPE html>
9
<html lang="en">
10
<head>
11
    <meta charset="UTF-8">
12
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
13
    <title>分段填充折线图</title>
14
    <script src="https://d3js.org/d3.v7.min.js"></script>
15
    <style>
16
        .tooltip {
17
            position: absolute;
18
            background-color: rgba(255, 255, 255, 0.9);
19
            border: 1px solid #ccc;
20
            padding: 5px;
21
            font-size: 12px;
22
            pointer-events: none;
23
            opacity: 0;
24
        }
25
        .line {
26
            fill: none;
27
            stroke: black;
28
            stroke-width: 2;
29
        }
30
    </style>
31
</head>
32
<body>
33
    <div id="chart"></div>
34
    <div class="tooltip" id="tooltip"></div>
35
36
    <script>
37
        // 示例数据
38
        const data = [
39
            { time: "2025-01-01", value: 10, description: "说明1" },
40
            { time: "2025-01-02", value: 15, description: "说明2" },
41
            { time: "2025-01-03", value: 20, description: "说明3" },
42
            { time: "2025-01-04", value: 25, description: "说明4" },
43
            { time: "2025-01-05", value: 30, description: "说明5" },
44
            { time: "2025-01-06", value: 35, description: "说明6" },
45
            { time: "2025-01-07", value: 40, description: "说明7" },
46
            { time: "2025-01-08", value: 45, description: "说明8" },
47
            { time: "2025-01-09", value: 50, description: "说明9" },
48
            { time: "2025-01-10", value: 55, description: "说明10" },
49
            { time: "2025-01-11", value: 60, description: "说明11" },
50
            { time: "2025-01-12", value: 65, description: "说明12" },
51
            { time: "2025-01-13", value: 70, description: "说明13" },
52
            { time: "2025-01-14", value: 75, description: "说明14" },
53
            { time: "2025-01-15", value: 80, description: "说明15" },
54
            { time: "2025-01-16", value: 85, description: "说明16" },
55
            { time: "2025-01-17", value: 90, description: "说明17" },
56
            { time: "2025-01-18", value: 95, description: "说明18" },
57
            { time: "2025-01-19", value: 100, description: "说明19" },
58
            { time: "2025-01-20", value: 105, description: "说明20" }
59
        ];
60
61
        // 设置图表尺寸
62
        const margin = { top: 20, right: 30, bottom: 30, left: 40 };
63
        const width = 800 - margin.left - margin.right;
64
        const height = 400 - margin.top - margin.bottom;
65
66
        // 创建 SVG 容器
67
        const svg = d3.select("#chart")
68
            .append("svg")
69
            .attr("width", width + margin.left + margin.right)
70
            .attr("height", height + margin.top + margin.bottom)
71
            .append("g")
72
            .attr("transform", `translate(${margin.left},${margin.top})`);
73
74
        // 解析时间格式
75
        const parseTime = d3.timeParse("%Y-%m-%d");
76
77
        // 格式化数据
78
        data.forEach(d => {
79
            d.time = parseTime(d.time);
80
            d.value = +d.value;
81
        });
82
83
        // 设置比例尺
84
        const x = d3.scaleTime()
85
            .domain(d3.extent(data, d => d.time))
86
            .range([0, width]);
87
88
        const y = d3.scaleLinear()
89
            .domain([0, d3.max(data, d => d.value)])
90
            .range([height, 0]);
91
92
        // 添加 X 轴
93
        svg.append("g")
94
            .attr("transform", `translate(0,${height})`)
95
            .call(d3.axisBottom(x));
96
97
        // 添加 Y 轴
98
        svg.append("g")
99
            .call(d3.axisLeft(y));
100
101
        // 创建折线生成器
102
        const line = d3.line()
103
            .x(d => x(d.time))
104
            .y(d => y(d.value));
105
106
        // 绘制折线
107
        svg.append("path")
108
            .datum(data)
109
            .attr("class", "line")
110
            .attr("d", line);
111
112
        // 分段填充颜色
113
        const first10 = data.slice(0, 10);
114
        const last10 = data.slice(-10);
115
        const middle = data.slice(10, -10);
116
117
        // 填充前十个时间段的绿色区域
118
        svg.append("path")
119
            .datum(first10)
120
            .attr("fill", "green")
121
            .attr("opacity", 0.3)
122
            .attr("d", d3.area()
123
                .x(d => x(d.time))
124
                .y0(height)
125
                .y1(d => y(d.value))
126
            );
127
128
        // 填充中间时间段的蓝色区域
129
        svg.append("path")
130
            .datum(middle)
131
            .attr("fill", "blue")
132
            .attr("opacity", 0.3)
133
            .attr("d", d3.area()
134
                .x(d => x(d.time))
135
                .y0(height)
136
                .y1(d => y(d.value))
137
            );
138
139
        // 填充后十个时间段的红色区域
140
        svg.append("path")
141
            .datum(last10)
142
            .attr("fill", "red")
143
            .attr("opacity", 0.3)
144
            .attr("d", d3.area()
145
                .x(d => x(d.time))
146
                .y0(height)
147
                .y1(d => y(d.value))
148
            );
149
150
        // 添加悬停交互
151
        const tooltip = d3.select("#tooltip");
152
153
        svg.selectAll(".dot")
154
            .data(data)
155
            .enter()
156
            .append("circle")
157
            .attr("class", "dot")
158
            .attr("cx", d => x(d.time))
159
            .attr("cy", d => y(d.value))
160
            .attr("r", 5)
161
            .attr("fill", "steelblue")
162
            .on("mouseover", (event, d) => {
163
                tooltip.style("opacity", 1)
164
                    .html(`时间: ${d3.timeFormat("%Y-%m-%d")(d.time)}<br>数值: ${d.value}<br>说明: ${d.description}`)
165
                    .style("left", `${event.pageX + 5}px`)
166
                    .style("top", `${event.pageY - 20}px`);
167
            })
168
            .on("mouseout", () => {
169
                tooltip.style("opacity", 0);
170
            });
171
    </script>
172
</body>
173
</html>
174
```
175
176 2 jun chen
## 以下是使用 **JavaScript** 和 **Python** 分别实现交互式网页图表的两种方法,包含完整代码和步骤说明:
177 1 jun chen
178
---
179
180
### **方法一:JavaScript + Plotly(纯前端实现)**
181
#### 特点:直接在浏览器中运行,无需后端,适合快速展示。
182 2 jun chen
183 1 jun chen
```html
184
<!DOCTYPE html>
185
<html>
186
<head>
187
    <title>交互式图表</title>
188
    <!-- 引入 Plotly.js -->
189
    <script src="https://cdn.plot.ly/plotly-2.24.1.min.js"></script>
190
</head>
191
<body>
192
    <div id="chart"></div>
193
194
    <script>
195
        // 读取CSV文件(假设文件名为 data.csv)
196
        fetch('data.csv')
197
            .then(response => response.text())
198
            .then(csvText => {
199
                // 解析CSV数据
200
                const rows = csvText.split('\n');
201
                const x = [], y = [];
202
                rows.forEach((row, index) => {
203
                    if (index === 0) return; // 跳过标题行
204
                    const [xVal, yVal] = row.split(',');
205
                    x.push(parseFloat(xVal));
206
                    y.push(parseFloat(yVal));
207
                });
208
209
                // 绘制图表
210
                Plotly.newPlot('chart', [{
211
                    x: x,
212
                    y: y,
213
                    type: 'scatter',
214
                    mode: 'lines+markers',
215
                    marker: { color: 'blue' },
216
                    line: { shape: 'spline' }
217
                }], {
218
                    title: '交互式数据图表',
219
                    xaxis: { title: 'X轴' },
220
                    yaxis: { title: 'Y轴' },
221
                    hovermode: 'closest'
222
                });
223
            });
224
    </script>
225
</body>
226
</html>
227
```
228
229
#### 使用步骤:
230
1. 将CSV文件命名为 `data.csv`,格式如下:
231
   ```csv
232
   x,y
233
   1,5
234
   2,3
235
   3,7
236
   4,2
237
   5,8
238
   ```
239
2. 将HTML文件和 `data.csv` 放在同一目录下,用浏览器打开HTML文件。
240
3. 效果:支持**缩放、悬停显示数值、拖拽平移**等交互。
241
242
---
243
244
### **方法二:Python + Plotly(生成独立HTML文件)**
245
#### 特点:适合Python用户,自动化生成图表文件。
246 2 jun chen
247 1 jun chen
```python
248
import pandas as pd
249
import plotly.express as px
250
251
# 1. 读取CSV文件
252
df = pd.read_csv("data.csv")
253
254
# 2. 创建交互式图表
255
fig = px.line(
256
    df, x='x', y='y',
257
    title='Python生成的交互式图表',
258
    markers=True,  # 显示数据点
259
    line_shape='spline'  # 平滑曲线
260
)
261
262
# 3. 自定义悬停效果和样式
263
fig.update_traces(
264
    hoverinfo='x+y',  # 悬停显示x和y值
265
    line=dict(width=2, color='royalblue'),
266
    marker=dict(size=8, color='firebrick')
267
)
268
269
# 4. 保存为HTML文件
270
fig.write_html("interactive_chart.html")
271
```
272
273
#### 使用步骤:
274
1. 安装依赖:
275
   ```bash
276
   pip install pandas plotly
277
   ```
278
2. 运行代码后,生成 `interactive_chart.html`,用浏览器打开即可看到图表。
279
280
---
281
282
### **交互功能对比**
283
| **功能**               | **JavaScript/Plotly** | **Python/Plotly** |
284
|------------------------|-----------------------|-------------------|
285
| 缩放/平移              | ✔️                    | ✔️                |
286
| 悬停显示数值           | ✔️                    | ✔️                |
287
| 数据点高亮             | ✔️                    | ✔️                |
288
| 导出为图片(PNG/JPEG) | ✔️                    | ✔️                |
289
| 动态更新数据           | ✔️(需额外代码)      | ❌                |
290
291
---
292
293
### **进阶方案(可选)**
294
1. **动态数据加载**(JavaScript):
295
   ```html
296
   <input type="file" id="csvFile" accept=".csv">
297
   <div id="chart"></div>
298
   <script>
299
     document.getElementById('csvFile').addEventListener('change', function(e) {
300
       const file = e.target.files[0];
301
       const reader = new FileReader();
302
       reader.onload = function(e) {
303
         // 解析并绘制图表(代码同方法一)
304
       };
305
       reader.readAsText(file);
306
     });
307
   </script>
308
   ```
309
   - 用户可上传任意CSV文件,实时生成图表。
310
311
2. **添加控件**(Python + Dash):
312
   ```python
313
   from dash import Dash, dcc, html
314
   import pandas as pd
315
   import plotly.express as px
316
317
   app = Dash(__name__)
318
   df = pd.read_csv("data.csv")
319
320
   app.layout = html.Div([
321
       dcc.Graph(
322
           id='live-chart',
323
           figure=px.scatter(df, x='x', y='y', title='Dash动态图表')
324
       ),
325
       html.Button('更新数据', id='update-button')
326
   ])
327
328
   if __name__ == '__main__':
329
       app.run_server(debug=True)
330
   ```
331
   - 运行后访问 `http://localhost:8050`,支持动态交互和按钮触发操作。
332
333
---
334
335
### **最终效果示例**
336
![交互式图表示例](https://plotly.com/javascript/static/images/line-chart.png)
337
338
选择适合你的场景快速实现吧!