Skip to content

Commit

Permalink
fix(renderer): stroke opacity (#335)
Browse files Browse the repository at this point in the history
* Make stroke opacity independent from fill opacity
* Changed shared opacity style to be an opacity multiplication __factor__ not a replaced value
  • Loading branch information
nickofthyme authored Aug 26, 2019
1 parent 70b0aa1 commit d8c8459
Show file tree
Hide file tree
Showing 9 changed files with 560 additions and 278 deletions.
70 changes: 54 additions & 16 deletions .playground/playgroud.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,63 @@
import React from 'react';
import { Axis, Chart, getAxisId, getSpecId, Position, ScaleType, Settings, BarSeries } from '../src';

import { Axis, Chart, getAxisId, getSpecId, niceTimeFormatter, Position, ScaleType, Settings, BarSeries } from '../src';
import { KIBANA_METRICS } from '../src/utils/data_samples/test_dataset_kibana';
import { CursorEvent } from '../src/specs/settings';
import { CursorUpdateListener } from '../src/chart_types/xy_chart/store/chart_state';

export class Playground extends React.Component {
ref1 = React.createRef<Chart>();
ref2 = React.createRef<Chart>();
ref3 = React.createRef<Chart>();

onCursorUpdate: CursorUpdateListener = (event?: CursorEvent) => {
this.ref1.current!.dispatchExternalCursorEvent(event);
this.ref2.current!.dispatchExternalCursorEvent(event);
this.ref3.current!.dispatchExternalCursorEvent(event);
};

render() {
return (
<div className="chart">
<Chart>
<Settings showLegend={true} />
<Axis id={getAxisId('y')} position={Position.Left} />
<Axis id={getAxisId('x')} position={Position.Bottom} />
<BarSeries
id={getSpecId('bar')}
yScaleType={ScaleType.Linear}
xScaleType={ScaleType.Time}
xAccessor={0}
yAccessors={[1]}
data={KIBANA_METRICS.metrics.kibana_os_load[0].data.slice(0, 15)}
/>
</Chart>
</div>
<Chart>
<Settings tooltip={{ type: 'vertical' }} debug={false} legendPosition={Position.Right} showLegend={true} />
<Axis
id={getAxisId('timestamp')}
title="timestamp"
position={Position.Bottom}
tickFormat={niceTimeFormatter([1555819200000, 1555905600000])}
/>
<Axis id={getAxisId('count')} title="count" position={Position.Left} tickFormat={(d) => d.toFixed(2)} />
<BarSeries
id={getSpecId('dataset B')}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
data={KIBANA_METRICS.metrics.kibana_os_load[1].data.slice(0, 15)}
xAccessor={0}
yAccessors={[1]}
stackAccessors={[0]}
barSeriesStyle={{
rectBorder: {
strokeOpacity: 1,
strokeWidth: 4,
stroke: 'blue',
visible: true,
},
rect: {
opacity: 0.25,
fill: 'red',
},
}}
/>
<BarSeries
id={getSpecId('dataset C')}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
data={KIBANA_METRICS.metrics.kibana_os_load[1].data.slice(0, 15)}
xAccessor={0}
yAccessors={[1]}
stackAccessors={[0]}
/>
</Chart>
);
}
}
66 changes: 28 additions & 38 deletions src/chart_types/xy_chart/rendering/rendering.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { DEFAULT_GEOMETRY_STYLES } from '../../../utils/themes/theme_commons';
import { getSpecId } from '../../../utils/ids';
import {
BarGeometry,
Expand All @@ -8,7 +7,7 @@ import {
getStyleOverrides,
GeometryId,
} from './rendering';
import { BarSeriesStyle } from '../../../utils/themes/theme';
import { BarSeriesStyle, SharedGeometryStyle } from '../../../utils/themes/theme';
import { DataSeriesDatum } from '../utils/series';
import { RecursivePartial, mergePartial } from '../../../utils/commons';

Expand Down Expand Up @@ -116,67 +115,58 @@ describe('Rendering utils', () => {
},
};

const sharedThemeStyle = DEFAULT_GEOMETRY_STYLES;
const specOpacity = 0.66;

const defaultStyle = getGeometryStyle(geometryId, null, sharedThemeStyle);
const sharedThemeStyle: SharedGeometryStyle = {
default: {
opacity: 1,
},
highlighted: {
opacity: 0.5,
},
unhighlighted: {
opacity: 0.25,
},
};

// no highlighted elements
expect(defaultStyle).toEqual({ opacity: 1 });

const customDefaultStyle = getGeometryStyle(geometryId, null, sharedThemeStyle, specOpacity);

// no highlighted elements with custom spec opacity
expect(customDefaultStyle).toEqual({ opacity: 0.66 });

const highlightedStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedThemeStyle);
const defaultStyle = getGeometryStyle(geometryId, null, sharedThemeStyle);
expect(defaultStyle).toBe(sharedThemeStyle.default);

// should equal highlighted opacity
expect(highlightedStyle).toEqual({ opacity: 1 });

const unhighlightedStyle = getGeometryStyle(geometryId, unhighlightedLegendItem, sharedThemeStyle);
const highlightedStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedThemeStyle);
expect(highlightedStyle).toBe(sharedThemeStyle.highlighted);

