colorpath
Version:
Find path for colors
175 lines (153 loc) • 4.89 kB
JavaScript
;
var getAverage = function (arr) {
return arr.reduce(function (prev, next) { return prev + next; }) / arr.length
};
var validatePercentages = function (p) {
return (Math.max.apply(null, p) - Math.min.apply(null, p)) <= 0.02
};
var formatColor = function (color) {
if (Array.isArray(color)) { return color }
color = '' + color;
if (/^#?[0123456789AaBbCcDdEeFf]{6}$/.test(color)) {
color = color.slice(-6);
var redChannel = parseInt(color[0] + color[1], 16);
var greenChannel = parseInt(color[2] + color[3], 16);
var blueChannel = parseInt(color[4] + color[5], 16);
return [redChannel, greenChannel, blueChannel]
} else if (/\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}/.test(color)) {
color = /\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}/.exec(color)[0].split(',').map(function (channel) { return Number(channel.trim()); });
if (color.every(function (channel) { return channel <= 255; })) { return color }
}
/* istanbul ignore next */
throw new Error((color + " is not a legal color!"))
};
var formatNumber = function (num) { return Number(num.toPrecision(4)); };
var util = {
getAverage: getAverage,
validatePercentages: validatePercentages,
formatColor: formatColor,
formatNumber: formatNumber
};
var tint = function (s, p) {
var source = util.formatColor(s);
return source.map(function (sourceChannel) {
sourceChannel = Number(sourceChannel);
var whiteChannel = 255;
return Math.round(sourceChannel + (whiteChannel - sourceChannel) * p)
})
};
var shade = function (s, p) {
var source = util.formatColor(s);
return source.map(function (sourceChannel) { return Math.round(sourceChannel * (1 - p)); })
};
var mix = function (s, m, p) {
var source = util.formatColor(s);
var mixer = util.formatColor(m);
return source.map(function (sourceChannel, index) {
var mixerChannel = mixer[index];
return Math.round(mixerChannel - (mixerChannel - sourceChannel) * p)
})
};
var findTint = function (s, d) {
var source = util.formatColor(s);
var destination = util.formatColor(d);
var pByChannel = [];
source.forEach(function (sourceChannel, index) {
var whiteChannel = 255;
var destChannel = destination[index];
if (sourceChannel === whiteChannel) {
if (destChannel !== whiteChannel) { pByChannel.push(-1); }
return
}
pByChannel.push((destChannel - sourceChannel) / (whiteChannel - sourceChannel));
});
if (util.validatePercentages(pByChannel)) {
return util.formatNumber(util.getAverage(pByChannel))
} else {
throw new Error((d + " can not be tinted from " + s + ", try findPath instead."))
}
};
var findShade = function (s, d) {
var source = util.formatColor(s);
var destination = util.formatColor(d);
var pByChannel = source.map(function (sourceChannel, index) {
var destChannel = destination[index];
return destChannel / sourceChannel
});
if (util.validatePercentages(pByChannel)) {
return util.formatNumber(1 - util.getAverage(pByChannel))
} else {
throw new Error((d + " can not be shaded from " + s + ", try findPath instead."))
}
};
var source;
var destination;
var getMixerChannel = function (index, p) {
return Math.round((destination[index] - source[index] * p) / (1 - p))
};
var getPercentage = function (index, edge) {
return (edge - destination[index]) / (edge - source[index])
};
var validatePercentage = function (p) {
p = util.formatNumber(p);
return p >= 0 && p <= 1
};
var validateMixer = function (mixer) {
return mixer.every(function (channel) { return channel >= 0 && channel <= 255; })
};
var findMixer = function (s, d) {
source = util.formatColor(s);
destination = util.formatColor(d);
var p;
var mixer = [];
var edges = [0, 255];
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 2; j++) {
var edge = edges[j];
p = getPercentage(i, edge);
mixer = [getMixerChannel(0, p), getMixerChannel(1, p), getMixerChannel(2, p)];
if (validatePercentage(p) && validateMixer(mixer)) {
return {
mixer: mixer,
percentage: util.formatNumber(p)
}
}
}
}
/* istanbul ignore next */
return {}
};
var findPath = function (s, d) {
var source = util.formatColor(s);
var destination = util.formatColor(d);
try {
try {
return {
method: 'shade',
percentage: findShade(source, destination)
}
} catch (e) {
return {
method: 'tint',
percentage: findTint(source, destination)
}
}
} catch (e) {
var result = findMixer(source, destination);
return {
method: 'mix',
mixer: result.mixer,
percentage: result.percentage
}
}
};
var colorPath = {
tint: tint,
shade: shade,
mix: mix,
findTint: findTint,
findShade: findShade,
findMixer: findMixer,
findPath: findPath
};
module.exports = colorPath;