DEV Community

Lionel Marco
Lionel Marco

Posted on

3 2

React Color Scale Interpolation

Sometimes we have to work with colors, either for labels, icons, bar charts or for a choropleth maps.

It will show a way in which given two colors, the color between them will be calculated, or a scale with many colors will be generated.

For UI components will be use MATERIAL-UI library.

Image

Table of Contents

1) Color interpolation

Here the more important function is which, given two colors, them
calculate a resulting color. It will be more similar to one or the other,
acording to a conversion ratio, that will vary between 0 and 1.




  const interpolate = (start,end,ratio)=>{    
    const r = Math.trunc(ratio*end[0] + (1-ratio)*start[0])
    const g = Math.trunc(ratio*end[1] + (1-ratio)*start[1])
    const b = Math.trunc(ratio*end[2] + (1-ratio)*start[2])    
    return [r,g,b]
  }



Enter fullscreen mode Exit fullscreen mode

For example, interpolating in the middle of Black and White:

interpolate([0,0,0],[255,255,255],0.5)

We get Gray:

[127,127,127]

2) Color Selector

The MATERIAL-UI library have a Select control, in this case we use to display a list of color and choice one of them.

There are two color selector, one for start and other for end.



          <Select  value={color1}                                    
                   name={'color1'}
                   onChange={ this.onColorChange}
                   renderValue={ showIcon } 
                 >                    
                     { colors.map( e =>  <MenuItem  key={e} value={e}><ColorIcon color={e} /></MenuItem> )}    
          </Select> 


Enter fullscreen mode Exit fullscreen mode

The colors come from a previously declared array:



const colors = ['#800080','#FF0000','#FFD700','#00FF00','#006400','#0000FF']; //purple,red,gold,darkgreen,blue


Enter fullscreen mode Exit fullscreen mode

Color Icon

On every choice of our select an icon with their respective color is displayed.
The icon receive a props 'color', basically it is a rectangule filled with the given color.



function ColorIcon({color}) {
 return (
 <SvgIcon   viewBox="0 0 50 20" style={{ width: 50, height:20  }}>     
   <rect   fill={color} x={0} y='0' width={50} height={20}  ></rect>            
 </SvgIcon>
);
};


Enter fullscreen mode Exit fullscreen mode

Show Icon

The select have a 'renderValue' props, wich give the flexibility of show another thing that only text.


 showIcon } 

Enter fullscreen mode Exit fullscreen mode


function showIcon(value) {
  return (  <ColorIcon color={value}/> );
}



Enter fullscreen mode Exit fullscreen mode

3) Ratio Slider

The MATERIAL-UI library have a Slider control, the slider will control the ratio of color mix. Varying from 0 to 100, then will be re maped to 0-1.



          <Slider
            value={ratio}          
            onChange={ this.onRatioChange}
            step={10} min={0} max={100}
            valueLabelDisplay="auto"
            marks={[ {value: 0,label: '0%'}, {value: 50,label: '50%'}, {value: 100,label: '100%'}]}                                   
          />



Enter fullscreen mode Exit fullscreen mode

Every change on the slider will update the state and trigger a new render:



  onRatioChange = (event, newValue) => {    
    this.setState(prevState => ({...prevState,ratio: newValue})); 
  };


Enter fullscreen mode Exit fullscreen mode

4) Full code

Next the full code is showed, imports are ommited just for shortness.



function ColorIcon({color}) {
 return (
 <SvgIcon   viewBox="0 0 50 20" style={{ width: 50, height:20  }}>     
   <rect   fill={color} x={0} y='0' width={50} height={20}  ></rect>            
 </SvgIcon>
);
};


function showIcon(value) {
  return (  <ColorIcon color={value}/> );
}

const colors = ['#800080','#FF0000','#FFD700','#00FF00','#006400','#0000FF']; //purple,red,gold,darkgreen,blue

export default class ColorInterpolation extends React.Component {

  constructor(props) {
    super(props);
    this.state = {color1:colors[2],color2:colors[1],ratio:50}      
  };   