// should equal unhighlighted opacity
expect(unhighlightedStyle).toEqual({ opacity: 0.25 });

const customHighlightedStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedThemeStyle, specOpacity);
const unhighlightedStyle = getGeometryStyle(geometryId, unhighlightedLegendItem, sharedThemeStyle);
expect(unhighlightedStyle).toBe(sharedThemeStyle.unhighlighted);

// should equal custom spec highlighted opacity
expect(customHighlightedStyle).toEqual({ opacity: 0.66 });

const customUnhighlightedStyle = getGeometryStyle(
geometryId,
unhighlightedLegendItem,
sharedThemeStyle,
specOpacity,
);
const customHighlightedStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedThemeStyle);
expect(customHighlightedStyle).toBe(sharedThemeStyle.highlighted);

// unhighlighted elements remain unchanged with custom opacity
expect(customUnhighlightedStyle).toEqual({ opacity: 0.25 });
const customUnhighlightedStyle = getGeometryStyle(geometryId, unhighlightedLegendItem, sharedThemeStyle);
expect(customUnhighlightedStyle).toBe(sharedThemeStyle.unhighlighted);

// has individual highlight
const hasIndividualHighlight = getGeometryStyle(geometryId, null, sharedThemeStyle, undefined, {
const hasIndividualHighlight = getGeometryStyle(geometryId, null, sharedThemeStyle, {
hasHighlight: true,
hasGeometryHover: true,
});

expect(hasIndividualHighlight).toEqual({ opacity: 1 });
expect(hasIndividualHighlight).toBe(sharedThemeStyle.highlighted);

// no highlight
const noHighlight = getGeometryStyle(geometryId, null, sharedThemeStyle, undefined, {
const noHighlight = getGeometryStyle(geometryId, null, sharedThemeStyle, {
hasHighlight: false,
hasGeometryHover: true,
});

expect(noHighlight).toEqual({ opacity: 0.25 });
expect(noHighlight).toBe(sharedThemeStyle.unhighlighted);

// no geometry hover
const noHover = getGeometryStyle(geometryId, null, sharedThemeStyle, undefined, {
const noHover = getGeometryStyle(geometryId, null, sharedThemeStyle, {
hasHighlight: true,
hasGeometryHover: false,
});

expect(noHover).toEqual({ opacity: 1 });
expect(noHover).toBe(sharedThemeStyle.highlighted);
});

describe('getStyleOverrides', () => {
Expand Down
25 changes: 11 additions & 14 deletions src/chart_types/xy_chart/rendering/rendering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export interface GeometryValue {

/** Shared style properties for varies geometries */
export interface GeometryStyle {
/**
* Opacity multiplier
*
* if set to `0.5` all given opacities will be halfed
*/
opacity: number;
}

Expand Down Expand Up @@ -482,34 +487,26 @@ export function renderArea(
export function getGeometryStyle(
geometryId: GeometryId,
highlightedLegendItem: LegendItem | null,
sharedThemeStyle: SharedGeometryStyle,
specOpacity?: number,
sharedGeometryStyle: SharedGeometryStyle,
individualHighlight?: { [key: string]: boolean },
): GeometryStyle {
const sharedStyle =
specOpacity == null
? sharedThemeStyle
: {
...sharedThemeStyle,
highlighted: { opacity: specOpacity },
default: { opacity: specOpacity },
};
const { default: defaultStyles, highlighted, unhighlighted } = sharedGeometryStyle;

if (highlightedLegendItem != null) {
const isPartOfHighlightedSeries = belongsToDataSeries(geometryId, highlightedLegendItem.value);

return isPartOfHighlightedSeries ? sharedStyle.highlighted : sharedStyle.unhighlighted;
return isPartOfHighlightedSeries ? highlighted : unhighlighted;
}

if (individualHighlight) {
const { hasHighlight, hasGeometryHover } = individualHighlight;
if (!hasGeometryHover) {
return sharedStyle.highlighted;
return highlighted;
}
return hasHighlight ? sharedStyle.highlighted : sharedStyle.unhighlighted;
return hasHighlight ? highlighted : unhighlighted;
}

return sharedStyle.default;
return defaultStyles;
}

export function isPointOnGeometry(
Expand Down
13 changes: 3 additions & 10 deletions src/components/react_canvas/area_geometries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,7 @@ export class AreaGeometries extends React.PureComponent<AreaGeometriesDataProps,
acc.push(...this.renderAreaLines(glyph, i, sharedStyle, highlightedLegendItem));
}
if (seriesPointStyle.visible) {
const customOpacity = seriesPointStyle ? seriesPointStyle.opacity : undefined;
const geometryStyle = getGeometryStyle(
geometryId,
this.props.highlightedLegendItem,
sharedStyle,
customOpacity,
);
const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle);
const pointStyleProps = buildPointStyleProps(glyph.color, seriesPointStyle, geometryStyle);
acc.push(...this.renderPoints(glyph.points, i, pointStyleProps, glyph.geometryId));
}
Expand All @@ -76,8 +70,7 @@ export class AreaGeometries extends React.PureComponent<AreaGeometriesDataProps,
highlightedLegendItem: LegendItem | null,
): JSX.Element => {
const { area, color, transform, geometryId, seriesAreaStyle } = glyph;
const customOpacity = seriesAreaStyle ? seriesAreaStyle.opacity : undefined;
const geometryStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedStyle, customOpacity);
const geometryStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedStyle);
const key = getGeometryIdKey(geometryId, 'area-');
const areaProps = buildAreaRenderProps(transform.x, area, color, seriesAreaStyle, geometryStyle);
return <Path {...areaProps} key={key} />;
Expand All @@ -89,7 +82,7 @@ export class AreaGeometries extends React.PureComponent<AreaGeometriesDataProps,
highlightedLegendItem: LegendItem | null,
): JSX.Element[] => {
const { lines, color, geometryId, transform, seriesAreaLineStyle } = glyph;
const geometryStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedStyle, seriesAreaLineStyle.opacity);
const geometryStyle = getGeometryStyle(geometryId, highlightedLegendItem, sharedStyle);

return lines.map((linePath, lineIndex) => {
const key = getGeometryIdKey(geometryId, `area-line-${areaIndex}-${lineIndex}`);
Expand Down
34 changes: 29 additions & 5 deletions src/components/react_canvas/bar_geometries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { animated, Spring } from 'react-spring/renderprops-konva.cjs';
import { LegendItem } from '../../chart_types/xy_chart/legend/legend';
import { BarGeometry, getGeometryStyle } from '../../chart_types/xy_chart/rendering/rendering';
import { SharedGeometryStyle } from '../../utils/themes/theme';
import { buildBarRenderProps } from './utils/rendering_props_utils';
import { buildBarRenderProps, buildBarBorderRenderProps } from './utils/rendering_props_utils';

interface BarGeometriesDataProps {
animated?: boolean;
Expand Down Expand Up @@ -55,7 +55,6 @@ export class BarGeometries extends React.PureComponent<BarGeometriesDataProps, B
bar.geometryId,
this.props.highlightedLegendItem,
sharedStyle,
seriesStyle.rect.opacity,
individualHighlight,
);
const key = `bar-${index}`;
Expand All @@ -65,6 +64,15 @@ export class BarGeometries extends React.PureComponent<BarGeometriesDataProps, B
<Group key={index}>
<Spring native from={{ y: y + height, height: 0 }} to={{ y, height }}>
{(props: { y: number; height: number }) => {
const barPropsBorder = buildBarBorderRenderProps(
x,
props.y,
width,
props.height,
seriesStyle.rect,
seriesStyle.rectBorder,
geometryStyle,
);
const barProps = buildBarRenderProps(
x,
props.y,
Expand All @@ -76,12 +84,26 @@ export class BarGeometries extends React.PureComponent<BarGeometriesDataProps, B
geometryStyle,
);

return <animated.Rect {...barProps} key={key} />;
return (
<React.Fragment key={key}>
<animated.Rect {...barProps} />
{barPropsBorder && <animated.Rect {...barPropsBorder} />}
</React.Fragment>
);
}}
</Spring>
</Group>
);
} else {
const barPropsBorder = buildBarBorderRenderProps(
x,
y,
width,
height,
seriesStyle.rect,
seriesStyle.rectBorder,
geometryStyle,
);
const barProps = buildBarRenderProps(
x,
y,
Expand All @@ -92,9 +114,11 @@ export class BarGeometries extends React.PureComponent<BarGeometriesDataProps, B
seriesStyle.rectBorder,
geometryStyle,
);

return (
<React.Fragment key={index}>
<Rect {...barProps} key={key} />
<React.Fragment key={key}>
<Rect {...barProps} />
{barPropsBorder && <Rect {...barPropsBorder} />}
</React.Fragment>
);
}
Expand Down
6 changes: 2 additions & 4 deletions src/components/react_canvas/line_geometries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,14 @@ export class LineGeometries extends React.PureComponent<LineGeometriesDataProps,

getLineToRender(glyph: LineGeometry, sharedStyle: SharedGeometryStyle, key: string) {
const { line, color, transform, geometryId, seriesLineStyle } = glyph;
const customOpacity = seriesLineStyle ? seriesLineStyle.opacity : undefined;
const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle, customOpacity);
const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle);
const lineProps = buildLineRenderProps(transform.x, line, color, seriesLineStyle, geometryStyle);
return <Path {...lineProps} key={key} />;
}

getPointToRender(glyph: LineGeometry, sharedStyle: SharedGeometryStyle, key: string) {
const { points, color, geometryId, seriesPointStyle } = glyph;
const customOpacity = seriesPointStyle ? seriesPointStyle.opacity : undefined;
const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle, customOpacity);
const geometryStyle = getGeometryStyle(geometryId, this.props.highlightedLegendItem, sharedStyle);
const pointStyleProps = buildPointStyleProps(color, seriesPointStyle, geometryStyle);
return this.renderPoints(points, key, pointStyleProps);
}
Expand Down
Loading

0 comments on commit d8c8459

Please sign in to comment.