目录
Vue3 + hook + Apexcharts实现可视化图表渲染的封装 - 第一节 - 基础封装
Vue3 + hook + Apexcharts实现可视化图表渲染的封装 - 第二节 - 图表的全屏功能
Apexcharts实现BoxPlot中绘制散点 - 第三节 (当前所在)
摘要
当我们通过Apexcharts绘制BoxPlot前要知道BoxPlot的五个值代表分别代表:最小值,最大值,中位数,上四分位数,下四分位数,这五个数值是根据一组数据计算出来的。这时候我想在BoxPlot中显示这些数据点的分布时,我们通过查阅Apexcharts文档知道并不能满足此功能,只有类似下图的功能:
如果你按照文档里的例子实现是所有的点会在每个图例的中轴形成一条直性,并不是我们想要的散点图。我们想要的实现效果应该是下面那样,点是分布在每个图例中的。如下:
实现效果
思路
当我们从后端获取到数据之后,将数据更新到图表中时,会触发
updated
事件,通过监听这个事件在图表绘制完成时我们通过操作DOM
的方式,收到绘制这些散点。就是获取画好的BoxPlot
的svg
元素,然后生成散点的svg
将其append
进去。
代码实现
注意:
data
为后端或者自己格式化后的数组,如果不同代码里的逻辑需要更改
// 绘制散点
const paintCircle = (el, value, cx, cy) => {
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle')
circle.setAttribute('cx', cx)
circle.setAttribute('cy', cy)
circle.setAttribute('r', 4)
return circle
}
/**
* 绘制BoxPlot上的散点图
* @param {*} el
* @param {*} data 格式为[[x, [y1, y2, y3], [all data]], [x, [y1, y2, y3], [all data]]
* @param {*} special 当为true时,表示绘制的是特殊的BoxPlot,data的格式为{series, yaxis, allData}
*/
export const paintBoxPlotScatter = (el, data) => {
let min = 0, max = 1
const { min: _min, max: _max } = getBoxPlotMinAndMax(data)
min = _min
max = _max
const interval = max - min
const target = el.querySelector('svg')
const allGSvg = document.createElementNS('http://www.w3.org/2000/svg', 'g')
const height = target.querySelector('.apexcharts-xcrosshairs').getAttribute('height')
const barWidth = target.querySelector('.apexcharts-series path').getAttribute('barWidth')
const transform = target.querySelector('.apexcharts-inner').getAttribute('transform')
allGSvg.setAttribute('class', 'scatter-g')
allGSvg.setAttribute('transform', transform)
data.forEach((item, index) => {
const gSvg = document.createElementNS('http://www.w3.org/2000/svg', 'g')
gSvg.setAttribute('fill', '#c9a2ca')
const xValue = target.querySelectorAll('.apexcharts-xaxis-tick')[index].getAttribute('x1')
// 遍历每个散点值
item[2].forEach(sItem => {
// 计算每个散点的坐标, cx以xValue为中心然后生成随机便宜量
const cx = +xValue + _.random(-barWidth / 2, barWidth / 2)
// 根据height和interval计算cy
const cy = height - (sItem - min) / interval * height
gSvg.appendChild(paintCircle(el, sItem, cx, cy))
})
allGSvg.appendChild(gSvg)
})
target.appendChild(allGSvg)
}