Skip to content

Commit

Permalink
feat(bar): Intent to ship bar.radius option (#476)
Browse files Browse the repository at this point in the history
- Implement bar rounded corner option.
- Changed the bar path's command from line based(L) to use H and V to simplify data definition.

Fix #359
Close #476
  • Loading branch information
netil authored Jul 6, 2018
1 parent 83e1ce7 commit 17e2b41
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 37 deletions.
40 changes: 29 additions & 11 deletions demo/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -2017,37 +2017,55 @@ d3.select(".chart_area")
]
},
BarChartOptions: {
Width: {
BarPadding: {
options: {
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 130, 100, 140, 200, 150, 50],
["data3", 130, 100, 140, 200, 150, 50]
["data2", 130, 250, 140, 200, 150, 50],
["data3", 100, 200, 340, 300, 250, 250],
["data4", 80, 230, 240, 100, 350, 150]
],
type: "bar"
},
bar: {
width: {
ratio: 0.9,
max: 30
padding: 3
}
}
},
BarRadius: {
options: {
data: {
columns: [
["data1", 80, 250, -200, 200, 250, 150],
["data2", 170, -350, 240, 200, -250, 150],
["data3", -120, 100, 240, -300, 350, 350],
["data4", 180, 130, 340, 200, 250, -250]
],
type: "bar"
},
bar: {
radius: {
ratio: 0.5
}
}
}
},
Padding: {
BarWidth: {
options: {
data: {
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data2", 130, 250, 140, 200, 150, 50],
["data3", 100, 200, 340, 300, 250, 250],
["data4", 80, 230, 240, 100, 350, 150]
["data2", 130, 100, 140, 200, 150, 50],
["data3", 130, 100, 140, 200, 150, 50]
],
type: "bar"
},
bar: {
padding: 3
width: {
ratio: 0.9,
max: 30
}
}
}
}
Expand Down
62 changes: 54 additions & 8 deletions spec/shape/shape.bar-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,33 +301,79 @@ describe("SHAPE BAR", () => {
data: {
type: "bar",
columns: [
["data1", 30, 200, 100, 400, 150, 250],
["data1", 30, 200, -100, 400, 150, 250],
["data2", 130, 340, 200, 500, 250, 350]
]
},
bar: {
width: width,
padding: padding
width,
padding,
radius: 10
},
axis: {
rotated: false
}
};
});

const checkRadius = path => {
const removeSpace = v => v.replace(/\s/g,"");
const main = chart.internal.main;

// check the path from the third data value
main.selectAll(`.${CLASS.shape}.${CLASS.bar}-2`).each(function(d, i) {
expect(removeSpace(this.getAttribute("d"))).to.be.equal(removeSpace(path[i]));
})
};

it(`bar width should be ${width}px`, () => {
const internal = chart.internal;
const barWidth = internal.main.select(`.${CLASS.chartBar} path.${CLASS.shape}`)
const main = chart.internal.main;
const barWidth = main.select(`.${CLASS.chartBar} path.${CLASS.shape}`)
.node().getBBox().width;

expect(barWidth).to.be.equal(width);
});

it(`bar padding should be ${padding}px`, () => {
const internal = chart.internal;
const bar1 = internal.main.select(`.${CLASS.chartBar}.${CLASS.target}-data1 path.${CLASS.shape}`)
const main = chart.internal.main;
const targetClass = `.${CLASS.chartBar}.${CLASS.target}`;

const bar1 = main.select(`${targetClass}-data1 path.${CLASS.shape}`)
.node().getBBox().x + width;
const bar2 = internal.main.select(`.${CLASS.chartBar}.${CLASS.target}-data2 path.${CLASS.shape}`)
const bar2 = main.select(`${targetClass}-data2 path.${CLASS.shape}`)
.node().getBBox().x;

expect(bar2 - bar1).to.be.equal(padding);
});

it("check the bar radius", () => {
checkRadius([
"M228.5,331.55555555555554 V380.5833333333333 a10,10 1 0 0 10,10H233.5 a10,10 1 0 0 10,-10V331.55555555555554z",
"M246.5,331.55555555555554 V223.5 a10,10 0 0 1 10,-10H251.5 a10,10 0 0 1 10,10V331.55555555555554z"
]);
});

it("set options axis.rotated=true", () => {
args.axis.rotated = true;
});

