According to the guidelines outlined in the CSSOM specification:
<alphavalue>
If the value is internally represented as an integer between 0 and 255 inclusive (an 8-bit unsigned integer), these steps should be followed:
- Take the given integer and name it alpha.
- If there is a whole number between 0 and 100 that, when multiplied by 2.55 and rounded to the nearest whole number (rounding up if equidistant), equals alpha, then let rounded be that number divided by 100.
- If not, calculate rounded as alpha divided by 0.255, rounded to the nearest whole number (upward rounding if equidistant), then dividing by 1000.
- Serialize the result of rounded as a <number>.
If this fails, serialize the original value as a <number>.
Assuming the opacity of color is saved as a binary value:
const alpha = Math.round(49.6 * 2.55) // 126
const integer = Math.round(alpha / 2.55) // 49
const hasInteger = Math.round(integer * 2.55) === alpha // false
const value = hasInteger
? integer / 100
: Math.round(alpha / 0.255) / 1000
// 0.494
Note regarding floating point precision:
Some values may pose issues with floating point precision, such as when using 50
for opacity where alpha gets rounded to 127</code instead of <code>128
. This discrepancy arises from JavaScript's handling of decimal numbers like
(50 * 2.55) === 127.49999999999999
.
To achieve more accurate serialization of opacity in JavaScript:
const alpha = Math.round((50 * 2.55).toPrecision(15)) // 128
const integer = Math.round(alpha / 2.55) // 50
const hasInteger = Math.round((integer * 2.55).toPrecision(15)) === alpha // true
const value = hasInteger
? integer / 100
: Math.round(alpha / 0.255) / 1000
// 0.5