• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

naver / billboard.js / 27122665529

08 Jun 2026 07:32AM UTC coverage: 92.54% (-1.1%) from 93.621%
27122665529

push

github

web-flow
feat(canvas): add canvas rendering mode

Add canvas entry, renderer engine, axis renderer, theme probing, and hit detection.
Support canvas flow, subchart, zoom, selection, grid/regions, export, tooltip, and focus.
Add tests, benchmarks, types, and docs for canvas mode limitations.

10455 of 11840 branches covered (88.3%)

Branch coverage included in aggregate %.

5094 of 5363 new or added lines in 68 files covered. (94.98%)

19 existing lines in 3 files now uncovered.

13349 of 13883 relevant lines covered (96.15%)

26556.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.97
/src/ChartInternal/shape/core/path.ts
1
/**
2
 * Copyright (c) 2017 ~ present NAVER Corp.
3
 * billboard.js project is licensed under the MIT license
4
 */
5
import {area as d3Area, line as d3Line} from "d3-shape";
6

7
type PathContext = CanvasRenderingContext2D | null | undefined;
8
type PathResult = string | void;
9

10
/**
11
 * Get values with null/step handling for line-like paths.
12
 * @param {object} $$ ChartInternal instance
13
 * @param {object} target Data target
14
 * @returns {Array} Drawable values
15
 * @private
16
 */
17
function getLineValues($$, target): any[] {
18
        let values = $$.config.line_connectNull ? $$.filterRemoveNull(target.values) : target.values;
9,918✔
19

20
        if ($$.isStepType(target)) {
9,918✔
21
                values = $$.convertValuesToStep(values);
258✔
22
        }
23

24
        return values;
9,918✔
25
}
26

27
/**
28
 * Generate line path drawing function shared by SVG and canvas renderers.
29
 * @param {object} $$ ChartInternal instance
30
 * @param {object} lineIndices Shape indices
31
 * @param {boolean} isSub Whether to use subchart scales
32
 * @param {object} context Optional canvas path context
33
 * @returns {function} Line path drawing function
34
 * @private
35
 */
36
export function generateDrawLinePath(
37
        $$,
38
        lineIndices,
39
        isSub?: boolean,
40
        context?: PathContext
41
): (d) => PathResult {
42
        const {config, scale} = $$;
6,333✔
43
        const lineConnectNull = config.line_connectNull;
6,333✔
44
        const isRotated = config.axis_rotated;
6,333✔
45

46
        const getPoints = $$.generateGetLinePoints(lineIndices, isSub);
6,333✔
47
        const yScale = $$.getYScaleById.bind($$);
6,333✔
48

49
        const xValue = d => (isSub ? $$.subxx : $$.xx).call($$, d);
67,076✔
50
        const yValue = (d, i) => (
6,333✔
51
                $$.isGrouped(d.id) ? getPoints(d, i)[0][1] : yScale(d.id, isSub)($$.getBaseValue(d))
67,076✔
52
        );
53

54
        let line = d3Line<any>();
6,333✔
55

56
        line = isRotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue);
6,333✔
57
        context && (line = line.context(context));
6,333✔
58

59
        if (!lineConnectNull) {
6,333✔
60
                line = line.defined(d => $$.getBaseValue(d) !== null);
67,310✔
61
        }
62

63
        const x = isSub ? scale.subX : scale.x;
6,333✔
64

