Skip to content

Commit ec3f142

Browse files
committed
Add filterHueRotate
1 parent 8fe9e72 commit ec3f142

File tree

4 files changed

+95
-43
lines changed

4 files changed

+95
-43
lines changed

docs/api.md

Lines changed: 59 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -723,34 +723,6 @@ culori.blend(['red', 'green'], function average(b, s) {
723723
});
724724
```
725725

726-
## Filter effects
727-
728-
Culori implements some of the filter effects defined by the W3C [Filter Effects Module Level 1](https://drafts.fxtf.org/filter-effects-1/). The _amount_ parameter is usually in the `[0, 1]` interval, but may go above `1` for some filters. The filters were designed for RGB colors, so the _mode_ parameter is expected to be `rgb` or `lrgb`.
729-
730-
<a name="filterBrightness" href="#filterBrightness">#</a> culori.**filterBrightness**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
731-
732-
The [`brightness()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/brightness) CSS filter. An _amount_ of `1` leaves the color unchanged. Smaller values darken the color (with `0` being fully black), while larger values brighten it.
733-
734-
<a name="filterContrast" href="#filterContrast">#</a> culori.**filterContrast**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
735-
736-
The [`contrast()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/contrast) filter. An _amount_ of `1` leaves the color unchanged. Smaller values decrease the contrast (with `0` being fully gray), while larger values increase it.
737-
738-
<a name="filterSepia" href="#filterSepia">#</a> culori.**filterSepia**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
739-
740-
The [`sepia()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/sepia) filter. An _amount_ of `0` leaves the color unchanged, and `1` applies the sepia effect fully.
741-
742-
<a name="filterGrayscale" href="#filterGrayscale">#</a> culori.**filterGrayscale**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
743-
744-
The [`grayscale()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/grayscale) filter. An _amount_ of `0` leaves the color unchanged, and `1` makes the color fully achromatic.
745-
746-
<a name="filterSaturate" href="#filterSaturate">#</a> culori.**filterSaturate**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
747-
748-
The [`saturate()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/saturate) filter. An _amount_ of `1` leaves the color unchanged. Smaller values desaturate the color (with `0` being fully achromatic), while larger values saturate it.
749-
750-
<a name="filterInvert" href="#filterInvert">#</a> culori.**filterInvert**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
751-
752-
The [`invert()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/invert) filter. An _amount_ of `0` leaves the color unchanged, and `1` makes the color fully inverted.
753-
754726
## Random colors
755727

756728
<a name="random" href="#random">#</a> culori.**random**(_mode = 'rgb'_, _constraints = {}_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/random.js)
@@ -796,15 +768,71 @@ Even with these ranges in place, a combination of channel values may not be disp
796768

797769
## WCAG utilities
798770

771+
A couple of utility functions based on the [Web Content Acccessibility Guidelines 2.0 specification](https://www.w3.org/TR/WCAG20/).
772+
799773
<a name="wcagLuminance" href="#wcagLuminance">#</a> culori.**wcagLuminance**(_color_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/wcag.js)
800774

801-
Computes the relative luminance of a color.
775+
Computes the [relative luminance](https://www.w3.org/TR/WCAG20/#relativeluminancedef) of a color.
776+
777+
```js
778+
culori.wcagLuminance('red');
779+
// ⇒ 0.2126
780+
```
802781

803782
<a name="wcagContrast" href="#wcagContrast">#</a> culori.**wcagContrast**(_colorA_, _colorB_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/wcag.js)
804783

805-
Computes the contrast between two colors.
784+
Computes the [contrast ratio](https://www.w3.org/TR/WCAG20/#contrast-ratiodef) between two colors.
785+
786+
```js
787+
culori.wcagContrast('red', 'black');
788+
// ⇒ 5.252
789+
```
790+
791+
## Filters
792+
793+
### CSS filter effects
794+
795+
Culori implements some of the filter effects defined by the W3C [Filter Effects Module Level 1](https://drafts.fxtf.org/filter-effects-1/). The _amount_ parameter is usually in the `[0, 1]` interval, but may go above `1` for some filters. The filters were designed for RGB colors, so the _mode_ parameter is expected to be `rgb` or `lrgb`.
796+
797+
<a name="filterBrightness" href="#filterBrightness">#</a> culori.**filterBrightness**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
798+
799+
The [`brightness()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/brightness) CSS filter. An _amount_ of `1` leaves the color unchanged. Smaller values darken the color (with `0` being fully black), while larger values brighten it.
800+
801+
<a name="filterContrast" href="#filterContrast">#</a> culori.**filterContrast**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
802+
803+
The [`contrast()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/contrast) filter. An _amount_ of `1` leaves the color unchanged. Smaller values decrease the contrast (with `0` being fully gray), while larger values increase it.
804+
805+
<a name="filterSepia" href="#filterSepia">#</a> culori.**filterSepia**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
806+
807+
The [`sepia()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/sepia) filter. An _amount_ of `0` leaves the color unchanged, and `1` applies the sepia effect fully.
808+
809+
<a name="filterGrayscale" href="#filterGrayscale">#</a> culori.**filterGrayscale**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
810+
811+
The [`grayscale()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/grayscale) filter. An _amount_ of `0` leaves the color unchanged, and `1` makes the color fully achromatic.
812+
813+
<a name="filterSaturate" href="#filterSaturate">#</a> culori.**filterSaturate**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
814+
815+
The [`saturate()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/saturate) filter. An _amount_ of `1` leaves the color unchanged. Smaller values desaturate the color (with `0` being fully achromatic), while larger values saturate it.
816+
817+
<a name="filterInvert" href="#filterInvert">#</a> culori.**filterInvert**(_amount = 1_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
818+
819+
The [`invert()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/invert) filter. An _amount_ of `0` leaves the color unchanged, and `1` makes the color fully inverted.
820+
821+
<a name="filterHueRotate" href="#filterHueRotate">#</a> culori.**filterHueRotate**(_degrees = 0_, _mode = 'rgb'_) &middot; [Source](https://github.com/evercoder/culori/blob/master/src/filter.js)
822+
823+
The [`hue-rotate()`](https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/hue-rotate) filter.
824+
825+
```js
826+
culori
827+
.samples(5)
828+
.map(culori.interpolate(['red', 'green', 'blue']))
829+
.map(culori.filterSepia(0.5))
830+
.map(culori.formatHex);
831+
832+
// ⇒ ["#751800", "#664200", "#576c00", "#1a3e82", "#0010ff"];
833+
```
806834

807-
## Color vision deficiency (CVD) simulation
835+
### Color vision deficiency (CVD) simulation
808836

809837
Simulate how a color may be perceived by people with color vision deficiencies (CVD).
810838

src/filter.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,30 @@ const matrixGrayscale = amount => {
7272
];
7373
};
7474

75+
const matrixHueRotate = degrees => {
76+
let rad = (Math.PI * degrees) / 180;
77+
let c = Math.cos(rad);
78+
let s = Math.sin(rad);
79+
return [
80+
0.213 + c * 0.787 - s * 0.213,
81+
0.715 - c * 0.715 - s * 0.715,
82+
0.072 - c * 0.072 + s * 0.928,
83+
0,
84+
0.213 - c * 0.213 + s * 0.143,
85+
0.715 + c * 0.285 + s * 0.14,
86+
0.072 - c * 0.072 - s * 0.283,
87+
0,
88+
0.213 - c * 0.213 - s * 0.787,
89+
0.715 - c * 0.715 + s * 0.715,
90+
0.072 + c * 0.928 + s * 0.072,
91+
0,
92+
0,
93+
0,
94+
0,
95+
1
96+
];
97+
};
98+
7599
const matrix = (values, mode) => {
76100
let conv = converter(mode);
77101
let channels = getModeDefinition(mode).channels;
@@ -109,12 +133,15 @@ const filterInvert = (amt = 1, mode = 'rgb') => {
109133
let a = clamp(amt);
110134
return mapper((v, ch) => (ch === 'alpha' ? v : lerp(a, 1 - a, v)), mode);
111135
};
136+
const filterHueRotate = (deg = 0, mode = 'rgb') =>
137+
matrix(matrixHueRotate(deg), mode);
112138

113139
export {
114140
filterBrightness,
115141
filterContrast,
116142
filterSepia,
117143
filterSaturate,
118144
filterGrayscale,
119-
filterInvert
145+
filterInvert,
146+
filterHueRotate
120147
};

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,5 +169,6 @@ export {
169169
filterSepia,
170170
filterInvert,
171171
filterSaturate,
172-
filterGrayscale
172+
filterGrayscale,
173+
filterHueRotate
173174
} from './filter';

test/filter.test.js

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
formatHex,
77
filterSaturate,
88
filterGrayscale,
9-
filterInvert
9+
filterInvert,
10+
filterHueRotate
1011
} from '../src/index';
1112

1213
tape('filterBrightness', t => {
@@ -19,9 +20,7 @@ tape('filterContrast', t => {
1920

2021
tape('filterSepia', t => {
2122
t.equal(formatHex(filterSepia(0)('red')), '#ff0000', 'unchanged');
22-
2323
t.equal(formatHex(filterSepia(1)('red')), '#645945', 'fully sepia');
24-
2524
t.end();
2625
});
2726

@@ -31,32 +30,29 @@ tape('filterSaturate', t => {
3130
'#2f2f2f',
3231
'fully desaturated'
3332
);
34-
3533
t.equal(formatHex(filterSaturate(1)('#cc0033')), '#cc0033', 'unchanged');
36-
3734
t.equal(
3835
formatHex(filterSaturate(2)('#cc0033')),
3936
'#ff0037',
4037
'oversaturated'
4138
);
42-
4339
t.end();
4440
});
4541

4642
tape('filterGrayscale', t => {
4743
t.equal(formatHex(filterGrayscale(0)('red')), '#ff0000', 'unchanged');
48-
4944
t.equal(formatHex(filterGrayscale(1)('red')), '#363636', 'fully grayscale');
50-
5145
t.end();
5246
});
5347

5448
tape('filterInvert', t => {
5549
t.equal(formatHex(filterInvert(0)('red')), '#ff0000', 'unchanged');
56-
5750
t.equal(formatHex(filterInvert(0.5)('red')), '#808080', 'gray');
58-
5951
t.equal(formatHex(filterInvert(1)('red')), '#00ffff', 'fully inverted');
52+
t.end();
53+
});
6054

55+
tape('filterHueRotate', t => {
56+
t.equal(formatHex(filterHueRotate(60)('red')), '#6c3b00');
6157
t.end();
6258
});

0 commit comments

Comments
 (0)