Highcharts 加载大批量散点图界面报错,
"highcharts"版本"^11.1.0"
vue2脚手架,使用 highcharts 绘制折线图加散点图
同样的代码配置,折线图能加载三十万数据,没有问题,而散点图,加载一千个数据就已经崩溃了
界面上没有报错,有警告
highcharts.js:12 Highcharts warning #12: www.highcharts.com/errors/12/
百度得到的结果就是数据量太大了
<script>
import Highcharts, { color } from 'highcharts';
import Boost from "highcharts/modules/boost.js";
import highstock from 'highcharts/modules/stock.js';
import exporting from 'highcharts/modules/exporting.js';
import exportData from 'highcharts/modules/export-data.js';
const moment = require('moment')
import { cloneDeep } from 'lodash'
highstock(Highcharts)
Boost(Highcharts);
exporting(Highcharts);
exportData(Highcharts);
import webSocket from './webSocket'
const instructName = '频率'
export default {
mixins: [webSocket],
props: {
paramIdList: {
type: Array,
default: () => []
},
cmdPoints: {
type: Array,
default: () => []
},
colorList: {
type: Array,
default: () => []
},
startTime: {
type: String,
default: () => ''
},
endTime: {
type: String,
default: () => ''
},
},
data() {
return {
highchart: null,
taggingList: [], // 获取到所有带有标注的点
};
},
watch: {
paramIdList: {
deep: true,
handler: function (newV) {
if (newV.length > 0) {
let arr = []
newV.forEach((obj, index) => {
arr.push({
animation: false,
type: "line",
name: obj,
data: [],
color: this.colorList[index + 1]
})
});
arr.push({
animation: false,
type: "scatter",
name: instructName,
data: [],
marker: {
radius: 4,
enabled: true, // 确保标记是启用的
symbol: 'circle'
},
color: this.colorList[0],
yAxis: 1
})
this.drawChart(arr);
this.wsSend()
}
}
},
cmdPoints: {
deep: true,
immediate: true,
handler: function (newV) {
if (newV.length > 0) {
this.setPoints(newV)
}
}
}
},
activated() {
this.doWebSocket()
},
methods: {
setPoints(arr) {
let arrLength = arr.length
let chunkSize = 2000
let seriesObj = this.highchart.series.find(item => item.name == instructName)
seriesObj.setData(arr.slice(0, chunkSize))
// seriesObj.setData(arr)
// for (let i = chunkSize; i < arrLength; i++) {
// // seriesObj.addPoint(arr[i], true, false);
// // seriesObj.setData(arr.slice(i, i + chunkSize))
// }
},
drawChart(series) {
let that = this
console.time('Timer')
this.highchart = Highcharts.stockChart('highchartsBox', {
series,
boost: {
useGPUTranslations: true,
usePreAllocated: true
},
exporting: {
buttons: {
enabled: true,
contextButton: {
menuItems: [{
text: '查看全屏', // 菜单项的文本
onclick: function () {
this.fullscreen.toggle()
}
}]
}
}
},
chart: {
animation: false,
width: null,
zoomType: 'x',
spacingLeft: 50,
spacingRight: 50,
scrollablePlotArea: {
minWidth: 600,
scrollPositionX: 1
},
events: {
click: function () { },
load: function () { }
}
},
xAxis: {
top: "80%", // 设置X轴距离顶部的位置
height: "0%", // 设置X轴的高度
type: 'datetime', // 设置x轴为时间戳格式
ordinal: false,
minRange: 1000,
tickInterval: 1000,
labels: {
// step: 1, // 设置为1以显示所有的X轴坐标点
enabled: false
},
},
stockTools: {
gui: {
enabled: false // 启用左侧菜单栏
}
},
scrollbar: {
enabled: false // 禁用滚动条
},
accessibility: {
enabled: false
},
time: {
useUTC: false
},
rangeSelector: {
enabled: true, // 禁用范围选择器
inputEnabled: false, // 是否启用输入框
selected: null, // 禁用默认选中的按钮
buttons: [{
type: 'all',
text: '全部'
}]
},
yAxis: [
{
labels: {
align: 'left'
},
height: '80%',
resize: {
enabled: true
},
gridLineWidth: 1, // 设置分割线的宽度
// tickInterval: 0.01, // 设置刻度间隔为 1
gridLineDashStyle: 'Dash',
gridLineColor: '#e0e0e0', // 设置分割线的颜色
opposite: false //
},
{
labels: {
align: 'left'
},
visible: false, // 设置为 false 隐藏该 Y 轴
top: '80%',
height: '20%',
tickPositions: [-1, 0],
min: -1, // 设置合适的最小值
max: 0,
offset: 0,
gridLineWidth: 1, // 设置分割线的宽度
tickInterval: 0.01, // 设置刻度间隔为 1
gridLineDashStyle: 'Dash',
gridLineColor: '#e0e0e0', // 设置分割线的颜色
opposite: false //
}
],
navigator: {
enabled: true, // 启用缩略图
adaptToUpdatedData: true, // 根据数据更新缩略图
series: {
color: '#ccc', // 缩略图的颜色
lineWidth: 1, // 缩略图的线宽
fillOpacity: 0.2 // 填充透明度
},
xAxis: {
labels: {
formatter: function () {
return moment(this.value).format('YYYY-MM-DD HH:mm:ss.SSS')
},
style: {
fontSize: '10px'
},
enabled: true
},
minRange: 1000 // 缩略图的最小范围
}
},
// 节点点击事件
plotOptions: {
series: {
// dataGrouping: {
// enabled: true, // 启用数据分组
// approximation: 'average', // 聚合方式:平均值
// groupPixelWidth: 10 // 每个分组的宽度(像素)
// },
point: {
events: {
click: function () {
that.sendPoint(this)
}
}
}
}
},
tooltip: {
enabled: true, // 是否显示 tooltip
backgroundColor: 'rgba(255, 255, 255, 1)',
borderWidth: 2,
borderColor: 'black',
borderRadius: 5,
useHTML: false,
shared: false, // 使 tooltip 对所有系列共享
crosshairs: true, // 显示十字准线
formatter: function () {
return that.drawTooltip(this)
}
},
});
console.timeEnd("Timer");
},
chartsMarker(e) {
let scatterObj = {}
this.highchart.series.forEach(i => {
if (i.name == instructName) {
scatterObj = i
}
})
// 高亮对应的散点
let count = 0
for (let i = 0; i < scatterObj.data.length; i++) {
const ele = scatterObj.data[i];
if (ele.marker && ele.marker.fillColor == 'red') {
ele.update({
marker: {
enabled: true,
radius: 4,
fillColor: this.colorList[0], // 设置标记的填充颜色
}
})
count++
}
if (ele.params.sendTime == e.sendTime) {
ele.update({
marker: {
enabled: true,
radius: 6,
fillColor: 'red', // 设置标记的填充颜色
},
});
count++
}
if (count == 2) {
break
}
}
},
// 绘制鼠标提示框
drawTooltip(obj) {
let str = ''
if (obj.series.name == instructName) {
let param = obj.point.params
str = `${param.sendTime}<br/>指令ID:${param.cmdId}<br/>指令名称:${param.cmdName}<br/>目标设备:${param.targetDevice}`
} else {
let points = obj.points
let str1 = ''
points.forEach(i => {
str1 += `${i.series.name}:${i.y}<br/>`
})
str = `${moment(obj.x).format('YYYY-MM-DD HH:mm:ss.SSS')}<br/>${str1}`
}
return str
},
// 节点 点击事件
sendPoint(obj) {
if (obj?.params?.cmdId) {
if (obj.options?.dataLabels?.enabled) {
obj.update({
marker: {
enabled: true,
radius: 4,
fillColor: this.colorList[0], // 设置标记的填充颜色
},
dataLabels: {
enabled: false,
}
});
} else {
let params = obj.params
obj.update({
marker: {
enabled: true,
radius: 6,
fillColor: '#00f', // 设置标记的填充颜色
},
dataLabels: {
enabled: true,
backgroundColor: this.colorList[0],
borderWidth: 1,
borderColor: 'black',
borderRadius: 5,
crop: false,
overflow: 'none', // 使标签不被裁剪
allowOverlap: true, // 允许重叠
// useHTML: true,
formatter: function () {
return `${params.sendTime}<br/>指令ID:${params.cmdId}<br/>指令名称:${params.cmdName}<br/>目标设备:${params.targetDevice}`
},
style: {
fontWeight: 'normal',
textShadow: 'none', // 禁用阴影
color: '#FFF'
},
x: 0,
y: -20
},
});
}
this.$emit('clickPointNode', obj)
} else {
var lineColor = obj.series.color;
if (!obj.marker || !obj.marker.enabled) {
obj.update({
marker: {
enabled: true,
radius: 6
},
dataLabels: {
enabled: true,
backgroundColor: lineColor,
borderWidth: 1,
borderColor: 'black',
borderRadius: 5,
// crop: false,
// overflow: 'none', // 使标签不被裁剪
allowOverlap: true, // 允许重叠
// useHTML: true,
formatter: function () {
return `${moment(obj.x).format('YYYY-MM-DD HH:mm:ss.SSS')}<br/>${obj.series.name}:${obj.y}`
},
style: {
fontWeight: 'normal',
textShadow: 'none', // 禁用阴影
color: '#FFF'
}
},
});
} else {
obj.update({
marker: {
enabled: false,
},
dataLabels: {
enabled: false,
}
});
}
// 强制重新绘画这个图形
// obj.series.chart.redraw();
// 异常点标注暂时不作
// this.openTagging(obj)
}
},
openTagging(obj) {
let msg = `参数ID:${obj.series.name}<br/>当前坐标点:(${moment(obj.x).format('YYYY-MM-DD HH:mm:ss.SSS')},${obj.y})`
this.$prompt(msg, '异常点标注', {
dangerouslyUseHTMLString: true,
inputPlaceholder: '请输入异常标注信息',
inputErrorMessage: '异常标注信息不可为空',
inputPattern: /\S/,
showInput: true,
}).then(({ value }) => {
let params = {
name: obj.series.name,
x: obj.x,
y: obj.y,
value: value
}
this.submitTagging(params)
})
},
async submitTagging(params) {
setTimeout(() => {
// let res = await api(params)
this.$message.success('当前点位标注成功')
}, 1000);
},
async getTagging() {
setTimeout(() => {
// let res = await api(params)
this.taggingList = []
}, 1000);
},
},
};
</script>
折线图绘制逻辑,将数据先保存,等数据完全返回,就直接绘制,没有问题,目前能渲染三十万数据
// 处理接收到的消息
wsOnmessage(event) {
const message = JSON.parse(event.data)
let { currentBatch, totalBatch, data } = message
Object.keys(data).forEach(i => {
if (this.socketDataObj.hasOwnProperty(i)) {
this.socketDataObj[i] = [...this.socketDataObj[i], ...data[i]]
} else {
this.socketDataObj[i] = data[i]
}
})
if (currentBatch == totalBatch) {
let series = this.highchart.series
series.forEach((item, index) => {
if (this.socketDataObj.hasOwnProperty(item.name)) {
series[index].setData(this.socketDataObj[item.name])
}
})
}
},
散点图绘制逻辑,散点图数据是正常接口返回,
setPoints(arr) {
let arrLength = arr.length
let chunkSize = 2000
let seriesObj = this.highchart.series.find(item => item.name == instructName)
seriesObj.setData(arr.slice(0, chunkSize))
// seriesObj.setData(arr)
// for (let i = chunkSize; i < arrLength; i++) {
// // seriesObj.addPoint(arr[i], true, false);
// // seriesObj.setData(arr.slice(i, i + chunkSize))
// }
},
图形整体就是N条折线图,一个散点图,使用双Y轴,因为散点表示频率,只有时间X轴,没有y轴,所以设置散点图的点是【{x: 时间戳,y:-1,params:{其他参数}}】。第二根y轴设置位置在第一根y轴下方,设置隐藏,这样能看起来是在一起的。
界面整体处理逻辑就是,先将图生成出来,但是没有点。因为折线图数据量比较大,使用ws传递,接收到数据之后先保存,等数据完全传输,使用setData 一并绘制多条折线图。折线图绘制没有问题,目前测试三十万数据,可以完全绘制。
不能使用 数据聚和,没十个点取平均值等设置 客户要求如此
// dataGrouping: {
// enabled: true, // 启用数据分组
// approximation: 'average', // 聚合方式:平均值
// groupPixelWidth: 10 // 每个分组的宽度(像素)
// },
我的问题就是关于,散点图。数据约有上万条,我设置切割数据为一千条时候,界面就可以完全加载,散点图正常,折线图正常。
但是我设置散点图切割一千二百条数据时候就会报警告,
highcharts.js:12 Highcharts warning #12: www.highcharts.com/errors/12/
散点图使用addPoint 循环会崩溃,并且数据量很多,不能使用这个
seriesObj.addPoint(arr[i], true, false);
折线图就是使用setData 一并将数据添加的,但是到散点图就不行了,
seriesObj.setData(arr.slice(i, i + chunkSize))
为什么同样的配置,一个能绘制三十万数据,而另一个只能绘制一千条数据,我想要的是将散点图同样绘制十万级数据。
界面整体效果 如 https://segmentfault.com/q/1010000045158901 展示
附带效果截图,
这个图是散点截取一千之后显示得
这个图是散点截取两千之后显示得,散点图根本加载不出来
报错信息如图所示
使用在线编辑可以显示一亿数据,同样的配置
以下是在线编辑代码,
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
<script src="https://code.highcharts.com/stock/modules/boost.js"></script>
<script src="https://code.highcharts.com/stock/modules/export-data.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
<div id="container"></div>
// Create the chart
const data = [],
n = 1000000;
for (let i = 0; i < n; i += 1) {
data.push({ x: i, y: -1 });
}
var aaa = Highcharts.stockChart("container", {
series: [
{
animation: false,
type: "scatter",
name: "频率",
data: [],
marker: {
radius: 4,
enabled: true, // 确保标记是启用的
symbol: "circle"
},
yAxis: 1
// type: "scatter",
// data: data,
// marker: {
// radius: 0.5
// },
}
],
boost: {
useGPUTranslations: true,
usePreAllocated: true
},
exporting: {
buttons: {
enabled: true,
contextButton: {
menuItems: [
{
text: "查看全屏", // 菜单项的文本
onclick: function () {
this.fullscreen.toggle();
}
}
]
}
}
},
chart: {
animation: false,
width: null,
zoomType: "x",
spacingLeft: 50,
spacingRight: 50,
scrollablePlotArea: {
minWidth: 600,
scrollPositionX: 1
},
events: {
click: function () {},
load: function () {}
}
},
xAxis: {
top: "80%", // 设置X轴距离顶部的位置
height: "0%", // 设置X轴的高度
type: "datetime", // 设置x轴为时间戳格式
ordinal: false,
minRange: 1000,
tickInterval: 1000,
labels: {
// step: 1, // 设置为1以显示所有的X轴坐标点
enabled: false
}
},
stockTools: {
gui: {
enabled: false // 启用左侧菜单栏
}
},
scrollbar: {
enabled: false // 禁用滚动条
},
accessibility: {
enabled: false
},
time: {
useUTC: false
},
rangeSelector: {
enabled: true, // 禁用范围选择器
inputEnabled: false, // 是否启用输入框
selected: null, // 禁用默认选中的按钮
buttons: [
{
type: "all",
text: "全部"
}
]
},
yAxis: [
{
labels: {
align: "left"
},
height: "80%",
resize: {
enabled: true
},
gridLineWidth: 1, // 设置分割线的宽度
// tickInterval: 0.01, // 设置刻度间隔为 1
gridLineDashStyle: "Dash",
gridLineColor: "#e0e0e0", // 设置分割线的颜色
opposite: false //
},
{
labels: {
align: "left"
},
visible: false, // 设置为 false 隐藏该 Y 轴
top: "80%",
height: "20%",
tickPositions: [-1, 0],
min: -1, // 设置合适的最小值
max: 0,
offset: 0,
gridLineWidth: 1, // 设置分割线的宽度
tickInterval: 0.01, // 设置刻度间隔为 1
gridLineDashStyle: "Dash",
gridLineColor: "#e0e0e0", // 设置分割线的颜色
opposite: false //
}
],
navigator: {
enabled: true, // 启用缩略图
adaptToUpdatedData: true, // 根据数据更新缩略图
series: {
color: "#ccc", // 缩略图的颜色
lineWidth: 1, // 缩略图的线宽
fillOpacity: 0.2 // 填充透明度
},
xAxis: {
labels: {
style: {
fontSize: "10px"
},
enabled: true
},
minRange: 1000 // 缩略图的最小范围
}
},
tooltip: {
enabled: true, // 是否显示 tooltip
backgroundColor: "rgba(255, 255, 255, 1)",
borderWidth: 2,
borderColor: "black",
borderRadius: 5,
useHTML: false,
shared: false, // 使 tooltip 对所有系列共享
crosshairs: true // 显示十字准线
}
});
function setPoints() {
let seriesObj = aaa.series.find((item) => item.name == "频率");
seriesObj.setData(data);
}
setPoints();
可以设置属性turboThreshold
plotOptions: {
series: {
turboThreshold: 0 //设置0关掉性能阈值检查,或者设置为你的数据量最大值
}
},
Highcharts 报出的警告 #12
通常与渲染性能相关,尤其是在处理大量数据点时。在你的情况下,虽然你已经使用 Boost 模块来尝试优化渲染性能,但在处理大量散点图数据时仍然遇到了问题。这可能是由于以下几个原因:
减少数据量:
优化数据加载:
使用数据分组:
dataGrouping
),这可以帮助减少显示的数据点数量,同时保持图表的可读性。你可以在 plotOptions
中为 series 配置 dataGrouping
。优化散点图标记:
marker.radius
来减少标记的大小。检查并优化浏览器性能:
使用 WebGL(如果可能):
调试和性能分析:
处理大量数据点时,优化渲染性能是关键。通过减少数据量、优化数据加载、使用数据分组、优化标记显示、检查浏览器性能以及进行性能分析,你可以显著提高 Highcharts 图表的性能和响应性。
本章节我们将为大家介绍 Highcharts 的散点图。 我们在前面的章节已经了解了 Highcharts 配置语法。接下来让我们来看下 Highcharts 的其他配置。 配置 chart 配置 配置 chart 的 type 为 'scatter' 。chart.type 描述了图表类型。默认值为 "line"。 chart.zoomType 属性可配置图表放大 ,通过拖动鼠标进行缩放,沿x轴
Highcharts 组合图 以下实例演示了散点图上添加回归线。 我们在前面的章节已经了解了 Highcharts 基本配置语法。接下来让我们来看下其他的配置。 配置 series 配置 设置 series 的 type 属性为 line/scatter ,series.type 描述了数据列类型。默认值为 "line"。 var series = { type: 'scatter' };
主要内容:GWT Highcharts 散点图 介绍,GWT Highcharts 散点图 示例GWT Highcharts 散点图 介绍 我们已经在《GWT Highcharts 入门案例》一章中看到了用于绘制此图表的步骤。现在让我们考虑以下示例以进一步了解散点图。 系列 将图表类型配置为基于散点图。series.type决定图表的系列类型。此处,默认值为“line”。 GWT Highcharts 散点图 示例 输出结果为:
我洛基,在“开始”之后,我遵循https://getbootstrap.com/docs/4.3/layout/overview/的指示(我猜在那个例子中缺少了一些东西) 我说: 链接到头标签中的css文件 但是它不起作用,类“hid-ph-flex”总是在“代码”显示中:无“代码”,火狐调试器警告说:“未知规则或错误读取规则在“包括” 我想我忘了一份文件,有什么帮助吗???
谢谢你的帮助。Haskell看起来很有趣,但如果没有一个好教授的指导和指导,这是一门很难学的语言。我只是想自学这门语言。
问题内容: 在使用jQuery学习Ajax请求的过程中,我尝试通过单击链接来加载google主页。所以我写了类似的东西: 在身体某处: 哪个没有用,最初我以为是语法错误或某些错误。但是后来,当我在服务器上用静态html文件替换google url时,它可以正常工作。 它是设计成这样工作的(如果是,为什么?),或者我做错了什么? 编辑:请问有人可以解释(或引用)跨域ajax调用引入的安全性问题吗?换