react-vis
react-vis copied to clipboard
color quirks
color sometimes works in strange and mysterious ways.
Categorical or linear scales don't work at the series level, except for lineSeries. ie
<XYPlot height={200} width={200} colorType='category' colorDomain={[0, 1, 2]} colorRange={['yellow', 'blue', 'red']}>
<VerticalBarSeries data={data} color={0} />
</XYPlot>
or
<XYPlot height={200} width={200} colorType='category' colorDomain={[0, 1]} colorRange={[ 'blue', 'red']}>
<VerticalBarSeries data={data} color={0.5} />
</XYPlot>
In both cases, the series will appear black. A scale will be created which will return an undefined value and override the defaults.
still happens https://codepen.io/jckr/pen/yKOpma
BarSeries
and MarkSeries
appear black in documentation.
I think I'm experiencing the same issue here: https://codepen.io/anon/pen/PaxBaP?editors=0010
I'm having the same issue with a VerticalBarSeries
(NB: <FlexibleXYPlot>
can be swapped out for XYPlot
)
<FlexibleXYPlot height={350} xType="ordinal">
<VerticalBarSeries animation cluster="things" data={data} />
</FlexibleXYPlot>
- I'm setting a
color
property on eachdata
list item to"green"
(akargb(0, 128, 0)
) -
<VerticalBarSeries>
imports<BarSeries>
which maps over every item in thedata
list - It applies these properties in a style object:
style: { opacity: opacityFunctor && opacityFunctor(d), stroke: strokeFunctor && strokeFunctor(d), fill: fillFunctor && fillFunctor(d), ...style },
- The
fill
andstroke
properties come from
andthis._getAttributeFunctor('fill') || this._getAttributeFunctor('color');
respectively.this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color');
-
this._getAttributeFunctor
is defined in<AbstractSeries>
which<BarSeries>
extends. - It returns
getAttributeFunctor(this.props, attr)
, wheregetAttributeFunctor
is defined inutils/scales-utils
- The
props
are plentiful, and the important bits look like this:{ ... colorDomain: (2) ["green", "green"], colorRange: (2) ["#EF5D28", "#FF9833"], data: [...], ... }
- The
getAttributeFunctor
function fromutils/scales-utils
looks like this:export function getAttributeFunctor(props, attr) { const scaleObject = getScaleObjectFromProps(props, attr); if (scaleObject) { const scaleFn = getScaleFnFromScaleObject(scaleObject); return d => scaleFn(_getAttrValue(d, scaleObject.accessor)); } return null; }
-
getScaleObjectFromProps(props, attr)
calls_collectScaleObjectFromProps
with the same args. - This creates a
scaleObject
for use with the underlyingd3
libs from the props:
In my case the important ones areconst { [attr]: value, [`_${attr}Value`]: fallbackValue, [`${attr}Range`]: range, // !! [`${attr}Distance`]: distance = 0, [`${attr}BaseValue`]: baseValue, [`${attr}Type`]: type = LINEAR_SCALE_TYPE, [`${attr}NoFallBack`]: noFallBack, [`get${toTitleCase(attr)}`]: accessor = d => d[attr], [`get${toTitleCase(attr)}0`]: accessor0 = d => d[`${attr}0`] } = props; let {[`${attr}Domain`]: domain} = props; // !!
colorRange
andcolorDomain
- I end up with an
scaleObject
that looks like the following:{ accessor: ƒ (d), accessor0: ƒ (d), attr: "color", baseValue: undefined, distance: 0, domain: (2) ["rgb(0, 128, 0)", "rgb(0, 128, 0)"], isValue: false, range: (2) ["rgb(239, 93, 40)", "rgb(255, 152, 51)"], type: "linear" }
- Back in the
getAttributeFunctor
function, thescaleObject
is returned - Now
getAttributeFunctor
callsgetScaleFnFromScaleObject
in
The returned function is going to be called with theif (scaleObject) { const scaleFn = getScaleFnFromScaleObject(scaleObject); return d => scaleFn(_getAttrValue(d, attr)); }
data
list (d
) that is passed in as a prop to theVerticalBarSeries
as<VerticalBarSeries ... data={data} />
(rememberopacity: opacityFunctor && opacityFunctor(d)
from<BarSeries>
) - The
_getAttrValue(d, scaleObject.accessor)
returns the expected"green"
value in RGB form:rgb(0, 128, 0)
But when this is passed to thescaleFn
(which comes fromd3
), it always returnsrgb(0, 0, 0)
, and it is this value that gets applied to thestroke
andfill
properties in thedata.map
withinBarSeries
(explaining the black bars).{ opacity: 1, stroke: "rgb(0, 0, 0)", fill: "rgb(0, 0, 0)" }
What I can't figure out is why the scaleFn
always returns rgb(0, 0, 0)
(black).
It might be a problem with this function in utils/scales-utils
:
export function getScaleFnFromScaleObject(scaleObject) {
...
const { type, domain, range } = scaleObject;
/*
My data is:
{
attr: "color",
baseValue: undefined,
...
domain: (2) ["rgb(0, 128, 0)", "rgb(0, 128, 0)"],
...
range: (2) ["rgb(239, 93, 40)", "rgb(255, 152, 51)"],
type: "linear"
}
*/
const modDomain =
( domain[0] === domain[1] ) ? (
( domain[0] === 0 ) ? [-1, 0] : [-domain[0], domain[0]]
) : domain;
/*
My modDomain is:
[NaN, "rgb(0, 128, 0)"]
My range (from above) is:
["rgb(239, 93, 40)", "rgb(255, 152, 51)"]
*/
...
const scale = SCALE_FUNCTIONS[type]()
.domain(modDomain)
.range(range);
...
return scale;
}
That NaN
in [NaN, "rgb(0, 128, 0)"]
looks pretty suspect, caused by trying to get a negative value of a string in [-domain[0], domain[0]]
(aka [-"rgb(0, 128, 0)", "rgb(0, 128, 0)"]
)
Is there any progress on this issue? I'm using a MarkSerie
and everytime I try to set a color string (such as rgb or hex) it appears black no matter what.
Same here with LineMarkSeries!
I'm not sure if this helps. but i used exact same code as @jckr except
- moved colorType='category' colorDomain={[0, 1, 2]} colorRange={['yellow', 'blue', 'red']} props from XYPlot to VerticalBarSeries props
- in data, add color:0~2
const data = [
{x: 0, y: 8, color:0},
{x: 1, y: 5, color:0},
{x: 2, y: 4, color:1},
{x: 3, y: 9, color:1},
{x: 4, y: 1, color:2},
{x: 5, y: 7, color:2},
{x: 6, y: 6, color:1},
{x: 7, y: 3, color:1},
{x: 8, y: 2, color:0},
{x: 9, y: 0, color:1}
];
<div>
<XYPlot height={300} width={300}>
<VerticalBarSeries colorType='category' colorDomain={[0, 1, 2]} colorRange={['yellow', 'blue', 'red']} data={data} />
</XYPlot>
</div>