Skip to content

Commit

Permalink
v0.1.0 Adds effects builders and QOL changes to utils
Browse files Browse the repository at this point in the history
  • Loading branch information
mster committed Apr 14, 2021
1 parent 25b1cea commit d0c5212
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 37 deletions.
88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,99 @@ scheme = scheme.map(e => HSV2RGB(e))
]
*/

schemes = schemes.map(e => HSV2Hex(e))
scheme = scheme.map(e => RGB2Hex(e))
/*
[ 'FF6745', 'FFC445', 'DDFF45', '80FF45' ]
*/
```

# Effects

Prismaek includes an effect builder for generating **tints**, **tones**, and **shades**. Effects have one required argument and three optional arguments.

<br>

### shade(base[, format][, step][, count ])
* #### **base** \<Object> | \<String> | \<Array> Base color object (HSV/RGB), hex value as a string, or an array containing any combination of either.
* #### **format** \<String> Output format. Optional, **Default:** `hex`
* Supported types: `hex`, `rgb`, `hsv`
* #### **step** \<Number> | \<String> Step size. Optional, **Default:** `0.10`
* #### **count** \<Number> | \<String> Iteration count (base color included). Optional, **Default:** `5`

<br>

```js
const { shade, tint, tone } = require('prismaek').effects;

const color = { r: 102, g: 55, b: 69 };
const shades = shade(color, "hex", 0.05, 5);
// [ '663745', '613442', '5C323E', '572F3B', '522C37' ]
```

Works with schemes too. Pretty cool, huh?

```js
const color = "#289866";
const tetradicScheme = tetradic(hex2HSV(color));
/* [{ h: 153, s: 0.737, v: 0.596 },
{ h: 243, s: 0.737, v: 0.596 },
{ h: 333, s: 0.737, v: 0.596 },
{ h: 63, s: 0.737, v: 0.596 }] */

