mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-06 00:14:03 +01:00
committed by
Evert Timberg
parent
483d06eebd
commit
5e194021c7
@@ -14,12 +14,15 @@ Both [line](line.md) and [radar](radar.md) charts support a `fill` option on the
|
||||
| Relative dataset index <sup>1</sup> | `string` | `'-1'`, `'-2'`, `'+1'`, ... |
|
||||
| Boundary <sup>2</sup> | `string` | `'start'`, `'end'`, `'origin'` |
|
||||
| Disabled <sup>3</sup> | `boolean` | `false` |
|
||||
| Stacked value below <sup>4</sup> | `string` | `'stack'` |
|
||||
|
||||
> <sup>1</sup> dataset filling modes have been introduced in version 2.6.0<br/>
|
||||
> <sup>2</sup> prior version 2.6.0, boundary values was `'zero'`, `'top'`, `'bottom'` (deprecated)<br/>
|
||||
> <sup>2</sup> prior version 2.6.0, boundary values was `'zero'`, `'top'`, `'bottom'` (not supported anymore)<br/>
|
||||
> <sup>3</sup> for backward compatibility, `fill: true` (default) is equivalent to `fill: 'origin'`<br/>
|
||||
> <sup>4</sup> stack mode has been introduced in version 3.0.0<br/>
|
||||
|
||||
**Example**
|
||||
|
||||
```javascript
|
||||
new Chart(ctx, {
|
||||
data: {
|
||||
@@ -43,6 +46,7 @@ If you need to support multiple colors when filling from one dataset to another,
|
||||
| `below` | `Color` | Same as the above. |
|
||||
|
||||
**Example**
|
||||
|
||||
```javascript
|
||||
new Chart(ctx, {
|
||||
data: {
|
||||
@@ -60,16 +64,19 @@ new Chart(ctx, {
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| [`plugins.filler.propagate`](#propagate) | `boolean` | `true` | Fill propagation when target is hidden.
|
||||
|
||||
### propagate
|
||||
|
||||
`propagate` takes a `boolean` value (default: `true`).
|
||||
|
||||
If `true`, the fill area will be recursively extended to the visible target defined by the `fill` value of hidden dataset targets:
|
||||
|
||||
**Example**
|
||||
|
||||
```javascript
|
||||
new Chart(ctx, {
|
||||
data: {
|
||||
@@ -92,8 +99,8 @@ new Chart(ctx, {
|
||||
```
|
||||
|
||||
`propagate: true`:
|
||||
- if dataset 2 is hidden, dataset 4 will fill to dataset 1
|
||||
- if dataset 2 and 1 are hidden, dataset 4 will fill to `'origin'`
|
||||
-if dataset 2 is hidden, dataset 4 will fill to dataset 1
|
||||
-if dataset 2 and 1 are hidden, dataset 4 will fill to `'origin'`
|
||||
|
||||
`propagate: false`:
|
||||
- if dataset 2 and/or 4 are hidden, dataset 4 will not be filled
|
||||
-if dataset 2 and/or 4 are hidden, dataset 4 will not be filled
|
||||
|
||||
@@ -10,12 +10,25 @@ import {clipArea, unclipArea} from '../helpers/helpers.canvas';
|
||||
import {isArray, isFinite, valueOrDefault} from '../helpers/helpers.core';
|
||||
import {_normalizeAngle} from '../helpers/helpers.math';
|
||||
|
||||
/**
|
||||
* @typedef { import('../core/core.controller').default } Chart
|
||||
* @typedef { import('../core/core.scale').default } Scale
|
||||
* @typedef { import("../elements/element.point").default } Point
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Chart} chart
|
||||
* @param {number} index
|
||||
*/
|
||||
function getLineByIndex(chart, index) {
|
||||
const meta = chart.getDatasetMeta(index);
|
||||
const visible = meta && chart.isDatasetVisible(index);
|
||||
return visible ? meta.dataset : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Line} line
|
||||
*/
|
||||
function parseFillOption(line) {
|
||||
const options = line.options;
|
||||
const fillOption = options.fill;
|
||||
@@ -35,7 +48,11 @@ function parseFillOption(line) {
|
||||
return fill;
|
||||
}
|
||||
|
||||
// @todo if (fill[0] === '#')
|
||||
/**
|
||||
* @param {Line} line
|
||||
* @param {number} index
|
||||
* @param {number} count
|
||||
*/
|
||||
function decodeFill(line, index, count) {
|
||||
const fill = parseFillOption(line);
|
||||
let target = parseFloat(fill);
|
||||
@@ -52,7 +69,7 @@ function decodeFill(line, index, count) {
|
||||
return target;
|
||||
}
|
||||
|
||||
return ['origin', 'start', 'end'].indexOf(fill) >= 0 ? fill : false;
|
||||
return ['origin', 'start', 'end', 'stack'].indexOf(fill) >= 0 && fill;
|
||||
}
|
||||
|
||||
function computeLinearBoundary(source) {
|
||||
@@ -163,6 +180,103 @@ function pointsFromSegments(boundary, line) {
|
||||
return points;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ chart: Chart; scale: Scale; index: number; line: Line; }} source
|
||||
* @return {Line}
|
||||
*/
|
||||
function buildStackLine(source) {
|
||||
const {chart, scale, index, line} = source;
|
||||
const linesBelow = getLinesBelow(chart, index);
|
||||
const points = [];
|
||||
const segments = line.segments;
|
||||
const sourcePoints = line.points;
|
||||
const startPoints = [];
|
||||
sourcePoints.forEach(point => startPoints.push({x: point.x, y: scale.bottom, _prop: 'x', _ref: point}));
|
||||
linesBelow.push(new Line({points: startPoints, options: {}}));
|
||||
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const segment = segments[i];
|
||||
for (let j = segment.start; j <= segment.end; j++) {
|
||||
addPointsBelow(points, sourcePoints[j], linesBelow);
|
||||
}
|
||||
}
|
||||
return new Line({points, options: {}, _refPoints: true});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Chart} chart
|
||||
* @param {number} index
|
||||
* @return {Line[]}
|
||||
*/
|
||||
function getLinesBelow(chart, index) {
|
||||
const below = [];
|
||||
const metas = chart.getSortedVisibleDatasetMetas();
|
||||
for (let i = 0; i < metas.length; i++) {
|
||||
const meta = metas[i];
|
||||
if (meta.index === index) {
|
||||
break;
|
||||
}
|
||||
if (meta.type === 'line') {
|
||||
below.unshift(meta.dataset);
|
||||
}
|
||||
}
|
||||
return below;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Point[]} points
|
||||
* @param {Point} sourcePoint
|
||||
* @param {Line[]} linesBelow
|
||||
*/
|
||||
function addPointsBelow(points, sourcePoint, linesBelow) {
|
||||
const postponed = [];
|
||||
for (let j = 0; j < linesBelow.length; j++) {
|
||||
const line = linesBelow[j];
|
||||
const {first, last, point} = findPoint(line, sourcePoint, 'x');
|
||||
|
||||
if (!point || (first && last)) {
|
||||
continue;
|
||||
}
|
||||
if (first) {
|
||||
// First point of an segment -> need to add another point before this,
|
||||
// from next line below.
|
||||
postponed.unshift(point);
|
||||
} else {
|
||||
points.push(point);
|
||||
if (!last) {
|
||||
// In the middle of an segment, no need to add more points.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
points.push(...postponed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Line} line
|
||||
* @param {Point} sourcePoint
|
||||
* @param {string} property
|
||||
* @returns {{point?: Point, first?: boolean, last?: boolean}}
|
||||
*/
|
||||
function findPoint(line, sourcePoint, property) {
|
||||
const segments = line.segments;
|
||||
const linePoints = line.points;
|
||||
for (let i = 0; i < segments.length; i++) {
|
||||
const segment = segments[i];
|
||||
for (let j = segment.start; j <= segment.end; j++) {
|
||||
const point = linePoints[j];
|
||||
if (sourcePoint[property] === point[property]) {
|
||||
return {
|
||||
first: j === segment.start,
|
||||
last: j === segment.end,
|
||||
point
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function getTarget(source) {
|
||||
const {chart, fill, line} = source;
|
||||
|
||||
@@ -170,15 +284,29 @@ function getTarget(source) {
|
||||
return getLineByIndex(chart, fill);
|
||||
}
|
||||
|
||||
if (fill === 'stack') {
|
||||
return buildStackLine(source);
|
||||
}
|
||||
|
||||
const boundary = computeBoundary(source);
|
||||
let points = [];
|
||||
let _loop = false;
|
||||
let _refPoints = false;
|
||||
|
||||
if (boundary instanceof simpleArc) {
|
||||
return boundary;
|
||||
}
|
||||
|
||||
return createBoundaryLine(boundary, line);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Point[] | { x: number; y: number; }} boundary
|
||||
* @param {Line} line
|
||||
* @return {Line?}
|
||||
*/
|
||||
function createBoundaryLine(boundary, line) {
|
||||
let points = [];
|
||||
let _loop = false;
|
||||
let _refPoints = false;
|
||||
|
||||
if (isArray(boundary)) {
|
||||
_loop = true;
|
||||
// @ts-ignore
|
||||
@@ -187,6 +315,7 @@ function getTarget(source) {
|
||||
points = pointsFromSegments(boundary, line);
|
||||
_refPoints = true;
|
||||
}
|
||||
|
||||
return points.length ? new Line({
|
||||
points,
|
||||
options: {tension: 0},
|
||||
@@ -402,6 +531,7 @@ export default {
|
||||
if (line && line.options && line instanceof Line) {
|
||||
source = {
|
||||
visible: chart.isDatasetVisible(i),
|
||||
index: i,
|
||||
fill: decodeFill(line, i, count),
|
||||
chart,
|
||||
scale: meta.vScale,
|
||||
|
||||
64
test/fixtures/plugin.filler/fill-line-stack.json
vendored
Normal file
64
test/fixtures/plugin.filler/fill-line-stack.json
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"config": {
|
||||
"type": "line",
|
||||
"data": {
|
||||
"labels": ["0", "1", "2", "3", "4", "5", "6", "7", "8"],
|
||||
"datasets": [{
|
||||
"backgroundColor": "rgba(255, 0, 0, 0.25)",
|
||||
"data": [null, null, 0, 1, 0, 1, null, 0, 1],
|
||||
"fill": "stack"
|
||||
}, {
|
||||
"backgroundColor": "rgba(0, 255, 0, 0.25)",
|
||||
"data": [1, 1, null, 1, 0, null, 1, 1, 0],
|
||||
"fill": "stack"
|
||||
}, {
|
||||
"backgroundColor": "rgba(0, 0, 255, 0.25)",
|
||||
"data": [0, 2, null, 2, 0, 2, 0],
|
||||
"fill": "stack"
|
||||
}, {
|
||||
"backgroundColor": "rgba(255, 0, 255, 0.25)",
|
||||
"data": [2, 0, null, 0, 2, 0, 2, 0, 2],
|
||||
"fill": "stack"
|
||||
}, {
|
||||
"backgroundColor": "rgba(0, 0, 0, 0.25)",
|
||||
"data": [null, null, null, 2, null, 2, 2],
|
||||
"fill": "stack"
|
||||
}, {
|
||||
"backgroundColor": "rgba(255, 255, 0, 0.25)",
|
||||
"data": [3, 1, 1, 3, 1, 1, 3, 1, 1],
|
||||
"fill": "stack"
|
||||
}]
|
||||
},
|
||||
"options": {
|
||||
"responsive": false,
|
||||
"spanGaps": false,
|
||||
"legend": false,
|
||||
"title": false,
|
||||
"scales": {
|
||||
"x": {
|
||||
"display": false
|
||||
},
|
||||
"y": {
|
||||
"display": false,
|
||||
"stacked": true,
|
||||
"min": 0
|
||||
}
|
||||
},
|
||||
"elements": {
|
||||
"point": {
|
||||
"radius": 0
|
||||
},
|
||||
"line": {
|
||||
"borderColor": "transparent",
|
||||
"tension": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"canvas": {
|
||||
"height": 256,
|
||||
"width": 512
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
test/fixtures/plugin.filler/fill-line-stack.png
vendored
Normal file
BIN
test/fixtures/plugin.filler/fill-line-stack.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Reference in New Issue
Block a user