如何从包含线性渐变定义的字符串中提取色标值?

How does one extract the color-stop values from a string which contains the definition of a linear gradient?

我有一个包含线性渐变定义的字符串...

linear-gradient(90deg, rgba(243,231,231,1), rgba(12,0,0,0.48))

...我需要将两个色标值分别分配给一个变量,就像下一个伪代码所示...

const color1 = "rgba(243,231,231,1)";
const color2 = "rgba(12,0,0,0.48)";

只提取数字也可以,我只需要一些可以使用的东西。

问题是,我不能只计算字符和子字符串,因为我的线性渐变字符串值是动态的并且长度可能会改变,有什么想法吗?

您可以使用 String#match.

let str = "linear-gradient(90deg, rgba(243,231,231,1), rgba(12,0,0,0.48))";
let res = str.match(/rgba\(.*?\)/g);
console.log(res);

正则表达式解释:

如果您需要同时支持 rgbrgba,请使用:

str.match(/rgba?\(.*?\)/g);

我会使用 regexp

https://regex101.com/r/MCdePv/1/

const str = "linear-gradient(90deg, rgba(243,231,231,1), rgba(12,0,0,0.48))";
const re = /(rgba?\(.*?\))/g; // find either rgb or rgba 
const [color1, color2] = str.match(re);
console.log(color1, color2);

到目前为止所有的方法确实涵盖了仅提取两个并且仅基于 rgb/rgba color-stop 一个渐变值。但是关于如何表达线性 css 梯度还有更多有效的变体......例如...

-webkit-linear-gradient(top , rgb(243, 231, 231), rgba(12, 0, 0, 0.48));
-moz-linear-gradient(90deg , hsl(0, 80%, 70%), #bada55)  
-o-linear-gradient(bottom, hsl(0, 80%, 70%), #bada55) ;  
linear-gradient(135deg , red, red 60%, blue) ;

linear-gradient(yellow, blue 20%, #0f0);

linear-gradient( to left top, blue, green 40%, red ); 
linear-gradient(to top left  , red, orange, yellow, green, blue, indigo, violet) ; 
linear-gradient(to bottom right , red, rgba(255, 0, 0, 0)); 
linear-gradient(right top to left bottom, red, rgb(255, 0, 0));

因此,通用方法的基础必须识别任何梯度定义的 color-stop 的开始。

这意味着 trimming a gradient string-value one has to strip off all the unnecessary information except for the clean list of color-stop values. A (kind of validating) regular expression which features a(n optional) positive lookbehind in order to reflect the css-specification of linear gradients utilized by split 确实实现了从左侧剥离...

/^(?:-\w+-)*linear-gradient\((?:.*?(?<=top|bottom|left|right|deg)\s*,\s*)*/

... 而 replace 将使用一个相当简单的正则表达式 ... /\s*\)\s*;*$/ ... 来从右边去除不必要的字符 ...

console.log([
  '  -webkit-linear-gradient(top , rgb(243, 231, 231), rgba(12, 0, 0, 0.48));',
  '-moz-linear-gradient(90deg , hsl(0, 80%, 70%), #bada55)  ',
  ' -o-linear-gradient(bottom, hsl(0, 80%, 70%), #bada55) ;  ',
  'linear-gradient(135deg , red, red 60%, blue) ;',
  '  linear-gradient(yellow, blue 20%, #0f0);',
  ' linear-gradient( to left top, blue, green 40%, red ); ',
  '   linear-gradient(to top left  , red, orange, yellow, green, blue, indigo, violet) ; ',
  ' linear-gradient(to bottom right , red, rgba(255, 0, 0, 0)); ',
  'linear-gradient(right top to left bottom, red, rgb(255, 0, 0));',
].map(str => str
  .trim()
  .split(/^(?:-\w+-)*linear-gradient\((?:.*?(?<=top|bottom|left|right|deg)\s*,\s*)*/)[1]
  .replace((/\s*\)\s*;*$/), '')
));
.as-console-wrapper { min-height: 100%!important; top: 0; }

以上示例的日志记录为每个线性渐变定义显示了一个干净的 color-stop only 字符串。

更复杂的部分是如何正确地 distinguish/separate 颜色停止值的每个可能变体。该规范再次提供帮助。值的拆分可以发生在每个逗号字符后跟一个非数字字符。

像这样的正则表达式.../,(?=\D)/ ... which features a positive lookahead 已经完成了这项工作。

但是为了完成这项任务,首先需要去除每个逗号字符周围的所有前导和尾随白色space。最后,可以通过在每个出现的逗号字符之前再次插入一个 space 来 美化 每个颜色停止值。

最终的通用方法,它从线性 css 梯度定义中提取所有色标值的列表,可能看起来类似于以下实现 ...

function getLinearGradientColorStopValues(str) {
  return str
    .trim()
    // strip off left side.
    .split(/^(?:-\w+-)*linear-gradient\((?:.*?(?<=top|bottom|left|right|deg)\s*,\s*)*/)[1]
    // strip off right side.
    .replace((/\s*\)\s*;*$/), '')
    // strip any whitespace (sequence) before and after each comma.
    .replace((/\s*,\s*/g), ',')
    // split color-stop values at each comma which is followed by a non digit character.
    .split(/,(?=\D)/)
    // prettify/normalize each color stop value for better readability.
    .map(item => item.replace((/,/g), ', '));
}

console.log([

  '  -webkit-linear-gradient(top , rgb(243, 231, 231), rgba(12, 0, 0, 0.48));',
  '-moz-linear-gradient(90deg , hsl(0, 80%, 70%), #bada55)  ',
  ' -o-linear-gradient(bottom, hsl(0, 80%, 70%), #bada55) ;  ',
  'linear-gradient(135deg , red, red 60%, blue) ;',
  '  linear-gradient(yellow, blue 20%, #0f0);',
  ' linear-gradient( to left top, blue, green 40%, red ); ',
  '   linear-gradient(to top left  , red, orange, yellow, green, blue, indigo, violet) ; ',
  ' linear-gradient(to bottom right , red, rgba(255, 0, 0, 0)); ',
  'linear-gradient(right top to left bottom, red, rgb(255, 0, 0));',

].map(getLinearGradientColorStopValues));
.as-console-wrapper { min-height: 100%!important; top: 0; }

OP 的 example/task 然后就这样解决了 ...

function getLinearGradientColorStopValues(str) {
  return str
    .trim()
    // strip off left side.
    .split(/^(?:-\w+-)*linear-gradient\((?:.*?(?<=top|bottom|left|right|deg)\s*,\s*)*/)[1]
    // strip off right side.
    .replace((/\s*\)\s*;*$/), '')
    // strip any whitespace (sequence) before and after each comma.
    .replace((/\s*,\s*/g), ',')
    // split color-stop values at each comma which is followed by a non digit character.
    .split(/,(?=\D)/)
    // prettify/normalize each color stop value for better readability.
    .map(item => item.replace((/,/g), ', '));
}

const [

  colorstop1,
  colorstop2,

] = getLinearGradientColorStopValues(

  "linear-gradient(90deg, rgba(243,231,231,1), rgba(12,0,0,0.48))"
);

console.log({ colorstop1, colorstop2 });
.as-console-wrapper { min-height: 100%!important; top: 0; }