Initial commit
This commit is contained in:
438
node/TestCli/node_modules/tinygradient/index.js
generated
vendored
Normal file
438
node/TestCli/node_modules/tinygradient/index.js
generated
vendored
Normal file
@@ -0,0 +1,438 @@
|
||||
const tinycolor = require('tinycolor2');
|
||||
|
||||
/**
|
||||
* @typedef {Object} TinyGradient.StopInput
|
||||
* @property {ColorInput} color
|
||||
* @property {number} pos
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TinyGradient.StepValue
|
||||
* @type {number} [r]
|
||||
* @type {number} [g]
|
||||
* @type {number} [b]
|
||||
* @type {number} [h]
|
||||
* @type {number} [s]
|
||||
* @type {number} [v]
|
||||
* @type {number} [a]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {StepValue}
|
||||
*/
|
||||
const RGBA_MAX = { r: 256, g: 256, b: 256, a: 1 };
|
||||
|
||||
/**
|
||||
* @type {StepValue}
|
||||
*/
|
||||
const HSVA_MAX = { h: 360, s: 1, v: 1, a: 1 };
|
||||
|
||||
/**
|
||||
* Linearly compute the step size between start and end (not normalized)
|
||||
* @param {StepValue} start
|
||||
* @param {StepValue} end
|
||||
* @param {number} steps - number of desired steps
|
||||
* @return {StepValue}
|
||||
*/
|
||||
function stepize(start, end, steps) {
|
||||
let step = {};
|
||||
|
||||
for (let k in start) {
|
||||
if (start.hasOwnProperty(k)) {
|
||||
step[k] = steps === 0 ? 0 : (end[k] - start[k]) / steps;
|
||||
}
|
||||
}
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the final step color
|
||||
* @param {StepValue} step - from `stepize`
|
||||
* @param {StepValue} start
|
||||
* @param {number} i - color index
|
||||
* @param {StepValue} max - rgba or hsva of maximum values for each channel
|
||||
* @return {StepValue}
|
||||
*/
|
||||
function interpolate(step, start, i, max) {
|
||||
let color = {};
|
||||
|
||||
for (let k in start) {
|
||||
if (start.hasOwnProperty(k)) {
|
||||
color[k] = step[k] * i + start[k];
|
||||
color[k] = color[k] < 0 ? color[k] + max[k] : (max[k] !== 1 ? color[k] % max[k] : color[k]);
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate gradient with RGBa interpolation
|
||||
* @param {StopInput} stop1
|
||||
* @param {StopInput} stop2
|
||||
* @param {number} steps
|
||||
* @return {tinycolor[]} color1 included, color2 excluded
|
||||
*/
|
||||
function interpolateRgb(stop1, stop2, steps) {
|
||||
const start = stop1.color.toRgb();
|
||||
const end = stop2.color.toRgb();
|
||||
const step = stepize(start, end, steps);
|
||||
let gradient = [stop1.color];
|
||||
|
||||
for (let i = 1; i < steps; i++) {
|
||||
const color = interpolate(step, start, i, RGBA_MAX);
|
||||
gradient.push(tinycolor(color));
|
||||
}
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate gradient with HSVa interpolation
|
||||
* @param {StopInput} stop1
|
||||
* @param {StopInput} stop2
|
||||
* @param {number} steps
|
||||
* @param {boolean|'long'|'short'} mode
|
||||
* @return {tinycolor[]} color1 included, color2 excluded
|
||||
*/
|
||||
function interpolateHsv(stop1, stop2, steps, mode) {
|
||||
const start = stop1.color.toHsv();
|
||||
const end = stop2.color.toHsv();
|
||||
|
||||
// rgb interpolation if one of the steps in grayscale
|
||||
if (start.s === 0 || end.s === 0) {
|
||||
return interpolateRgb(stop1, stop2, steps);
|
||||
}
|
||||
|
||||
let trigonometric;
|
||||
if (typeof mode === 'boolean') {
|
||||
trigonometric = mode;
|
||||
}
|
||||
else {
|
||||
const trigShortest = (start.h < end.h && end.h - start.h < 180) || (start.h > end.h && start.h - end.h > 180);
|
||||
trigonometric = (mode === 'long' && trigShortest) || (mode === 'short' && !trigShortest);
|
||||
}
|
||||
|
||||
const step = stepize(start, end, steps);
|
||||
let gradient = [stop1.color];
|
||||
|
||||
// recompute hue
|
||||
let diff;
|
||||
if ((start.h <= end.h && !trigonometric) || (start.h >= end.h && trigonometric)) {
|
||||
diff = end.h - start.h;
|
||||
}
|
||||
else if (trigonometric) {
|
||||
diff = 360 - end.h + start.h;
|
||||
}
|
||||
else {
|
||||
diff = 360 - start.h + end.h;
|
||||
}
|
||||
step.h = Math.pow(-1, trigonometric ? 1 : 0) * Math.abs(diff) / steps;
|
||||
|
||||
for (let i = 1; i < steps; i++) {
|
||||
const color = interpolate(step, start, i, HSVA_MAX);
|
||||
gradient.push(tinycolor(color));
|
||||
}
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute substeps between each stops
|
||||
* @param {StopInput[]} stops
|
||||
* @param {number} steps
|
||||
* @return {number[]}
|
||||
*/
|
||||
function computeSubsteps(stops, steps) {
|
||||
const l = stops.length;
|
||||
|
||||
// validation
|
||||
steps = parseInt(steps, 10);
|
||||
|
||||
if (isNaN(steps) || steps < 2) {
|
||||
throw new Error('Invalid number of steps (< 2)');
|
||||
}
|
||||
if (steps < l) {
|
||||
throw new Error('Number of steps cannot be inferior to number of stops');
|
||||
}
|
||||
|
||||
// compute substeps from stop positions
|
||||
let substeps = [];
|
||||
|
||||
for (let i = 1; i < l; i++) {
|
||||
const step = (steps - 1) * (stops[i].pos - stops[i - 1].pos);
|
||||
substeps.push(Math.max(1, Math.round(step)));
|
||||
}
|
||||
|
||||
// adjust number of steps
|
||||
let totalSubsteps = 1;
|
||||
for (let n = l - 1; n--;) totalSubsteps += substeps[n];
|
||||
|
||||
while (totalSubsteps !== steps) {
|
||||
if (totalSubsteps < steps) {
|
||||
const min = Math.min.apply(null, substeps);
|
||||
substeps[substeps.indexOf(min)]++;
|
||||
totalSubsteps++;
|
||||
}
|
||||
else {
|
||||
const max = Math.max.apply(null, substeps);
|
||||
substeps[substeps.indexOf(max)]--;
|
||||
totalSubsteps--;
|
||||
}
|
||||
}
|
||||
|
||||
return substeps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the color at a specific position
|
||||
* @param {StopInput[]} stops
|
||||
* @param {number} pos
|
||||
* @param {string} method
|
||||
* @param {StepValue} max
|
||||
* @returns {tinycolor}
|
||||
*/
|
||||
function computeAt(stops, pos, method, max) {
|
||||
if (pos < 0 || pos > 1) {
|
||||
throw new Error('Position must be between 0 and 1');
|
||||
}
|
||||
|
||||
let start, end;
|
||||
for (let i = 0, l = stops.length; i < l - 1; i++) {
|
||||
if (pos >= stops[i].pos && pos < stops[i + 1].pos) {
|
||||
start = stops[i];
|
||||
end = stops[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!start) {
|
||||
start = end = stops[stops.length - 1];
|
||||
}
|
||||
|
||||
const step = stepize(start.color[method](), end.color[method](), (end.pos - start.pos) * 100);
|
||||
const color = interpolate(step, start.color[method](), (pos - start.pos) * 100, max);
|
||||
return tinycolor(color);
|
||||
}
|
||||
|
||||
class TinyGradient {
|
||||
/**
|
||||
* @param {StopInput[]|ColorInput[]} stops
|
||||
* @returns {TinyGradient}
|
||||
*/
|
||||
constructor(stops) {
|
||||
// validation
|
||||
if (stops.length < 2) {
|
||||
throw new Error('Invalid number of stops (< 2)');
|
||||
}
|
||||
|
||||
const havingPositions = stops[0].pos !== undefined;
|
||||
let l = stops.length;
|
||||
let p = -1;
|
||||
let lastColorLess = false;
|
||||
// create tinycolor objects and clean positions
|
||||
this.stops = stops.map((stop, i) => {
|
||||
const hasPosition = stop.pos !== undefined;
|
||||
if (havingPositions ^ hasPosition) {
|
||||
throw new Error('Cannot mix positionned and not posionned color stops');
|
||||
}
|
||||
|
||||
if (hasPosition) {
|
||||
const hasColor = stop.color !== undefined;
|
||||
if (!hasColor && (lastColorLess || i === 0 || i === l - 1)) {
|
||||
throw new Error('Cannot define two consecutive position-only stops');
|
||||
}
|
||||
lastColorLess = !hasColor;
|
||||
|
||||
stop = {
|
||||
color : hasColor ? tinycolor(stop.color) : null,
|
||||
colorLess: !hasColor,
|
||||
pos : stop.pos
|
||||
};
|
||||
|
||||
if (stop.pos < 0 || stop.pos > 1) {
|
||||
throw new Error('Color stops positions must be between 0 and 1');
|
||||
}
|
||||
else if (stop.pos < p) {
|
||||
throw new Error('Color stops positions are not ordered');
|
||||
}
|
||||
p = stop.pos;
|
||||
}
|
||||
else {
|
||||
stop = {
|
||||
color: tinycolor(stop.color !== undefined ? stop.color : stop),
|
||||
pos : i / (l - 1)
|
||||
};
|
||||
}
|
||||
|
||||
return stop;
|
||||
});
|
||||
|
||||
if (this.stops[0].pos !== 0) {
|
||||
this.stops.unshift({
|
||||
color: this.stops[0].color,
|
||||
pos : 0
|
||||
});
|
||||
l++;
|
||||
}
|
||||
if (this.stops[l - 1].pos !== 1) {
|
||||
this.stops.push({
|
||||
color: this.stops[l - 1].color,
|
||||
pos : 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return new instance with reversed stops
|
||||
* @return {TinyGradient}
|
||||
*/
|
||||
reverse() {
|
||||
let stops = [];
|
||||
|
||||
this.stops.forEach(function (stop) {
|
||||
stops.push({
|
||||
color: stop.color,
|
||||
pos : 1 - stop.pos
|
||||
});
|
||||
});
|
||||
|
||||
return new TinyGradient(stops.reverse());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return new instance with looped stops
|
||||
* @return {TinyGradient}
|
||||
*/
|
||||
loop() {
|
||||
let stops1 = [];
|
||||
let stops2 = [];
|
||||
|
||||
this.stops.forEach((stop) => {
|
||||
stops1.push({
|
||||
color: stop.color,
|
||||
pos : stop.pos / 2
|
||||
});
|
||||
});
|
||||
|
||||
this.stops.slice(0, -1).forEach((stop) => {
|
||||
stops2.push({
|
||||
color: stop.color,
|
||||
pos : 1 - stop.pos / 2
|
||||
});
|
||||
});
|
||||
|
||||
return new TinyGradient(stops1.concat(stops2.reverse()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate gradient with RGBa interpolation
|
||||
* @param {number} steps
|
||||
* @return {tinycolor[]}
|
||||
*/
|
||||
rgb(steps) {
|
||||
const substeps = computeSubsteps(this.stops, steps);
|
||||
let gradient = [];
|
||||
|
||||
this.stops.forEach((stop, i) => {
|
||||
if (stop.colorLess) {
|
||||
stop.color = interpolateRgb(this.stops[i - 1], this.stops[i + 1], 2)[1];
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0, l = this.stops.length; i < l - 1; i++) {
|
||||
const rgb = interpolateRgb(this.stops[i], this.stops[i + 1], substeps[i]);
|
||||
gradient.splice(gradient.length, 0, ...rgb);
|
||||
}
|
||||
|
||||
gradient.push(this.stops[this.stops.length - 1].color);
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate gradient with HSVa interpolation
|
||||
* @param {number} steps
|
||||
* @param {boolean|'long'|'short'} [mode=false]
|
||||
* - false to step in clockwise
|
||||
* - true to step in trigonometric order
|
||||
* - 'short' to use the shortest way
|
||||
* - 'long' to use the longest way
|
||||
* @return {tinycolor[]}
|
||||
*/
|
||||
hsv(steps, mode) {
|
||||
const substeps = computeSubsteps(this.stops, steps);
|
||||
let gradient = [];
|
||||
|
||||
this.stops.forEach((stop, i) => {
|
||||
if (stop.colorLess) {
|
||||
stop.color = interpolateHsv(this.stops[i - 1], this.stops[i + 1], 2, mode)[1];
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0, l = this.stops.length; i < l - 1; i++) {
|
||||
const hsv = interpolateHsv(this.stops[i], this.stops[i + 1], substeps[i], mode);
|
||||
gradient.splice(gradient.length, 0, ...hsv);
|
||||
}
|
||||
|
||||
gradient.push(this.stops[this.stops.length - 1].color);
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate CSS3 command (no prefix) for this gradient
|
||||
* @param {String} [mode=linear] - 'linear' or 'radial'
|
||||
* @param {String} [direction] - default is 'to right' or 'ellipse at center'
|
||||
* @return {String}
|
||||
*/
|
||||
css(mode, direction) {
|
||||
mode = mode || 'linear';
|
||||
direction = direction || (mode === 'linear' ? 'to right' : 'ellipse at center');
|
||||
|
||||
let css = mode + '-gradient(' + direction;
|
||||
this.stops.forEach(function (stop) {
|
||||
css += ', ' + (stop.colorLess ? '' : stop.color.toRgbString() + ' ') + (stop.pos * 100) + '%';
|
||||
});
|
||||
css += ')';
|
||||
return css;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color at specific position with RGBa interpolation
|
||||
* @param {number} pos, between 0 and 1
|
||||
* @return {tinycolor}
|
||||
*/
|
||||
rgbAt(pos) {
|
||||
return computeAt(this.stops, pos, 'toRgb', RGBA_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color at specific position with HSVa interpolation
|
||||
* @param {number} pos, between 0 and 1
|
||||
* @return {tinycolor}
|
||||
*/
|
||||
hsvAt(pos) {
|
||||
return computeAt(this.stops, pos, 'toHsv', HSVA_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {StopInput[]|ColorInput[]|StopInput...|ColorInput...} stops
|
||||
* @returns {TinyGradient}
|
||||
*/
|
||||
module.exports = function (stops) {
|
||||
// varargs
|
||||
if (arguments.length === 1) {
|
||||
if (!Array.isArray(arguments[0])) {
|
||||
throw new Error('"stops" is not an array');
|
||||
}
|
||||
stops = arguments[0];
|
||||
}
|
||||
else {
|
||||
stops = Array.prototype.slice.call(arguments);
|
||||
}
|
||||
|
||||
return new TinyGradient(stops);
|
||||
};
|
||||
Reference in New Issue
Block a user