Implement builtin function mix()
This commit is contained in:
parent
c04f83ddcf
commit
af95658953
@ -67,4 +67,4 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
|
||||
};
|
||||
Some(Value::Color(color.fade_out(amount)))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -119,4 +119,22 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
|
||||
_ => todo!("non-color given to builtin function `blue()`")
|
||||
}
|
||||
});
|
||||
}
|
||||
decl!(f "mix", |args, _| {
|
||||
let color1 = match arg!(args, 0, "color1").eval() {
|
||||
Value::Color(c) => c,
|
||||
_ => todo!("non-color given to builtin function `mix()`")
|
||||
};
|
||||
|
||||
let color2 = match arg!(args, 1, "color2").eval() {
|
||||
Value::Color(c) => c,
|
||||
_ => todo!("non-color given to builtin function `mix()`")
|
||||
};
|
||||
|
||||
let weight = match arg!(args, 2, "weight"=Value::Dimension(Number::ratio(1, 2), Unit::None)) {
|
||||
Value::Dimension(n, Unit::None) => n,
|
||||
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
||||
_ => todo!("expected either unitless or % number for $weight")
|
||||
};
|
||||
Some(Value::Color(color1.mix(color2, weight)))
|
||||
});
|
||||
}
|
||||
|
@ -8,6 +8,18 @@ use num_traits::cast::ToPrimitive;
|
||||
|
||||
mod name;
|
||||
|
||||
macro_rules! clamp {
|
||||
($c:expr, $min:literal, $max:literal) => {
|
||||
if $c > Number::from($max) {
|
||||
Number::from($max)
|
||||
} else if $c < Number::from($min) {
|
||||
Number::from($min)
|
||||
} else {
|
||||
$c
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub(crate) struct Color {
|
||||
red: Number,
|
||||
@ -77,6 +89,32 @@ impl Color {
|
||||
pub fn green(&self) -> Number {
|
||||
self.green.clone()
|
||||
}
|
||||
|
||||
/// Mix two colors together with weight
|
||||
/// Algorithm adapted from
|
||||
/// <https://github.com/sass/dart-sass/blob/0d0270cb12a9ac5cce73a4d0785fecb00735feee/lib/src/functions/color.dart#L718>
|
||||
pub fn mix(self, other: Color, weight: Number) -> Self {
|
||||
let weight = clamp!(weight, 0, 100);
|
||||
let normalized_weight = weight.clone() * Number::from(2) - Number::from(1);
|
||||
let alpha_distance = self.alpha.clone() - other.alpha.clone();
|
||||
|
||||
let combined_weight1 =
|
||||
if normalized_weight.clone() * alpha_distance.clone() == Number::from(-1) {
|
||||
normalized_weight
|
||||
} else {
|
||||
(normalized_weight.clone() + alpha_distance.clone())
|
||||
/ (Number::from(1) + normalized_weight * alpha_distance)
|
||||
};
|
||||
let weight1 = (combined_weight1 + Number::from(1)) / Number::from(2);
|
||||
let weight2 = Number::from(1) - weight1.clone();
|
||||
|
||||
Color::from_rgba(
|
||||
self.red * weight1.clone() + other.red * weight2.clone(),
|
||||
self.green * weight1.clone() + other.green * weight2.clone(),
|
||||
self.blue * weight1.clone() + other.blue * weight2,
|
||||
self.alpha * weight.clone() + other.alpha * (Number::from(1) - weight),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// HSLA color functions
|
||||
@ -207,26 +245,11 @@ impl Color {
|
||||
}
|
||||
|
||||
/// Create RGBA representation from HSLA values
|
||||
pub fn from_hsla(
|
||||
mut hue: Number,
|
||||
mut saturation: Number,
|
||||
mut luminance: Number,
|
||||
mut alpha: Number,
|
||||
) -> Self {
|
||||
macro_rules! clamp {
|
||||
($c:ident, $min:literal, $max:literal) => {
|
||||
if $c > Number::from($max) {
|
||||
$c = Number::from($max)
|
||||
} else if $c < Number::from($min) {
|
||||
$c = Number::from($min)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
clamp!(hue, 0, 360);
|
||||
clamp!(saturation, 0, 1);
|
||||
clamp!(luminance, 0, 1);
|
||||
clamp!(alpha, 0, 1);
|
||||
pub fn from_hsla(hue: Number, saturation: Number, luminance: Number, alpha: Number) -> Self {
|
||||
let mut hue = clamp!(hue, 0, 360);
|
||||
let saturation = clamp!(saturation, 0, 1);
|
||||
let luminance = clamp!(luminance, 0, 1);
|
||||
let alpha = clamp!(alpha, 0, 1);
|
||||
|
||||
if saturation.clone() == Number::from(0) {
|
||||
let luminance = if luminance > Number::from(100) {
|
||||
|
@ -356,3 +356,23 @@ test!(
|
||||
"a {\n color: complement(red);\n}\n",
|
||||
"a {\n color: aqua;\n}\n"
|
||||
);
|
||||
test!(
|
||||
mix_no_weight,
|
||||
"a {\n color: mix(#f00, #00f);\n}\n",
|
||||
"a {\n color: purple;\n}\n"
|
||||
);
|
||||
test!(
|
||||
mix_weight_25,
|
||||
"a {\n color: mix(#f00, #00f, 25%);\n}\n",
|
||||
"a {\n color: #4000bf;\n}\n"
|
||||
);
|
||||
test!(
|
||||
mix_opacity,
|
||||
"a {\n color: mix(rgba(255, 0, 0, 0.5), #00f);\n}\n",
|
||||
"a {\n color: rgba(64, 0, 191, 0.75);\n}\n"
|
||||
);
|
||||
test!(
|
||||
mix_sanity_check,
|
||||
"a {\n color: mix(black, white);\n}\n",
|
||||
"a {\n color: gray;\n}\n"
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user