在JavaScript中更轻松地处理十六进制字符串和十六进制值

31

我有一些代码,它接受表示十六进制数字的字符串 - 实际上是十六进制颜色 - 并将它们相加。例如,将aaaaaa010101相加会产生输出ababab
但是,我的方法似乎过于冗长和复杂:

var hexValue = "aaaaaa";
hexValue = "0x" + hexValue;
hexValue = parseInt(hexValue, 16);
hexValue = hexValue + 0x010101;
hexValue = hexValue.toString(16);
document.write(hexValue); // outputs 'ababab'

在连接“0x”后,十六进制值仍然是一个字符串,所以我必须将其更改为数字,然后进行添加,然后我必须将其再次更改为十六进制格式!如果我要添加的数字本身就是十六进制字符串,或者考虑到在所有此操作开始之前我要从十六进制颜色中删除#,那么步骤甚至更多。

肯定有更简单的方法来进行简单的十六进制计算!并且只是为了明确,我不仅指将所有内容放在一行上,例如(parseInt(" 0x "+" aaaaaa ", 16)+ 0x010101) .toString(16)或使用速记符号-我的意思是实际上执行较少的操作。

是否有某种方法可以使JavaScript停止使用十进制进行其所有数学运算,并改为使用十六进制?还是有其他方法可以更轻松地使用JS与十六进制配合工作?


1
不行。JS 并没有使用十六进制进行所有的数学运算,而是使用二进制。在这里你说的其实是字符串。 - user123444555621
4个回答

42
不,没有办法告诉JavaScript语言默认使用十六进制整数格式而不是十进制。你的代码已经非常简洁了,但请注意,当您使用带有基数的“parseInt”时,您不需要在前面添加“0x”基数指示符。
以下是我处理你的问题的方法:
function addHexColor(c1, c2) {
  var hexStr = (parseInt(c1, 16) + parseInt(c2, 16)).toString(16);
  while (hexStr.length < 6) { hexStr = '0' + hexStr; } // Zero pad.
  return hexStr;
}

addHexColor('aaaaaa', '010101'); // => 'ababab'
addHexColor('010101', '010101'); // => '020202'

正如评论者所提到的,上述解决方案存在很多问题,因此下面是一个函数,它进行适当的输入验证并分别添加颜色通道,同时检查溢出。

function addHexColor2(c1, c2) {
  const octetsRegex = /^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i
  const m1 = c1.match(octetsRegex)
  const m2 = c2.match(octetsRegex)
  if (!m1 || !m2) {
    throw new Error(`invalid hex color triplet(s): ${c1} / ${c2}`)
  }
  return [1, 2, 3].map(i => {
    const sum = parseInt(m1[i], 16) + parseInt(m2[i], 16)
    if (sum > 0xff) {
      throw new Error(`octet ${i} overflow: ${m1[i]}+${m2[i]}=${sum.toString(16)}`)
    }
    return sum.toString(16).padStart(2, '0')
  }).join('')
}

addHexColor2('aaaaaa', 'bogus!') // => Error: invalid hex color triplet(s): aaaaaa / bogus!
addHexColor2('aaaaaa', '606060') // => Error: octet 1 overflow: aa+60=10a

2
这非常整洁。如果你将0x010101加到0xFFFFFF,或者将0x000100加到0x00FF00,你会得到一些奇怪的结果,因为实际上这里有三个八位字节对,它们应该分别用限制>00<FF求和,然后再拼接在一起。 - Bob

14
这样怎么样:

var hexValue = "aaaaaa";
hexValue = (parseInt(hexValue, 16) + 0x010101).toString(16);
document.writeln(hexValue); // outputs 'ababab'

如果使用parseInt函数,无需添加0x前缀。


7

我认为接受的答案是错误的。十六进制颜色表示不是线性的,而是给出了R、G和B三组两个字符。

因此,你不能仅仅添加一个整数并期望RGB正确相加。

For Example

n1 = '005500'; <--- green
n2 = '00ff00'; <--- brighter green

将这些数字相加应该会使绿色更绿。添加绿色不应增加红色的增量。但是,通过采用接受的答案所做的方式,即将整个数字视为一个数字,然后对于相加总和大于f的数字进行进位,如f + 1 = 10。
you get `015400` so by adding greens the RED increased .... WRONG

将 005500 和 00ff00 相加应该得到 00ff00,因为最大绿色值已经达到,无法再添加更多的绿色。


你说的一切都是正确的,但我认为这更像是一条评论而不是答案,因为你实际上没有提供问题的答案。如果你在这里提供一个解决方案 - 如你所描述的那样正确地执行 - 那么它甚至应该成为被接受的答案。就目前而言,我会给它点个踩...如果你要么把它移到评论中,要么回答问题,我很乐意改变我的态度。 - Alacritas

4

对于寻找能够在不越界的情况下添加和减去十六进制颜色的函数的人们,我刚刚写了这个函数来实现这一目标:

export function shiftColor(base, change, direction) {
  const colorRegEx = /^\#?[A-Fa-f0-9]{6}$/;

  // Missing parameter(s)
  if (!base || !change) {
    return '000000';
  }

  // Invalid parameter(s)
  if (!base.match(colorRegEx) || !change.match(colorRegEx)) {
    return '000000';
  }

  // Remove any '#'s
  base = base.replace(/\#/g, '');
  change = change.replace(/\#/g, '');

  // Build new color
  let newColor = '';
  for (let i = 0; i < 3; i++) {
    const basePiece = parseInt(base.substring(i * 2, i * 2 + 2), 16);
    const changePiece = parseInt(change.substring(i * 2, i * 2 + 2), 16);
    let newPiece = '';

    if (direction === 'add') {
      newPiece = (basePiece + changePiece);
      newPiece = newPiece > 255 ? 255 : newPiece;
    }
    if (direction === 'sub') {
      newPiece = (basePiece - changePiece);
      newPiece = newPiece < 0 ? 0 : newPiece;
    }

    newPiece = newPiece.toString(16);
    newPiece = newPiece.length < 2 ? '0' + newPiece : newPiece;
    newColor += newPiece;
  }

  return newColor;
}

你需要将基础颜色作为参数 1 传递,将更改颜色作为参数 2 传递,并根据你的意图在最后一个参数中选择“add”或“sub”。


这应该是被接受的答案,它实际上提供了一个可行的解决方案。 - Alacritas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接