65
        return d => {
6,333✔
66
                const y = yScale(d.id, isSub);
10,008✔
67
                let values = lineConnectNull ? $$.filterRemoveNull(d.values) : d.values;
10,008✔
68

69
                let x0 = 0;
10,008✔
70
                let y0 = 0;
10,008✔
71
                let path;
72

73
                if ($$.isLineType(d)) {
10,008!
74
                        const regions = config.data_regions[d.id];
10,008✔
75

76
                        if (regions && !context && $$.lineWithRegions) {
10,008✔
77
                                if ($$.isAreaRangeType(d)) {
90✔
78
                                        values = values.map(dv => ({...dv, value: $$.getRangedData(dv, "mid")}));
36✔
79
                                }
80

81
                                if ($$.isStepType(d)) {
90!
NEW
82
                                        values = $$.convertValuesToStep(values);
×
83
                                }
84

85
                                path = $$.lineWithRegions(values, scale.zoom || x, y, regions);
90✔
86
                        } else {
87
                                path = line.curve($$.getCurve(d))(getLineValues($$, {values}));
9,918✔
88
                        }
89
                } else {
NEW
90
                        if (values[0]) {
×
NEW
91
                                x0 = x(values[0].x);
×
NEW
92
                                y0 = y(values[0].value);
×
93
                        }
94

NEW
95
                        path = isRotated ? `M ${y0} ${x0}` : `M ${x0} ${y0}`;
×
96
                }
97

98
                return path || (context ? undefined : "M 0 0");
10,008!
99
        };
100
}
101

102
/**
103
 * Generate area path drawing function shared by SVG and canvas renderers.
104
 * @param {object} $$ ChartInternal instance
105
 * @param {object} areaIndices Shape indices
106
 * @param {boolean} isSub Whether to use subchart scales
107
 * @param {object} context Optional canvas path context
108
 * @returns {function} Area path drawing function
109
 * @private
110
 */
111
export function generateDrawAreaPath(
112
        $$,
113
        areaIndices,
114
        isSub?: boolean,
115
        context?: PathContext
116
): (d) => PathResult {
117
        const {config} = $$;
699✔
118
        const lineConnectNull = config.line_connectNull;
699✔
119
        const isRotated = config.axis_rotated;
699✔
120

121
        const getPoints = $$.generateGetAreaPoints(areaIndices, isSub);
699✔
122
        const yScale = $$.getYScaleById.bind($$);
699✔
123

124
        const xValue = d => (isSub ? $$.subxx : $$.xx).call($$, d);
8,517✔
125
        const value0 = (d, i) => ($$.isGrouped(d.id) ? getPoints(d, i)[0][1] : yScale(d.id, isSub)(
8,463✔
126
                $$.isAreaRangeType(d) ? $$.getRangedData(d, "high") : $$.getShapeYMin(d.id)
7,656✔
127
        ));
128
        const value1 = (d, i) => ($$.isGrouped(d.id) ? getPoints(d, i)[1][1] : yScale(d.id, isSub)(
8,517✔
129
                $$.isAreaRangeType(d) ? $$.getRangedData(d, "low") : d.value
7,710✔
130
        ));
131

132
        return d => {
699✔
133
                let values = lineConnectNull ? $$.filterRemoveNull(d.values) : d.values;
1,065✔
134
                let x0 = 0;
1,065✔
135
                let y0 = 0;
1,065✔
136
                let path;
137

138
                if ($$.isAreaType(d)) {
1,065✔
139
                        let area = d3Area<any>();
1,062✔
140

141
                        area = isRotated ?
1,062✔
142
                                area.y(xValue)
143
                                        .x0(value0)
144
                                        .x1(value1) :
145
                                area.x(xValue)
146
                                        .y0(config.area_above ? 0 : (
993✔
147
                                                config.area_below ? $$.state.height : value0
987✔
148
                                        ))
149
                                        .y1(value1);
150
                        context && (area = area.context(context));
1,062✔
151

152
                        if (!lineConnectNull) {
1,062✔
153
                                area = area.defined(d => $$.getBaseValue(d) !== null);
8,502✔
154
                        }
155

156
                        if ($$.isStepType(d)) {
1,062✔
157
                                values = $$.convertValuesToStep(values);
138✔
158
                        }
159

160
                        path = area.curve($$.getCurve(d))(values);
1,062✔
161
                } else {
162
                        if (values[0]) {
3!
163
                                x0 = $$.scale.x(values[0].x);
3✔
164
                                y0 = $$.getYScaleById(d.id)(values[0].value);
3✔
165
                        }
166

167
                        path = isRotated ? `M ${y0} ${x0}` : `M ${x0} ${y0}`;
3!
168
                }
169

170
                return path || (context ? undefined : "M 0 0");
1,065!
171
        };
172
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc