@w3h/material-color-utilities
Version:
Algorithms and utilities that power the Material Design 3 (M3) color system, including choosing theme colors from images and creating tones of colors; all in a new color space.
151 lines • 5.69 kB
JavaScript
/**
* @license
* Copyright 2021 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A color system built using CAM16 hue and chroma, and L* from
* L*a*b*.
*
* Using L* creates a link between the color system, contrast, and thus
* accessibility. Contrast ratio depends on relative luminance, or Y in the XYZ
* color space. L*, or perceptual luminance can be calculated from Y.
*
* Unlike Y, L* is linear to human perception, allowing trivial creation of
* accurate color tones.
*
* Unlike contrast ratio, measuring contrast in L* is linear, and simple to
* calculate. A difference of 40 in HCT tone guarantees a contrast ratio >= 3.0,
* and a difference of 50 guarantees a contrast ratio >= 4.5.
*/
import * as utils from '../utils/color_utils.js';
import { Cam16 } from './cam16.js';
import { HctSolver } from './hct_solver.js';
import { ViewingConditions } from './viewing_conditions.js';
/**
* HCT, hue, chroma, and tone. A color system that provides a perceptually
* accurate color measurement system that can also accurately render what colors
* will appear as in different lighting environments.
*/
export class Hct {
static from(hue, chroma, tone) {
return new Hct(HctSolver.solveToInt(hue, chroma, tone));
}
/**
* @param argb ARGB representation of a color.
* @return HCT representation of a color in default viewing conditions
*/
static fromInt(argb) {
return new Hct(argb);
}
toInt() {
return this.argb;
}
/**
* A number, in degrees, representing ex. red, orange, yellow, etc.
* Ranges from 0 <= hue < 360.
*/
get hue() {
return this.internalHue;
}
/**
* @param newHue 0 <= newHue < 360; invalid values are corrected.
* Chroma may decrease because chroma has a different maximum for any given
* hue and tone.
*/
set hue(newHue) {
this.setInternalState(HctSolver.solveToInt(newHue, this.internalChroma, this.internalTone));
}
get chroma() {
return this.internalChroma;
}
/**
* @param newChroma 0 <= newChroma < ?
* Chroma may decrease because chroma has a different maximum for any given
* hue and tone.
*/
set chroma(newChroma) {
this.setInternalState(HctSolver.solveToInt(this.internalHue, newChroma, this.internalTone));
}
/** Lightness. Ranges from 0 to 100. */
get tone() {
return this.internalTone;
}
/**
* @param newTone 0 <= newTone <= 100; invalid valids are corrected.
* Chroma may decrease because chroma has a different maximum for any given
* hue and tone.
*/
set tone(newTone) {
this.setInternalState(HctSolver.solveToInt(this.internalHue, this.internalChroma, newTone));
}
/** Sets a property of the Hct object. */
setValue(propertyName, value) {
this[propertyName] = value;
}
toString() {
return `HCT(${this.hue.toFixed(0)}, ${this.chroma.toFixed(0)}, ${this.tone.toFixed(0)})`;
}
static isBlue(hue) {
return hue >= 250 && hue < 270;
}
static isYellow(hue) {
return hue >= 105 && hue < 125;
}
static isCyan(hue) {
return hue >= 170 && hue < 207;
}
constructor(argb) {
this.argb = argb;
const cam = Cam16.fromInt(argb);
this.internalHue = cam.hue;
this.internalChroma = cam.chroma;
this.internalTone = utils.lstarFromArgb(argb);
this.argb = argb;
}
setInternalState(argb) {
const cam = Cam16.fromInt(argb);
this.internalHue = cam.hue;
this.internalChroma = cam.chroma;
this.internalTone = utils.lstarFromArgb(argb);
this.argb = argb;
}
/**
* Translates a color into different [ViewingConditions].
*
* Colors change appearance. They look different with lights on versus off,
* the same color, as in hex code, on white looks different when on black.
* This is called color relativity, most famously explicated by Josef Albers
* in Interaction of Color.
*
* In color science, color appearance models can account for this and
* calculate the appearance of a color in different settings. HCT is based on
* CAM16, a color appearance model, and uses it to make these calculations.
*
* See [ViewingConditions.make] for parameters affecting color appearance.
*/
inViewingConditions(vc) {
// 1. Use CAM16 to find XYZ coordinates of color in specified VC.
const cam = Cam16.fromInt(this.toInt());
const viewedInVc = cam.xyzInViewingConditions(vc);
// 2. Create CAM16 of those XYZ coordinates in default VC.
const recastInVc = Cam16.fromXyzInViewingConditions(viewedInVc[0], viewedInVc[1], viewedInVc[2], ViewingConditions.make());
// 3. Create HCT from:
// - CAM16 using default VC with XYZ coordinates in specified VC.
// - L* converted from Y in XYZ coordinates in specified VC.
const recastHct = Hct.from(recastInVc.hue, recastInVc.chroma, utils.lstarFromY(viewedInVc[1]));
return recastHct;
}
}
//# sourceMappingURL=hct.js.map