mirror of
https://github.com/janishutz/color-thief.git
synced 2025-11-25 05:44:24 +00:00
start migrating to TS, update eslint config
This commit is contained in:
19
.eslintrc.js
19
.eslintrc.js
@@ -1,19 +0,0 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"commonjs": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": "eslint:recommended",
|
||||
"globals": {
|
||||
"Atomics": "readonly",
|
||||
"SharedArrayBuffer": "readonly"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018
|
||||
},
|
||||
"rules": {
|
||||
"one-var": ["warn", { "initialized": "never" }]
|
||||
}
|
||||
}
|
||||
75
eslint.config.mjs
Normal file
75
eslint.config.mjs
Normal file
@@ -0,0 +1,75 @@
|
||||
import stylisticTS from '@stylistic/eslint-plugin';
|
||||
|
||||
export default [
|
||||
{
|
||||
plugins: {
|
||||
'@stylistic/ts': stylisticTS,
|
||||
},
|
||||
languageOptions: {
|
||||
ecmaVersion: 6,
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
// Error prevention
|
||||
'prefer-const': 'error',
|
||||
'no-async-promise-executor': 'error',
|
||||
'no-cond-assign': 'error',
|
||||
'no-const-assign': 'error',
|
||||
'no-constant-condition': 'error',
|
||||
'no-debugger': 'error',
|
||||
'no-dupe-else-if': 'error',
|
||||
'no-dupe-keys': 'warn',
|
||||
'no-dupe-class-members': 'error',
|
||||
'no-dupe-args': 'error',
|
||||
'no-duplicate-case': 'error',
|
||||
'no-duplicate-imports': 'error',
|
||||
'no-empty-pattern': 'error',
|
||||
'no-fallthrough': 'error',
|
||||
'no-invalid-regexp': 'warn',
|
||||
'no-loss-of-precision': 'warn',
|
||||
'no-import-assign': 'error',
|
||||
'no-irregular-whitespace': 'error',
|
||||
'no-self-assign': 'error',
|
||||
'no-undef': 'warn',
|
||||
'no-unreachable': 'error',
|
||||
'no-unsafe-finally': 'error',
|
||||
'no-unsafe-negation': 'error',
|
||||
'no-unused-private-class-members': 'warn',
|
||||
'no-unused-vars': 'error',
|
||||
'no-use-before-define': 'error',
|
||||
|
||||
// Style
|
||||
'arrow-body-style': [ 'error', 'always' ],
|
||||
'camelcase': 'warn',
|
||||
'curly': [ 'error', 'all' ],
|
||||
'eqeqeq': [ 'error', 'always' ],
|
||||
'func-style': [ 'error', 'expression' ],
|
||||
'no-array-constructor': 'error',
|
||||
'no-else-return': 'error',
|
||||
'no-empty': 'warn',
|
||||
'no-empty-static-block': 'warn',
|
||||
'no-eval': 'error',
|
||||
'no-implicit-coercion': 'error',
|
||||
'no-invalid-this': 'warn',
|
||||
'no-multi-assign': 'warn',
|
||||
'no-new': 'error',
|
||||
'no-redeclare': 'error',
|
||||
'no-script-url': 'error',
|
||||
'no-shadow': 'error',
|
||||
'no-shadow-restricted-names': 'error',
|
||||
'no-underscore-dangle': 'warn',
|
||||
'no-unused-expressions': 'warn',
|
||||
'no-useless-constructor': 'warn',
|
||||
'no-var': 'warn',
|
||||
'prefer-arrow-callback': 'error',
|
||||
'prefer-exponentiation-operator': 'warn',
|
||||
'prefer-numeric-literals': 'error',
|
||||
'require-await': 'warn',
|
||||
|
||||
// Formatting
|
||||
// https://eslint.style/rules/js/array-bracket-newline
|
||||
// TODO: Finish up
|
||||
'@stylistic/ts/indent': [ 'error', 4 ]
|
||||
}
|
||||
}
|
||||
]
|
||||
8791
package-lock.json
generated
8791
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -34,22 +34,16 @@
|
||||
"test": "mocha && cypress run --config video=false",
|
||||
"test:browser": "./node_modules/.bin/cypress run --headed --browser chrome",
|
||||
"test:node": "mocha",
|
||||
"cypress": "./node_modules/.bin/cypress open"
|
||||
"cypress": "./node_modules/.bin/cypress open",
|
||||
"lint": "eslint ./src && prettier ./src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@node-minify/core": "^4.0.5",
|
||||
"@node-minify/uglify-es": "^4.0.5",
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"cypress": "^12.6.0",
|
||||
"eslint": "^5.16.0",
|
||||
"http-server": "^0.11.1",
|
||||
"microbundle": "^0.15.0",
|
||||
"mocha": "^6.2.0",
|
||||
"mustache": "^3.0.1"
|
||||
"@stylistic/eslint-plugin": "^2.3.0",
|
||||
"cypress": "^13.12.0",
|
||||
"eslint": "^8.40.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@lokesh.dhakar/quantize": "^1.3.0",
|
||||
"get-pixels": "^3.3.2"
|
||||
"ndarray-pixels": "^4.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// TODO: Update using this pr https://github.com/lokesh/color-thief/pull/254
|
||||
const getPixels = require('get-pixels');
|
||||
const quantize = require('@lokesh.dhakar/quantize');
|
||||
|
||||
@@ -27,73 +28,81 @@ function validateOptions(options) {
|
||||
|
||||
if (typeof colorCount === 'undefined' || !Number.isInteger(colorCount)) {
|
||||
colorCount = 10;
|
||||
} else if (colorCount === 1 ) {
|
||||
throw new Error('colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()');
|
||||
} else if (colorCount === 1) {
|
||||
throw new Error(
|
||||
'colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()',
|
||||
);
|
||||
} else {
|
||||
colorCount = Math.max(colorCount, 2);
|
||||
colorCount = Math.min(colorCount, 20);
|
||||
}
|
||||
|
||||
if (typeof quality === 'undefined' || !Number.isInteger(quality) || quality < 1) {
|
||||
if (
|
||||
typeof quality === 'undefined' ||
|
||||
!Number.isInteger(quality) ||
|
||||
quality < 1
|
||||
) {
|
||||
quality = 10;
|
||||
}
|
||||
|
||||
return {
|
||||
colorCount,
|
||||
quality
|
||||
}
|
||||
quality,
|
||||
};
|
||||
}
|
||||
|
||||
function loadImg(img) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getPixels(img, function(err, data) {
|
||||
if(err) {
|
||||
reject(err)
|
||||
getPixels(img, function (err, data) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(data);
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getColor(img, quality) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getPalette(img, 5, quality)
|
||||
.then(palette => {
|
||||
.then((palette) => {
|
||||
resolve(palette[0]);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function getPalette(img, colorCount = 10, quality = 10) {
|
||||
const options = validateOptions({
|
||||
colorCount,
|
||||
quality
|
||||
quality,
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
loadImg(img)
|
||||
.then(imgData => {
|
||||
.then((imgData) => {
|
||||
const pixelCount = imgData.shape[0] * imgData.shape[1];
|
||||
const pixelArray = createPixelArray(imgData.data, pixelCount, options.quality);
|
||||
const pixelArray = createPixelArray(
|
||||
imgData.data,
|
||||
pixelCount,
|
||||
options.quality,
|
||||
);
|
||||
|
||||
const cmap = quantize(pixelArray, options.colorCount);
|
||||
const palette = cmap? cmap.palette() : null;
|
||||
const palette = cmap ? cmap.palette() : null;
|
||||
|
||||
resolve(palette);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getColor,
|
||||
getPalette
|
||||
getPalette,
|
||||
};
|
||||
|
||||
|
||||
0
src/color-thief-node.ts
Normal file
0
src/color-thief-node.ts
Normal file
@@ -20,7 +20,6 @@ import core from './core.js';
|
||||
* @license
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
CanvasImage Class
|
||||
Class that wraps the html image element and canvas.
|
||||
@@ -55,13 +54,12 @@ var ColorThief = function () {};
|
||||
* most dominant color.
|
||||
*
|
||||
* */
|
||||
ColorThief.prototype.getColor = function(sourceImage, quality = 10) {
|
||||
ColorThief.prototype.getColor = function (sourceImage, quality = 10) {
|
||||
const palette = this.getPalette(sourceImage, 5, quality);
|
||||
const dominantColor = palette[0];
|
||||
return dominantColor;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* getPalette(sourceImage[, colorCount, quality])
|
||||
* returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
|
||||
@@ -77,10 +75,10 @@ ColorThief.prototype.getColor = function(sourceImage, quality = 10) {
|
||||
*
|
||||
*
|
||||
*/
|
||||
ColorThief.prototype.getPalette = function(sourceImage, colorCount, quality) {
|
||||
ColorThief.prototype.getPalette = function (sourceImage, colorCount, quality) {
|
||||
const options = core.validateOptions({
|
||||
colorCount,
|
||||
quality
|
||||
quality,
|
||||
});
|
||||
|
||||
// Create custom CanvasImage object
|
||||
@@ -88,53 +86,56 @@ ColorThief.prototype.getPalette = function(sourceImage, colorCount, quality) {
|
||||
const imageData = image.getImageData();
|
||||
const pixelCount = image.width * image.height;
|
||||
|
||||
const pixelArray = core.createPixelArray(imageData.data, pixelCount, options.quality);
|
||||
const pixelArray = core.createPixelArray(
|
||||
imageData.data,
|
||||
pixelCount,
|
||||
options.quality,
|
||||
);
|
||||
|
||||
// Send array to quantize function which clusters values
|
||||
// using median cut algorithm
|
||||
const cmap = quantize(pixelArray, options.colorCount);
|
||||
const palette = cmap? cmap.palette() : null;
|
||||
const palette = cmap ? cmap.palette() : null;
|
||||
|
||||
return palette;
|
||||
};
|
||||
|
||||
ColorThief.prototype.getColorFromUrl = function(imageUrl, callback, quality) {
|
||||
const sourceImage = document.createElement("img");
|
||||
ColorThief.prototype.getColorFromUrl = function (imageUrl, callback, quality) {
|
||||
const sourceImage = document.createElement('img');
|
||||
|
||||
sourceImage.addEventListener('load' , () => {
|
||||
sourceImage.addEventListener('load', () => {
|
||||
const palette = this.getPalette(sourceImage, 5, quality);
|
||||
const dominantColor = palette[0];
|
||||
callback(dominantColor, imageUrl);
|
||||
});
|
||||
sourceImage.src = imageUrl
|
||||
sourceImage.src = imageUrl;
|
||||
};
|
||||
|
||||
|
||||
ColorThief.prototype.getImageData = function(imageUrl, callback) {
|
||||
ColorThief.prototype.getImageData = function (imageUrl, callback) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', imageUrl, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
xhr.onload = function() {
|
||||
xhr.onload = function () {
|
||||
if (this.status == 200) {
|
||||
let uInt8Array = new Uint8Array(this.response);
|
||||
i = uInt8Array.length;
|
||||
let binaryString = new Array(i);
|
||||
for (let i = 0; i < uInt8Array.length; i++){
|
||||
for (let i = 0; i < uInt8Array.length; i++) {
|
||||
binaryString[i] = String.fromCharCode(uInt8Array[i]);
|
||||
}
|
||||
let data = binaryString.join('');
|
||||
let base64 = window.btoa(data);
|
||||
callback ('data:image/png;base64,' + base64);
|
||||
}
|
||||
callback('data:image/png;base64,' + base64);
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
ColorThief.prototype.getColorAsync = function(imageUrl, callback, quality) {
|
||||
ColorThief.prototype.getColorAsync = function (imageUrl, callback, quality) {
|
||||
const thief = this;
|
||||
this.getImageData(imageUrl, function(imageData){
|
||||
const sourceImage = document.createElement("img");
|
||||
sourceImage.addEventListener('load' , function(){
|
||||
this.getImageData(imageUrl, function (imageData) {
|
||||
const sourceImage = document.createElement('img');
|
||||
sourceImage.addEventListener('load', function () {
|
||||
const palette = thief.getPalette(sourceImage, 5, quality);
|
||||
const dominantColor = palette[0];
|
||||
callback(dominantColor, this);
|
||||
@@ -143,5 +144,4 @@ ColorThief.prototype.getColorAsync = function(imageUrl, callback, quality) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export default ColorThief;
|
||||
|
||||
180
src/color-thief.ts
Normal file
180
src/color-thief.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import quantize from '../node_modules/@lokesh.dhakar/quantize/dist/index.mjs';
|
||||
import core from './core.js';
|
||||
|
||||
/*
|
||||
* Color Thief v2.4.0
|
||||
* by Lokesh Dhakar - http://www.lokeshdhakar.com
|
||||
*
|
||||
* Thanks
|
||||
* ------
|
||||
* Nick Rabinowitz - For creating quantize.js.
|
||||
* John Schulz - For clean up and optimization. @JFSIII
|
||||
* Nathan Spady - For adding drag and drop support to the demo page.
|
||||
*
|
||||
* License
|
||||
* -------
|
||||
* Copyright Lokesh Dhakar
|
||||
* Released under the MIT license
|
||||
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
|
||||
*
|
||||
* @license
|
||||
*/
|
||||
|
||||
/*
|
||||
CanvasImage Class
|
||||
Class that wraps the html image element and canvas.
|
||||
It also simplifies some of the canvas context manipulation
|
||||
with a set of helper functions.
|
||||
*/
|
||||
|
||||
const CanvasImage = function (image: HTMLImageElement) {
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.context = this.canvas.getContext('2d');
|
||||
this.width = this.canvas.width = image.naturalWidth;
|
||||
this.height = this.canvas.height = image.naturalHeight;
|
||||
this.context.drawImage(image, 0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
CanvasImage.prototype.getImageData = function () {
|
||||
return this.context.getImageData(0, 0, this.width, this.height);
|
||||
};
|
||||
|
||||
interface ColorThiefResult {
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
class ColorThief {
|
||||
/**
|
||||
* Use the median cut algorithm provided by quantize.js to cluster similar
|
||||
* colors and return the base color from the largest cluster.
|
||||
* @param {HTMLImageElement} sourceImage:HTMLImageElement
|
||||
* @param {number} quality Quality is an optional argument. It needs to be an integer. 1 is the highest quality settings.
|
||||
* 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
|
||||
* faster a color will be returned but the greater the likelihood that it will not be the visually
|
||||
* most dominant color.
|
||||
* @returns {ColorThiefResult} returns {r: num, g: num, b: num}
|
||||
*/
|
||||
getColor(
|
||||
sourceImage: HTMLImageElement,
|
||||
quality: number = 10,
|
||||
): ColorThiefResult {
|
||||
const palette = this.getPalette(sourceImage, 5, quality);
|
||||
return palette[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the median cut algorithm provided by quantize.js to cluster similar colors.
|
||||
* @param {HTMLImageElement} sourceImage The image you want to have processed, as an HTMLImageElement
|
||||
* @param {number?} colorCount colorCount determines the size of the palette; the number of colors returned. If not set, it
|
||||
* defaults to 10.
|
||||
* @param {number?} quality quality is an optional argument. It needs to be an integer. 1 is the highest quality settings.
|
||||
* 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
|
||||
* faster the palette generation but the greater the likelihood that colors will be missed.
|
||||
* @returns {ColorThiefResult[]} returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
|
||||
*/
|
||||
getPalette(
|
||||
sourceImage: HTMLImageElement,
|
||||
colorCount?: number,
|
||||
quality: number = 10,
|
||||
): ColorThiefResult[] {
|
||||
const options = core.validateOptions({
|
||||
colorCount,
|
||||
quality,
|
||||
});
|
||||
|
||||
// Create custom CanvasImage object
|
||||
const image = new CanvasImage(sourceImage);
|
||||
const imageData = image.getImageData();
|
||||
const pixelCount = image.width * image.height;
|
||||
|
||||
const pixelArray = core.createPixelArray(
|
||||
imageData.data,
|
||||
pixelCount,
|
||||
options.quality,
|
||||
);
|
||||
|
||||
// Send array to quantize function which clusters values
|
||||
// using median cut algorithm
|
||||
const cmap = quantize(pixelArray, options.colorCount);
|
||||
const palette = cmap ? cmap.palette() : null;
|
||||
|
||||
return palette;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @param {any} imageUrl
|
||||
* @param {any} callback
|
||||
* @param {any} quality
|
||||
* @returns {any}
|
||||
*/
|
||||
getColorFromUrl(imageUrl, callback, quality) {
|
||||
const sourceImage = document.createElement('img');
|
||||
|
||||
sourceImage.addEventListener('load', () => {
|
||||
const palette = this.getPalette(sourceImage, 5, quality);
|
||||
const dominantColor = palette[0];
|
||||
callback(dominantColor, imageUrl);
|
||||
});
|
||||
sourceImage.src = imageUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @param {string} imageUrl
|
||||
* @param {string} callback
|
||||
* @returns {void}
|
||||
*/
|
||||
getImageData(imageUrl: string, callback) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', imageUrl, true);
|
||||
xhr.responseType = 'arraybuffer';
|
||||
|
||||
fetch(imageUrl).then((res) => {
|
||||
if (res.status === 200) {
|
||||
res.arrayBuffer().then((response) => {
|
||||
const uInt8Array = new Uint8Array(response);
|
||||
let binaryString = new Array(uInt8Array.length);
|
||||
for (let i = 0; i < uInt8Array.length; i++) {
|
||||
binaryString[i] = String.fromCharCode(uInt8Array[i]);
|
||||
}
|
||||
let data = binaryString.join('');
|
||||
let base64 = window.btoa(data);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
xhr.onload = function () {
|
||||
if (this.status == 200) {
|
||||
let uInt8Array = new Uint8Array(this.response);
|
||||
|
||||
callback('data:image/png;base64,' + base64);
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
* @param {any} imageUrl
|
||||
* @param {any} callback
|
||||
* @param {any} quality
|
||||
* @returns {any}
|
||||
*/
|
||||
getColorAsync = function (imageUrl, callback, quality) {
|
||||
const thief = this;
|
||||
this.getImageData(imageUrl, function (imageData) {
|
||||
const sourceImage = document.createElement('img');
|
||||
sourceImage.addEventListener('load', function () {
|
||||
const palette = thief.getPalette(sourceImage, 5, quality);
|
||||
const dominantColor = palette[0];
|
||||
callback(dominantColor, this);
|
||||
});
|
||||
sourceImage.src = imageData;
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default ColorThief;
|
||||
18
src/core.js
18
src/core.js
@@ -24,24 +24,30 @@ function validateOptions(options) {
|
||||
|
||||
if (typeof colorCount === 'undefined' || !Number.isInteger(colorCount)) {
|
||||
colorCount = 10;
|
||||
} else if (colorCount === 1 ) {
|
||||
throw new Error('colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()');
|
||||
} else if (colorCount === 1) {
|
||||
throw new Error(
|
||||
'colorCount should be between 2 and 20. To get one color, call getColor() instead of getPalette()',
|
||||
);
|
||||
} else {
|
||||
colorCount = Math.max(colorCount, 2);
|
||||
colorCount = Math.min(colorCount, 20);
|
||||
}
|
||||
|
||||
if (typeof quality === 'undefined' || !Number.isInteger(quality) || quality < 1) {
|
||||
if (
|
||||
typeof quality === 'undefined' ||
|
||||
!Number.isInteger(quality) ||
|
||||
quality < 1
|
||||
) {
|
||||
quality = 10;
|
||||
}
|
||||
|
||||
return {
|
||||
colorCount,
|
||||
quality
|
||||
}
|
||||
quality,
|
||||
};
|
||||
}
|
||||
|
||||
export default {
|
||||
createPixelArray,
|
||||
validateOptions
|
||||
validateOptions,
|
||||
};
|
||||
|
||||
0
src/core.ts
Normal file
0
src/core.ts
Normal file
Reference in New Issue
Block a user