  onColorChange = (e) => {    
    const {name,value} = e.target;
    this.setState(prevState => ({...prevState,[name]: value}));  
  };

  onRatioChange = (event, newValue) => {    
    this.setState(prevState => ({...prevState,ratio: newValue})); 
  };  

  render() {   
    //console.log("Render");     
    const {color1,color2,ratio} = this.state;    
    const interpolatedColor=getColor(color1,color2,ratio);

    return (    

      <div style={{  
        maxWidth:'500px',         
        display: "flex" ,                    
        flexDirection: "column", 
        margin:'10px',padding:'20px',
        border: '2px solid grey',
        borderRadius:'4px'
        }}>

        <div style={{               
           display: "flex" ,                    
           flexDirection: "row",      
           alignItems: "center",
           justifyContent: "space-around" }}>

          <Typography> Source: </Typography> 
          <Select  value={color1}                                    
                   name={'color1'}
                   onChange={ this.onColorChange}
                   renderValue={ showIcon } >                    
                     { colors.map( e =>  <MenuItem  key={e} value={e}><ColorIcon color={e} /></MenuItem> )}    
          </Select>    
          <Typography> Target: </Typography>       
          <Select   value={color2}                                    
                    name={'color2'}
                    onChange={ this.onColorChange}
                    renderValue={ showIcon } >                    
                     { colors.map( e =>  <MenuItem  key={e} value={e}><ColorIcon color={e} /></MenuItem> )}
          </Select>    
          </div>  
          <Slider
            value={ratio}          
            onChange={ this.onRatioChange}
            step={10} min={0} max={100}
            valueLabelDisplay="auto"
            marks={[ {value: 0,label: '0%'}, {value: 50,label: '50%'}, {value: 100,label: '100%'}]}
          />
          <div style={{marginTop:'5px', display: "flex" , alignItems: "center", justifyContent: "center" }}>   
            <Typography > Interpolated:</Typography>        
            <Typography > {ratio}% </Typography>        
            <ColorIcon color={interpolatedColor} />
            <Typography >{interpolatedColor}</Typography>
          </div>    
          <div style={{marginTop:'5px', display: "flex" , alignItems: "center", justifyContent: "center" }}>   
            <Typography > Scale:</Typography>        
            { [0,10,20,40,60,80,100].map( (v,i)=> {
              let c =getColor(this.state.color1,this.state.color2,v)
            return <ColorIcon key={i} color={c} />})}
          </div> 
        </div>
    );
}
}

function getColor(c1,c2,ratio)
{

  const interpolate = (start,end,ratio)=>{    
    const r = Math.trunc(ratio*end[0] + (1-ratio)*start[0])
    const g = Math.trunc(ratio*end[1] + (1-ratio)*start[1])
    const b = Math.trunc(ratio*end[2] + (1-ratio)*start[2])    
    return [r,g,b]
  }

  const hexToRgb = (hex) =>   
     [ 
       parseInt(hex.substr(1,2),16),
       parseInt(hex.substr(3,2),16),
       parseInt(hex.substr(5,2),16),
     ];  

  const rgbToHex = (rgb) => '#' + rgb.map(x => {
    const hex = x.toString(16)
    return hex.length === 1 ? '0' + hex : hex
  }).join('');

  const rgbInterpolated = interpolate(hexToRgb(c1),hexToRgb(c2),ratio/100);
  return rgbToHex(rgbInterpolated);
}



Enter fullscreen mode Exit fullscreen mode

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

nextjs tutorial video

Youtube Tutorial Series πŸ“Ί

So you built a Next.js app, but you need a clear view of the entire operation flow to be able to identify performance bottlenecks before you launch. But how do you get started? Get the essentials on tracing for Next.js from @nikolovlazar in this video series πŸ‘€

Watch the Youtube series

πŸ‘‹ Kindness is contagious

Please leave a ❀️ or a friendly comment on this post if you found it helpful!

Okay