it("check the rotated axis bar radius", () => {
checkRadius([
"M131.11111111111111,161.5 H39.166666666666664 a10,10 1 0 0 -10,10 V166.5 a10,10 1 0 0 10,10 H131.11111111111111z",
"M131.11111111111111,179.5 H285 a10,10 0 0 1 10,10 V184.5 a10,10 0 0 1 -10,10 H131.11111111111111z"
]);
});

it("set options axis.rotated=true", () => {
args.axis.rotated = false;
args.bar.radius = {ratio: 0.5};
});

it("check the axis bar radius in ratio", () => {
checkRadius([
"M228.5,331.55555555555554 V383.0833333333333 a7.5,7.5 1 0 0 7.5,7.5 H236 a7.5,7.5 1 0 0 7.5,-7.5 V331.55555555555554z",
"M246.5,331.55555555555554 V221 a7.5,7.5 0 0 1 7.5,-7.5 H254 a7.5,7.5 0 0 1 7.5,7.5 V331.55555555555554z"
]);
});
});
});
39 changes: 26 additions & 13 deletions src/config/Options.js
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ export default class Options {
/**
* Enable to select data points by dragging.<br><br>
* If this option set true, data points can be selected by dragging.
* **NOTE:** If this option set true, scrolling on the chart will be disabled because dragging event will handle the event.
* - **NOTE:** If this option set true, scrolling on the chart will be disabled because dragging event will handle the event.
* @name data․selection․draggable
* @memberOf Options
* @type {Boolean}
Expand Down Expand Up @@ -1029,7 +1029,7 @@ export default class Options {
* @type {Object}
* @property {Array} [color.pattern] custom color pattern
* @property {Function} [color.tiles] if defined, allows use svg's patterns to fill data area. It should return an array of [SVGPatternElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGPatternElement).
* - **Note:** The pattern element's id will be defined as `bb-colorize-pattern-$COLOR-VALUE`.<br>
* - **NOTE:** The pattern element's id will be defined as `bb-colorize-pattern-$COLOR-VALUE`.<br>
* ex. When color pattern value is `['red', '#fff']` and defined 2 patterns,then ids for pattern elements are:<br>
* - `bb-colorize-pattern-red`
* - `bb-colorize-pattern-fff`
Expand Down Expand Up @@ -1504,7 +1504,7 @@ export default class Options {

/**
* Set to display system tooltip for tick text
* - **Note:** Only available for category axis type (`axis.x.type='category'`)
* - **NOTE:** Only available for category axis type (`axis.x.type='category'`)
* @name axis․x․tick․tooltip
* @memberOf Options
* @type {Boolean}
Expand Down Expand Up @@ -1902,7 +1902,7 @@ export default class Options {

/**
* Set the number of y axis ticks.<br><br>
* **NOTE:** The position of the ticks will be calculated precisely, so the values on the ticks will not be rounded nicely. In the case, axis.y.tick.format or axis.y.tick.values will be helpful.
* - **NOTE:** The position of the ticks will be calculated precisely, so the values on the ticks will not be rounded nicely. In the case, axis.y.tick.format or axis.y.tick.values will be helpful.
* @name axis․y․tick․time
* @memberOf Options
* @private
Expand All @@ -1929,7 +1929,7 @@ export default class Options {
* You can set padding for y axis to create more space on the edge of the axis.
* This option accepts object and it can include top and bottom. top, bottom will be treated as pixels.
*
* **NOTE:** For area and bar type charts, [area.zerobased](#.area) or [bar.zerobased](#.bar) options should be set to 'false` to get padded bottom.
* - **NOTE:** For area and bar type charts, [area.zerobased](#.area) or [bar.zerobased](#.bar) options should be set to 'false` to get padded bottom.
* @name axis․y․padding
* @memberOf Options
* @type {Object}
Expand Down Expand Up @@ -2268,20 +2268,20 @@ export default class Options {
* @type {Object}
* @property {Boolean} [point.show=true] Whether to show each point in line.
* @property {Number|Function} [point.r=2.5] The radius size of each point.<br>
* - **Note:** Disabled for 'bubble' type
* - **NOTE:** Disabled for 'bubble' type
* @property {Boolean} [point.focus.expand.enabled=true] Whether to expand each point on focus.
* @property {Boolean} [point.focus.expand.r=point.r*1.75] The radius size of each point on focus.<br>
* - **Note:** For 'bubble' type, the default is `bubbleSize*1.15`
* - **NOTE:** For 'bubble' type, the default is `bubbleSize*1.15`
* @property {Number} [point.select.r=point.r*4] The radius size of each point on selected.
* @property {String} [point.type="circle"] The type of point to be drawn<br>
* - **Note:**
* - **NOTE:**
* - If chart has 'bubble' type, only circle can be used.
* - For IE, non circle point expansions are not supported due to lack of transform support.
* - **Available Values:**
* - circle
* - rectangle
* @property {Array} [point.pattern=[]] The type of point or svg shape as string, to be drawn for each line<br>
* - **Note:**
* - **NOTE:**
* - This is an `experimental` feature and can have some unexpected behaviors.
* - If chart has 'bubble' type, only circle can be used.
* - For IE, non circle point expansions are not supported due to lack of transform support.
Expand Down Expand Up @@ -2365,28 +2365,41 @@ export default class Options {
* @name bar
* @memberOf Options
* @type {Object}
* @property {Boolean} [bar.padding=0] The padding pixel value between each bar.
* @property {Number} [bar.radius] Set the radius of bar edge in pixel.<br>- **NOTE:** Only for non-stacking bars.
* @property {Number} [bar.radius.ratio] Set the radius ratio of bar edge in relative the bar's width.
* @property {Number} [bar.width] Change the width of bar chart.
* @property {Number} [bar.width.ratio=0.6] Change the width of bar chart by ratio.
* @property {Number} [bar.width.max] The maximum width value for ratio.
* @property {Boolean} [bar.zerobased=true] Set if min or max value will be 0 on bar chart.
* @property {Boolean} [bar.padding=0] The padding pixel value between each bar.
* @example
* bar: {
* padding: 1,
*
* // the 'radius' option can be used only for non-stacking bars
* radius: 10,
* // or
* radius: {
* ratio: 0.5
* }
*
* width: 10,
* // or
* width: {
* ratio: 0.2,
* max: 20
* },
* zerobased: false,
* padding: 1
*
* zerobased: false
* }
*/
bar_padding: 0,
bar_radius: undefined,
bar_radius_ratio: undefined,
bar_width: undefined,
bar_width_ratio: 0.6,
bar_width_max: undefined,
bar_zerobased: true,
bar_padding: 0,

/**
* Set bubble options
Expand Down
41 changes: 36 additions & 5 deletions src/shape/bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,50 @@ extend(ChartInternal.prototype, {
const $$ = this;
const config = $$.config;
const getPoints = $$.generateGetBarPoints(barIndices, isSub);
const isRotated = config.axis_rotated;
const isGrouped = config.data_groups.length;

// get the bar radius
const getRadius = isNumber(config.bar_radius) && config.bar_radius > 0 ?
() => config.bar_radius : (
isNumber(config.bar_radius_ratio) ? w => w * config.bar_radius_ratio : null
);

return (d, i) => {
// 4 points that make a bar
const points = getPoints(d, i);

// switch points if axis is rotated, not applicable for sub chart
const indexX = config.axis_rotated ? 1 : 0;
const indexX = +isRotated;
const indexY = +!indexX;

return `M ${points[0][indexX]},${points[0][indexY]}
L ${points[1][indexX]},${points[1][indexY]}
L ${points[2][indexX]},${points[2][indexY]}
L ${points[3][indexX]},${points[3][indexY]} z`;
const isNegative = d.value < 0;
const pathRadius = ["", ""];
let radius = 0;

if (getRadius && !isGrouped) {
const index = isRotated ? indexY : indexX;
const barW = points[2][index] - points[0][index];

radius = getRadius(barW);

const arc = `a${radius},${radius} ${isNegative ? `1 0 0` : `0 0 1`} `;

pathRadius[+!isRotated] = `${arc}${radius},${radius}`;
pathRadius[+isRotated] = `${arc}${[-radius, radius][isRotated ? "sort" : "reverse"]()}`;

isNegative && pathRadius.reverse();
}

const path = isRotated ?
`H${points[1][indexX] - radius} ${pathRadius[0]}
V${points[2][indexY] - radius} ${pathRadius[1]}
H${points[3][indexX]}` :
`V${points[1][indexY] + (isNegative ? -radius : radius)} ${pathRadius[0]}
H${points[2][indexX] - radius} ${pathRadius[1]}
V${points[3][indexY]}`;

return `M${points[0][indexX]},${points[0][indexY]} ${path}z`;
};
},

Expand Down

0 comments on commit 17e2b41

Please sign in to comment.