Public
Edited
Dec 23, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = ((data, alarm, sentiment, alter) => {
const chart = new G2.Chart({
width: 960,
height: 720,
container: "container"
});

const layer = chart.spaceLayer();

const facetRect = layer
.facetRect()
.data(data)
.attr("paddingRight", 20)
.attr("paddingTop", 40)
.encode("y", "metricId")
// .legend("color", false)
.axis(false);

// facetRect.interaction({
// type: "tooltip",
// series: true,
// facet: true,
// crosshairs: true
// });

facetRect
.line()
// .attr("clip", true)
.encode("x", (d) => new Date(d.date))
.encode("y", "value")
.encode("color", "metricId")
.encode("series", "type")
.attr("frame", false)
.scale("x", { ...xScale })
.scale("y", { facet: false })
.axis("x", ({ rowIndex }) => {
return rowIndex === 0
? {
position: "top",
labelAutoRotate: false,
line: true,
grid: false
}
: {
grid: false,
line: false,
tick: false,
label: false
};
})
.axis("y", {
labelAutoRotate: false,
title: false,
labelFormatter: "~s",
tick: false,
grid: false
})
.style("stroke opacity", (data) => {
return data[0]?.type === "yesterday" ? 0.25 : 1;
});

// 绘制报警
layer
.point()
.attr("key", "alarm")
.attr("clip", true)
.attr("paddingRight", INNER_PADDING_RIGHT)
.attr("paddingTop", PADDING_TOP)
.data(alarm)
.encode("x", (d) => new Date(d.date))
.encode("y", 0)
.encode("shape", "hyphen")
.axis(false)
.scale("x", { ...xScale })
.scale("y", { type: "identity", independent: true })
.style({
height: 6,
lineWidth: 6,
// 向下偏移高度的一半
transform: "translate(0,3)",
stroke: "red",
fillOpacity: 0.45
})
.animate({
enterType: "scaleInX"
});

// 绘制舆情
layer
.lineX()
.attr("key", "sentiment")
.attr("clip", true)
.attr("paddingRight", INNER_PADDING_RIGHT)
.attr("paddingTop", PADDING_TOP)
.data(sentiment)
.encode("x", (d) => new Date(d.date))
.axis(false)
.scale("x", { ...xScale })
.style({
stroke: "#FA8C16",
lineDash: [4, 4]
});

// 绘制变更
layer
.range()
.attr("key", "alter")
.attr("clip", true)
.attr("paddingRight", INNER_PADDING_RIGHT)
.attr("paddingTop", PADDING_TOP)
.data(alter)
.encode("x", (d) => [new Date(d.x1), new Date(d.x2)])
.encode("y", [0, 0])
.axis(false)
.scale("x", { ...xScale })
.scale("y", { type: "identity", independent: true })
.label({
text: (d) => d.msg,
fontSize: 10,
fill: "#6236FF",
fillOpacity: 1,
position: "left",
dy: -44,
dx: 4, // 让 connector 可以对齐 background 的边
textBaseline: "middle",
connector: true,
connectorStroke: "#6236FF",
connectorLineDash: [4, 2],
background: true,
// @todo fix paddingTop, paddingBottom 有偏差
backgroundPadding: [2, 4, 0],
backgroundFill: "#6236FF",
backgroundFillOpacity: 0.1,
// 下面设置文本省略
textOverflow: "ellipsis",
wordWrap: true,
wordWrapWidth: 120,
maxLines: 1
})
.style({
fill: "#653AFF",
fillOpacity: 0.1,
height: 24
})
.animate({ enterType: "scaleInX" });

chart.render();

return chart;
})(data, alarmData, sentimentData, alterData)
Insert cell
Insert cell
{
if (!chart) return;

const newData = data.filter((d) => metrics.includes(d.metricId));
const facetRect = chart.getNodesByType("facetRect")[0];
facetRect.data(newData);
chart.attr("height", Math.max(150, metrics.length * 120));
chart.render();
}
Insert cell
Insert cell
{
const between = (date) => date >= start && date <= end;
const newData = data.filter((d) => between(d.date));
const dateRange = [
d3Array.min(newData, (d) => d.date),
d3Array.max(newData, (d) => d.date)
];
const newXScale = {
type: "time",
mask: "HH:mm",
domain: dateRange.map((d) => new Date(d)),
// tickCount: 23,
tickInterval: 1000 * 60 * 60
};

// 更新 facet 数据
const facetRect = chart.getNodesByType("facetRect")[0];
facetRect.data(newData);
// 更新 line mark scale
const line = chart.getNodesByType("line")[0];
line.scale("x", { ...newXScale });

// 更新告警标注 mark
const alarmMark = chart.getNodeByKey("alarm");
alarmMark.scale("x", { ...newXScale });
alarmMark.data(alarmData.filter((d) => between(d.date)));

// 更新舆情标注线 mark
const sentimentMark = chart.getNodeByKey("sentiment");
sentimentMark.scale("x", { ...newXScale });
sentimentMark.data(sentimentData.filter((d) => between(d.date)));

// 更新变更标注区间 mark
const alterMark = chart.getNodeByKey("alter");
alterMark.scale("x", { ...newXScale });
alterMark.data(alterData.filter((d) => between(d.x1) || between(d.x2)));

// todo 增加 debounce
chart.render();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more