Highcharts手绘线条实现:如何扩展或借助第三方库达成手绘效果?
如何给Highcharts添加手绘效果?
刚好研究过类似的需求!给你两种可行的思路:一种是直接扩展Highcharts的原生能力,另一种是借助第三方手绘库实现,下面详细拆解:
一、原生扩展:修改绘图路径+SVG滤镜
Highcharts底层基于SVG渲染,我们可以通过重写它的绘图方法,给图形添加随机偏移,再配合SVG滤镜增强手绘质感。
1. 给折线/曲线添加不规则偏移
重写折线系列的drawGraph方法,在生成路径时给每个坐标点加上小范围的随机值,模拟手绘的抖动感:
// 重写折线的绘图方法 Highcharts.seriesTypes.line.prototype.drawGraph = function() { // 获取原始路径 const originalPath = this.getGraphPath(); // 对路径的每个线段添加随机偏移 const handDrawnPath = originalPath.map(segment => { // 只处理移动(M)和直线(L)指令 if (segment[0] === 'M' || segment[0] === 'L') { return [ segment[0], // X方向随机偏移±2像素 segment[1] + (Math.random() - 0.5) * 4, // Y方向随机偏移±3像素 segment[2] + (Math.random() - 0.5) * 6 ]; } return segment; }); // 应用修改后的路径 this.graph.attr({ d: handDrawnPath }); };
2. 让柱形/条形图边缘不规整
类似地,重写柱形系列的drawPoints方法,调整柱形的四个顶点坐标,让边缘呈现手绘的粗糙感:
// 重写柱形的绘图方法 Highcharts.seriesTypes.column.prototype.drawPoints = function() { this.points.forEach(point => { const { x, y, width, height } = point.shapeArgs; // 给四个顶点添加随机偏移 const x1 = x + (Math.random() - 0.5) * 3; const x2 = x + width + (Math.random() - 0.5) * 3; const yTop1 = y + (Math.random() - 0.5) * 5; const yTop2 = y + (Math.random() - 0.5) * 5; const yBottom = y + height; // 重新构建柱形的SVG路径 const handDrawnPath = [ ['M', x1, yBottom], ['L', x1, yTop1], ['L', x2, yTop2], ['L', x2, yBottom], ['Z'] ]; point.graphic.attr({ d: handDrawnPath }); }); };
3. 添加SVG滤镜增强质感
配合一个「噪点位移」滤镜,让整个图表看起来更像手绘在粗糙纸张上的效果:
首先在页面中添加一个隐藏的SVG定义滤镜:
<svg width="0" height="0" style="position: absolute; visibility: hidden;"> <filter id="handDrawFilter"> <!-- 生成分形噪点 --> <feTurbulence type="fractalNoise" baseFrequency="0.01" numOctaves="2" result="noise"/> <!-- 用噪点位移原图 --> <feDisplacementMap in="SourceGraphic" in2="noise" scale="3"/> </filter> </svg>
然后把滤镜应用到Highcharts容器:
.highcharts-container { filter: url(#handDrawFilter); }
二、第三方库方案:借助rough.js快速实现
如果不想自己折腾路径修改,可以用专门做手绘风格SVG的rough.js,思路是让Highcharts先生成基础图表,再用rough.js重新绘制关键图形元素。
实现步骤:
- 先初始化Highcharts图表,然后监听
render或redraw事件; - 获取图表中的SVG元素(比如折线、柱形的路径);
- 用rough.js重新绘制这些路径,并隐藏原有的图形:
// 初始化图表 const chart = Highcharts.chart('container', { // 你的图表配置项 series: [{ type: 'line', data: [12, 34, 23, 56, 45] }] }); // 监听渲染完成事件 chart.on('render', function() { const svgEl = chart.container.querySelector('svg'); // 创建rough.js的SVG绘制实例 const roughCanvas = rough.svg(svgEl); // 处理折线:获取原始路径,用rough.js重绘 const lineGraph = this.series[0].graph; const originalPath = lineGraph.attr('d'); // 绘制手绘风格的折线, roughness控制粗糙程度 roughCanvas.draw(originalPath, { roughness: 1.5, strokeWidth: 2 }); // 隐藏原折线 lineGraph.hide(); // 处理柱形(如果有) this.series.forEach(series => { if (series.type === 'column') { series.points.forEach(point => { const originalPath = point.graphic.attr('d'); roughCanvas.draw(originalPath, { roughness: 1.2, fill: '#2f7ed8', fillStyle: 'hachure' }); point.graphic.hide(); }); } }); });
注意事项:
- 如果图表有交互(比如缩放、tooltip),需要在
redraw事件里也执行重绘逻辑; - 调整
roughness参数可以控制手绘的粗糙程度,数值越大越不规则; - 数据量很大时,rough.js的重绘可能会有性能损耗,建议只在小数据量场景使用。
内容的提问来源于stack exchange,提问作者alpinist
相关产品推荐
相关产品推荐

