【问题标题】:Interpolate between two colours based on a percentage value根据百分比值在两种颜色之间进行插值
【发布时间】:2021-02-09 16:27:44
【问题描述】:

我目前正在用 JS 开发一个动画库,需要根据 0 - 1 之间的进度值计算两个值之间的颜色。

例如,函数可能如下所示。这会以 HEX 格式输入两种颜色。

  • colourA = 初始颜色
  • colourB = 最终颜色
  • 进度 = 0.5 (50%)

const interpolateColour = (colourA, colourB, progress) => { return polColour } 

目标是返回颜色 0.5 进度或两种颜色之间的 50%。我知道 HEX 颜色很可能需要转换为 RGBA 才能使 HSV 实现此效果,但不确定哪种方法最好。

编辑: 我想通了……


const is = {
    hex: a => /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(a),
    rgb: a => /^rgb/.test(a),
    hsl: a => /^hsl/.test(a),
    col: a => (is.hex(a) || is.rgb(a) || is.hsl(a)),
}

const convertToRgba = (colour) => {
    return is.hex(colour) ? hexToRgba(colour)
        : is.rgb(colour) ? rbgToRgba(colour)
            : is.hsl(colour) ? hslToRgba(colour)
                : colour
}

const hexToRgba = (colour, alpha = 1) => {
    const [r, g, b] = colour.match(/\w\w/g).map(x => parseInt(x, 16))
    return `rgba(${r},${g},${b},${alpha})`
};

const rbgToRgba = (colour, alpha = 1) => {
    const [r, g, b] = colour.replace(/[^\d,]/g, '').split(',')
    return `rgba(${r},${g},${b},${alpha})`
}

const deconstructRgba = (rgba) => {
    return rgba.replace(/[^\d,]/g, '').split(',').map(x => parseInt(x))
}

const formatRbga = (colour) => {
    return `rgba(${colour.r},${colour.g},${colour.b},${colour.a})`
}

const interpolateColour = (colourA, colourB, progress) => {
    const [r1, g1, b1, a1] = deconstructRgba(convertToRgba(colourA))
    const [r2, g2, b2, a2] = deconstructRgba(convertToRgba(colourB))
    return formatRbga({
        r: Math.round((r1 + r2) * progress),
        g: Math.round((g1 + g2) * progress),
        b: Math.round((b1 + b2) * progress),
        a: Math.round((a1 + a2) * progress)
    })
}

export {
    interpolateColour,
    convertToRgba,
    hexToRgba,
    rbgToRgba,
    deconstructRgba
}

【问题讨论】:

    标签: javascript reactjs animation colors


    【解决方案1】:

    R、G 和 B 值的简单插值就足够了。

    // extract numeric r, g, b values from `rgb(nn, nn, nn)` string
    function getRgb(color) {
      let [r, g, b] = color.replace('rgb(', '')
        .replace(')', '')
        .split(',')
        .map(str => Number(str));;
      return {
        r,
        g,
        b
      }
    }
    
    function colorInterpolate(colorA, colorB, intval) {
      const rgbA = getRgb(colorA),
        rgbB = getRgb(colorB);
      const colorVal = (prop) =>
        Math.round(rgbA[prop] * (1 - intval) + rgbB[prop] * intval);
      return {
        r: colorVal('r'),
        g: colorVal('g'),
        b: colorVal('b'),
      }
    }
    
    
    function doit(progression) {
      const div1 = document.getElementById('color1');
      const color1 = div1.style.backgroundColor;
      const div2 = document.getElementById('color2');
      const color2 = div2.style.backgroundColor;
    
      const rgbNew = colorInterpolate(
        color1,
        color2, progression
      );
    
      const divResult = document.getElementById('result');
      divResult.style.backgroundColor =
        `rgb( ${rgbNew.r}, ${rgbNew.g}, ${rgbNew.b})`;
    }
    
    document.querySelector('button').onclick = () => {
      const intval = Number(document.querySelector('input').value);
      doit(intval);
    };
    #color1,
    #color2,
    #result {
      width: 200px;
      height: 40px;
      margin: 12px;
      padding: 0.2rem 0.5rem;
    }
    <h4>Interpolate Between Two Colors</h4>
    <label for="number">Interpolate by:</label>
    <input id="number" type="number" value="0.5" min="0" max="1" step="0.05" />
    <button>Interpolate</button>
    
    <div style="display: flex">
      <div id="color1" style="background-color: #922B21">color1</div>
      <div id="result" style="background-color: #e0e0e0">interpolated</div>
      <div id="color2" style="background-color: #85C1E9">color2</div>
    </div>

    【讨论】:

      【解决方案2】:

      使用十六进制颜色:

      function convert(col1, col2, p) {
        const rgb1 = parseInt(col1, 16);
        const rgb2 = parseInt(col2, 16);
      
        const [r1, g1, b1] = toArray(rgb1);
        const [r2, g2, b2] = toArray(rgb2);
      
        const q = 1-p;
        const rr = Math.round(r1 * p + r2 * q);
        const rg = Math.round(g1 * p + g2 * q);
        const rb = Math.round(b1 * p + b2 * q);
      
        return  Number((rr << 16) + (rg << 8) + rb).toString(16);
      }
      
      function toArray(rgb) {
        const r = rgb >> 16;
        const g = (rgb >> 8) % 256;
        const b = rgb % 256;
      
        return [r, g, b];
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-23
        • 2016-08-03
        • 1970-01-01
        • 2016-11-23
        • 2020-10-18
        • 1970-01-01
        相关资源
        最近更新 更多