Skip to content

Commit

Permalink
feat(options): Intent to ship bar.linearGradient
Browse files Browse the repository at this point in the history
Implement gradient color for bar

Close #397
  • Loading branch information
netil authored Sep 27, 2022
1 parent 8569dc1 commit 3a9989f
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 45 deletions.
68 changes: 68 additions & 0 deletions demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -3967,6 +3967,74 @@ d3.select(".chart_area")
}
},
],
BarLinearGradient: [
{
options: {
data: {
columns: [
["data1", 275, 250, 200, 130, 350],
["data2", 130, 350, 273, 200, 190],
["data3", 275, 250, 200, 130, 350],
["data4", 500, 490, 770, 675, 750]
],
type: "bar",
groups: [
["data1", "data2", "data3"]
]
},
bar: {
linearGradient: {
stops: [
[0, null, 1],
[1, null, 0.3]
]

}
}
}
},
{
options: {
data: {
columns: [
["data1", 230, 280, 251, 400, 150, 546, 158],
["data2", 180, 220, 201, 300, 250, 346, 358]
],
type: "bar"
},
bar: {
linearGradient: {
x: [1, 0],
y: [0, 1],
stops: [
[0, null, 1],
[0.3, null, 0.5],
[0.6, "green", 0.7],
[0.8, null, 0.7],
[1, "purple", 1]
]
}
}
}
},
{
options: {
data: {
columns: [
["data1", 275, 250, 200],
["data2", 130, 350, 273]
],
type: "bar"
},
axis: {
rotated: true
},
bar: {
linearGradient: true
}
}
}
],
BarOverlap: [
{
options: {
Expand Down
50 changes: 50 additions & 0 deletions src/ChartInternal/internals/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,56 @@ export default {
}
},

/**
* Get data gradient color url
* @param {string} id Data id
* @returns {string}
* @private
*/
getGradienColortUrl(id: string): string {
return `url(#${this.state.datetimeId}-gradient${this.getTargetSelectorSuffix(id)})`;
},

/**
* Update linear gradient definition (for area & bar only)
* @private
*/
updateLinearGradient(): void {
const $$ = this;
const {config, data: {targets}, state: {datetimeId}, $el: {defs}} = $$;

targets.forEach(d => {
const id = `${datetimeId}-gradient${$$.getTargetSelectorSuffix(d.id)}`;
const supportedType = ($$.isAreaType(d) && "area") || ($$.isBarType(d) && "bar");
const isRotated = config.axis_rotated;

if (supportedType && defs.select(`#${id}`).empty()) {
const color = $$.color(d);
const {
x = isRotated ? [1, 0] : [0, 0],
y = isRotated ? [0, 0] : [0, 1],
stops = [[0, color, 1], [1, color, 0]]
} = config[`${supportedType}_linearGradient`];

const linearGradient = defs.append("linearGradient")
.attr("id", `${id}`)
.attr("x1", x[0])
.attr("x2", x[1])
.attr("y1", y[0])
.attr("y2", y[1]);

stops.forEach(v => {
const stopColor = isFunction(v[1]) ? v[1].bind($$.api)(d.id) : v[1];

linearGradient.append("stop")
.attr("offset", v[0])
.attr("stop-color", stopColor || color)
.attr("stop-opacity", v[2]);
});
}
});
},

/**
* Set the data over color.
* When is out, will restate in its previous color value
Expand Down
47 changes: 9 additions & 38 deletions src/ChartInternal/shape/area.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import {area as d3Area} from "d3-shape";
import {select as d3Select} from "d3-selection";
import {$AREA, $CIRCLE, $LINE} from "../../config/classes";
import {getRandom, isFunction} from "../../module/util";
import {getRandom} from "../../module/util";
import type {IData, IDataRow} from "../data/IData";
import {d3Selection} from "../../../types";

Expand All @@ -21,46 +21,17 @@ export default {
.attr("class", $$.getClass("areas", true));
},

updateAreaGradient(): void {
const $$ = this;
const {config, state: {datetimeId}, $el: {defs}} = $$;

$$.data.targets.forEach(d => {
const id = `${datetimeId}-areaGradient${$$.getTargetSelectorSuffix(d.id)}`;

if ($$.isAreaType(d) && defs.select(`#${id}`).empty()) {
const color = $$.color(d);
const {
x = [0, 0],
y = [0, 1],
stops = [[0, color, 1], [1, color, 0]]
} = config.area_linearGradient;

const linearGradient = defs.append("linearGradient")
.attr("id", `${id}`)
.attr("x1", x[0])
.attr("x2", x[1])
.attr("y1", y[0])
.attr("y2", y[1]);

stops.forEach(v => {
const stopColor = isFunction(v[1]) ? v[1].bind($$.api)(d.id) : v[1];

linearGradient.append("stop")
.attr("offset", v[0])
.attr("stop-color", stopColor || color)
.attr("stop-opacity", v[2]);
});
}
});
},

/**
* Update area color
* @param {object} d Data object
* @returns {string} Color string
* @private
*/
updateAreaColor(d: IDataRow): string {
const $$ = this;

return $$.config.area_linearGradient ?
`url(#${$$.state.datetimeId}-areaGradient${$$.getTargetSelectorSuffix(d.id)})` :
$$.color(d);
$$.getGradienColortUrl(d.id) : $$.color(d);
},

/**
Expand All @@ -74,7 +45,7 @@ export default {
const {config, state, $el, $T} = $$;
const $root = isSub ? $el.subchart : $el;

config.area_linearGradient && $$.updateAreaGradient();
config.area_linearGradient && $$.updateLinearGradient();

const area = $root.main.selectAll(`.${$AREA.areas}`)
.selectAll(`.${$AREA.area}`)
Expand Down
23 changes: 20 additions & 3 deletions src/ChartInternal/shape/bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,13 @@ export default {
*/
updateBar(withTransition: boolean, isSub = false): void {
const $$ = this;
const {$el, $T} = $$;
const {config, $el, $T} = $$;
const $root = isSub ? $el.subchart : $el;
const classBar = $$.getClass("bar", true);
const initialOpacity = $$.initialOpacity.bind($$);

config.bar_linearGradient && $$.updateLinearGradient();

const bar = $root.main.selectAll(`.${$BAR.bars}`)
.selectAll(`.${$BAR.bar}`)
.data($$.labelishData.bind($$));
Expand All @@ -86,17 +88,32 @@ export default {

$root.bar = bar.enter().append("path")
.attr("class", classBar)
.style("fill", $$.getStylePropValue($$.color))
.style("fill", $$.updateBarColor.bind($$))
.merge(bar)
.style("opacity", initialOpacity);
},

/**
* Update bar color
* @param {object} d Data object
* @returns {string} Color string
* @private
*/
updateBarColor(d: IDataRow): string {
const $$ = this;
const fn = $$.getStylePropValue($$.color);

return $$.config.bar_linearGradient ?
$$.getGradienColortUrl(d.id) : (fn ? fn(d) : null);
},

/**
* Redraw function
* @param {Function} drawFn Retuned function from .getDrawShape() => .generateDrawBar()
* @param {boolean} withTransition With or without transition
* @param {boolean} isSub Subchart draw
* @returns {Array}
* @private
*/
redrawBar(drawFn, withTransition?: boolean, isSub = false) {
const $$ = this;
Expand All @@ -105,7 +122,7 @@ export default {
return [
$$.$T(bar, withTransition, getRandom())
.attr("d", d => (isNumber(d.value) || $$.isBarRangeType(d)) && drawFn(d))
.style("fill", $$.getStylePropValue($$.color))
.style("fill", $$.updateBarColor.bind($$))
.style("opacity", null)
];
},
Expand Down
32 changes: 32 additions & 0 deletions src/config/Options/shape/bar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export default {
* @property {object} bar Bar object
* @property {number} [bar.indices.removeNull=false] Remove nullish data on bar indices positions.
* @property {number} [bar.label.threshold=0] Set threshold ratio to show/hide labels.
* @property {boolean|object} [bar.linearGradient=false] Set the linear gradient on bar.<br><br>
* Or customize by giving below object value:
* - x {Array}: `x1`, `x2` value
* - y {Array}: `y1`, `y2` value
* - stops {Array}: Each item should be having `[offset, stop-color, stop-opacity]` values.
* @property {boolean} [bar.overlap=false] Bars will be rendered at same position, which will be overlapped each other. (for non-grouped bars only)
* @property {number} [bar.padding=0] The padding pixel value between each bar.
* @property {number} [bar.radius] Set the radius of bar edge in pixel.
Expand Down Expand Up @@ -42,6 +47,30 @@ export default {
* removeNull: true
* },
*
* // will generate follwing linearGradient:
* // <linearGradient x1="0" x2="0" y1="0" y2="1">
* // <stop offset="0" stop-color="$DATA_COLOR" stop-opacity="1"></stop>
* // <stop offset="1" stop-color="$DATA_COLOR" stop-opacity="0"></stop>
* // </linearGradient>
* linearGradient: true,
*
* // Or customized gradient
* linearGradient: {
* x: [0, 0], // x1, x2 attributes
* y: [0, 0], // y1, y2 attributes
* stops: [
* // offset, stop-color, stop-opacity
* [0, "#7cb5ec", 1],
*
* // setting 'null' for stop-color, will set its original data color
* [0.5, null, 0],
*
* // setting 'function' for stop-color, will pass data id as argument.
* // It should return color string or null value
* [1, function(id) { return id === "data1" ? "red" : "blue"; }, 0],
* ]
* },
*
* // remove nullish da
* overlap: true,
*
Expand Down Expand Up @@ -84,6 +113,9 @@ export default {
* }
*/
bar_label_threshold: 0,
bar_linearGradient: <
boolean|{x?: number[]; y?: number[]; stops?: [number, string|Function|null, number]}
> false,
bar_indices_removeNull: false,
bar_overlap: false,
bar_padding: 0,
Expand Down
4 changes: 2 additions & 2 deletions test/shape/area-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ describe("SHAPE AREA", () => {
chart.data().forEach(v => {
const color = chart.color(v.id);
const selectorSuffix = internal.getTargetSelectorSuffix(v.id);
const id = `#${internal.state.datetimeId}-areaGradient${selectorSuffix}`;
const id = `#${internal.state.datetimeId}-gradient${selectorSuffix}`;
const gradient = chart.$.svg.select(id);

expect(gradient.empty()).to.be.false;
Expand Down Expand Up @@ -502,7 +502,7 @@ describe("SHAPE AREA", () => {

chart.data().forEach(v => {
const selectorSuffix = internal.getTargetSelectorSuffix(v.id);
const id = `#${internal.state.datetimeId}-areaGradient${selectorSuffix}`;
const id = `#${internal.state.datetimeId}-gradient${selectorSuffix}`;
const gradient = chart.$.svg.select(id);

expect(gradient.empty()).to.be.false;
Expand Down
Loading

0 comments on commit 3a9989f

Please sign in to comment.