const tetradicTones = tone(tetradicScheme, "rgb", 0.1, 3);
/* {
'0': [
{ r: 60, g: 228, b: 153, shift: 0 },
{ r: 69, g: 60, b: 228, shift: 0 },
{ r: 228, g: 60, b: 135, shift: 0 },
{ r: 219, g: 228, b: 60, shift: 0 }
],
'0.1': [
{ r: 56, g: 213, b: 143, shift: 0.1 },
{ r: 64, g: 56, b: 213, shift: 0.1 },
{ r: 213, g: 56, b: 126, shift: 0.1 },
{ r: 204, g: 213, b: 56, shift: 0.1 }
],
'0.2': [
{ r: 52, g: 198, b: 133, shift: 0.2 },
{ r: 60, g: 52, b: 198, shift: 0.2 },
{ r: 198, g: 52, b: 117, shift: 0.2 },
{ r: 190, g: 198, b: 52, shift: 0.2 }
]
} */
```

Finally, something a bit funky.

```js
let woah = RGBs.map(rgb => tone(triadic(RGB2HSV(rgb))));
/*[
{
'0': [ '950269', '699502', '026995' ],
'0.1': [ '8B0162', '628B01', '01628B' ],
'0.2': [ '81015B', '5B8101', '015B81' ],
'0.3': [ '770154', '547701', '015477' ],
'0.4': [ '6D014D', '4D6D01', '014D6D' ]
},
{
'0': [ 'B972C2', 'C2B972', '72C2B9' ],
'0.1': [ 'AC6AB5', 'B5AC6A', '6AB5AC' ],
'0.2': [ 'A063A8', 'A8A063', '63A8A0' ],
'0.3': [ '945B9B', '9B945B', '5B9B94' ],
'0.4': [ '87548E', '8E8754', '548E87' ]
},
{
'0': [ '995368', '689953', '536899' ],
'0.1': [ '8F4D61', '618F4D', '4D618F' ],
'0.2': [ '85485A', '5A8548', '485A85' ],
'0.3': [ '7A4253', '537A42', '42537A' ],
'0.4': [ '703D4C', '4C703D', '3D4C70' ]
}
] */
```

# Contributing
Thanks for checking out my package! Contribution guidelines will be coming soon.



3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

module.exports = {
utils: require('./lib/utils'),
harmonies: require('./lib/harmonies')
harmonies: require('./lib/harmonies'),
effects: require('./lib/effects')
}
150 changes: 150 additions & 0 deletions lib/effects.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
'use strict'

const {
isHex,
isRGB,
isHSV,
hex2RGB,
HSV2RGB,
RGB2Hex,
RGB2HSV
} = require('./utils');

module.exports = {
shade,
tint,
tone
};

function shade (base, format, step, count) {
if (!base) throw new Error("Error: arg `base` is required!")

if (!format) format = "hex";
if (!step) step = 0.10;
if (!count) count = 5;

return effectsBuilder(base, format, step, count, (rgb, shift) => {
return {
r: Math.min(Math.round((+rgb.r) * (1 - shift)), 255),
g: Math.min(Math.round((+rgb.g) * (1 - shift)), 255),
b: Math.min(Math.round((+rgb.b) * (1 - shift)), 255),
shift
}
});
}

function tint (base, format, step, count) {
if (!base) throw new Error("Error: arg `base` is required!")

if (!format) format = "hex";
if (!step) step = 0.10;
if (!count) count = 5;

return effectsBuilder(base, format, step, count, (rgb, shift) => {
return {
r: Math.min(Math.round((255 - (+rgb.r)) * +shift), 255),
g: Math.min(Math.round((255 - (+rgb.g)) * +shift), 255),
b: Math.min(Math.round((255 - (+rgb.b)) * +shift), 255),
shift
}
});
}

function tone (base, format, step, count) {
if (!base) throw new Error("Error: arg `base` is required!")

if (!format) format = "hex";
if (!step) step = 0.10;
if (!count) count = 5;

return effectsBuilder(base, format, step, count, (rgb, shift) => {
return {
r: Math.min(Math.round((+rgb.r) + (+rgb.r) * (0.5 - shift)), 255),
g: Math.min(Math.round((+rgb.g) + (+rgb.g) * (0.5 - shift)), 255),
b: Math.min(Math.round((+rgb.b) + (+rgb.b) * (0.5 - shift)), 255),
shift
}
});
}


function effectsBuilder (base, format="hex", step=0.05, count=5, applyEffect) {
if (isNaN(step) || isNaN(count)) {
throw new Error(`Arguments step and count must be of type number (or coercible). step=${step} count=${count}`)
}

if (step < 0.01) {
throw new Error(`Shade step percision must be >= 0.01 (1%). step=${step}`);
}

const formatMap = {
"hex": RGB2Hex,
"hsv": RGB2HSV,
"rgb": ((e) => e)
}

const constructor = base?.constructor?.name

if (constructor === "Array") {
const _results = {};

let i = 0;
for(; i < count; i++) {
const shift = Math.round(i * step * 100) / 100;

_results[String(shift)] = base.map((color, index) => {
let hex, hsv;
if (
(color?.constructor?.name === "Object" || color?.constructor?.name === "String") &&
(isRGB(color) || (hex = isHex(color)) || (hsv = isHSV(color)))
) {
if (hex) color = hex2RGB(color);
if (hsv) color = HSV2RGB(color);
return formatMap[format](applyEffect(color, shift));
}

throw new Error(`Invalid color format; unable to generate shades. base, index=${index} color=${color}`)
});
}

return _results;
}

// valid for hsv or rgb
let hsv;
if (constructor === "Object" && (isRGB(base) || (hsv = isHSV(base)))) {
if (hsv) base = HSV2RGB(base);

const _results = [];

let i = 0;
for(; i < count; i++) {
const shift = Math.round(i * step * 100) / 100;
_results.push(formatMap[format](applyEffect(base, shift)));
}

return _results;
}

// only valid for singular hex color
if (base.constructor.name === "String") {
if (base.includes('#')) base = base.replace('#', '');

if (isHex(base)) {
const rgb = hex2RGB(base);
const _results = [];

let i = 0;
for(; i < count; i++) {
const shift = Math.round(i * step * 100) / 100;
_results.push(formatMap[format](applyEffect(rgb, shift)));
}

return _results;
}

throw new Error(`Invalid color format; unable to generate shades. base=${base}`)
}

throw new Error(`Shade generator requires a valid base color, or array of colors. \nbase.constructor.name=${base?.constructor?.name}`)
}
Loading

0 comments on commit d0c5212

Please sign in to comment.