工作中遇到了在表格中插入股票图表的需求,最后使用d3解决,参考代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
table {
width: 100%;
border-collapse: collapse;
}
th,
td {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
th {
background-color: #f2f2f2;
}
</style>
</head>
<body>
<div></div>
<table>
<thead>
<tr>
<th>股票代码</th>
<th>股票名称</th>
<th>趋势</th>
</tr>
</thead>
<tbody>
<tr>
<td>000001.hk</td>
<td>股票A</td>
<td><div class="chart-box"></div></td>
</tr>
<tr>
<td>000002.hk</td>
<td>股票B</td>
<td><div class="chart-box"></div></td>
</tr>
<tr>
<td>000003.hk</td>
<td>股票C</td>
<td><div class="chart-box"></div></td>
</tr>
<tr>
<td>000004.hk</td>
<td>股票D</td>
<td><div class="chart-box"></div></td>
</tr>
<tr>
<td>000005.hk</td>
<td>股票E</td>
<td><div class="chart-box"></div></td>
</tr>
<tr>
<td>000006.hk</td>
<td>股票F</td>
<td><div class="chart-box"></div></td>
</tr>
</tbody>
</table>
</body>
</html>
<script type="module">
import * as D3 from "https://cdn.jsdelivr.net/npm/d3@7/+esm";
const width = 500;
const height = 500;
const table = document.querySelector("table");
const rowCount = table.rows.length - 1; // 获取表格行数
const chartDom = document.querySelectorAll(".chart-box");
chartDom.forEach((itemDon, i) => {
itemDon.classList.add(`chart-box-${i}`);
});
const data = [
{ date: "2022-01-01", value: 10 },
{ date: "2022-01-02", value: 20 },
{ date: "2022-01-03", value: 15 },
{ date: "2022-01-04", value: 25 },
{ date: "2022-01-05", value: 18 },
{ date: "2022-01-06", value: 30 },
];
const margin = { top: 20, right: 30, bottom: 30, left: 40 };
function creatLineChart(data, id) {
// 创建 SVG 元素并设置属性
const svg = D3.select(`.chart-box-${id}`)
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("fill", "#fff")
.attr("viewBox", [3, 0, width, height])
.join(" ");
// 创建 x 比例尺
const x = D3.scaleLinear()
.domain([1, data.length]) // 设置域
.range([margin.left, width - margin.right + 6]) // 设置范围
.clamp(true);
// 为数据添加序号
let i = 0;
data.forEach((item) => (item.date = String(++i)));
// 创建 y 比例尺
let minVal = D3.min(data, (d) => d.value);
let maxVal = D3.max(data, (d) => d.value);
let padding = (maxVal - minVal) * 0.1;
const y = D3.scaleLinear()
.domain([minVal - padding, maxVal + padding])
.range([height - margin.bottom + 6, margin.top])
.clamp(true);
// 绘制 x 轴
svg.append("g").call((g) => {
drawXAxis(g, x);
});
// 绘制 y 轴
svg.append("g").call((g) => {
drawYAxis(g, y);
});
// 绘制折线图
const seriesUpdate = svg.selectAll(".lineBox").data([data], (d) => d.data);
seriesUpdate
.enter()
.append("path")
.attr("class", "lineBox")
.merge(seriesUpdate)
.transition()
.duration(1000)
.attr(
"d",
D3.line()
.x((d) => x(Number.parseInt(d.date.split("-").join(""))))
.y((d) => y(d.value))
)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-opacity", 1)
.attr("stroke-width", 1);
}
// 绘制 x 轴
function drawXAxis(g, x) {
g.attr("transform", `translate(0,${height - margin.top + 3})`)
.call(D3.axisBottom(x.copy().interpolate(D3.interpolateRound)))
.call((g) => g.select(".domain").remove())
.call((g) =>
g
.selectAll(".tick line")
.clone()
.attr("stroke", "#f4c46d")
.attr("stroke-opacity", 1)
.attr("stroke-width", 1)
.attr("y1", -height + margin.top + margin.bottom - 8)
)
.call((g) => g.selectAll(".tick text").remove())
.call((g) => g.selectAll(".tick").select("line").remove());
}
// 绘制 y 轴
function drawYAxis(g, y) {
g.attr("transform", `translate(${margin.left + 6},0)`)
.call(D3.axisLeft(y.copy().interpolate(D3.interpolateRound)))
.call((g) => g.select(".domain").remove())
.call((g) =>
g
.selectAll(".tick line")
.clone()
.attr("stroke", "#f4c46d")
.attr("stroke-opacity", 1)
.attr("stroke-width", 1)
.attr("x1", width - margin.left - margin.right)
)
.call((g) => g.selectAll(".tick text").remove())
.call((g) => g.selectAll(".tick").select("line").remove());
}
for (let i = 0; i < rowCount; i++) {
// 调用绘制折线图函数
creatLineChart(data, i);
}
</script>