├── .czrc
├── assets
└── color.png
├── Picture2color.xmind
├── examples
├── assets
│ ├── web.jpg
│ ├── color.png
│ ├── pic9.jpeg
│ └── style.css
├── index.html
├── dev-demo.html
└── demo
│ ├── color-drage.html
│ ├── color-picker.html
│ ├── color-deep.html
│ ├── color-point.html
│ ├── color-similar.html
│ └── color-analyse.html
├── .gitignore
├── .editorconfig
├── src
├── index.js
├── types
│ └── index.js
├── utils
│ ├── paramsFilter.js
│ └── index.js
└── lib
│ ├── Count.js
│ ├── Point.js
│ ├── Color.js
│ ├── Analyse.js
│ ├── ColorGroup.js
│ └── main.js
├── .babel.config.js
├── LICENSE
├── dist
├── worker.js
└── index.js
├── package.json
├── CHANGELOG.md
└── README.md
/.czrc:
--------------------------------------------------------------------------------
1 | { "path": "cz-conventional-changelog" }
--------------------------------------------------------------------------------
/assets/color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wanxiaodong404/picture2color/HEAD/assets/color.png
--------------------------------------------------------------------------------
/Picture2color.xmind:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wanxiaodong404/picture2color/HEAD/Picture2color.xmind
--------------------------------------------------------------------------------
/examples/assets/web.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wanxiaodong404/picture2color/HEAD/examples/assets/web.jpg
--------------------------------------------------------------------------------
/examples/assets/color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wanxiaodong404/picture2color/HEAD/examples/assets/color.png
--------------------------------------------------------------------------------
/examples/assets/pic9.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wanxiaodong404/picture2color/HEAD/examples/assets/pic9.jpeg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .git
2 | .idea
3 | .vscode
4 | .DS_Store
5 | package-lock.json
6 | yarn.lock
7 | yarn-error.log
8 | */**/.DS_Store
9 | **/.DS_Store
10 | /.DS_Store
11 | node_modules
12 |
13 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = false
7 | trim_trailing_whitespace = true
8 | indent_style = space
9 | indent_size = 4
10 | # end_of_line = crlf
11 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-05-12 17:35:04
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2021-09-08 17:58:34
6 | * @Description:
7 | */
8 |
9 | const Picture2color = require('./lib/main')
10 |
11 | module.exports = Picture2color
--------------------------------------------------------------------------------
/src/types/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-11-30 14:25:18
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2020-12-14 15:50:37
6 | * @Description:
7 | */
8 |
9 | /**
10 | * 颜色值输出类型
11 | */
12 | const COLORTYPE = {
13 | HEXCOLOR: 'HEX',
14 | RGBACOLOR: 'RGBA'
15 | }
16 |
17 |
18 | module.exports = {
19 | ...COLORTYPE
20 | }
--------------------------------------------------------------------------------
/.babel.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-09-18 17:51:04
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2020-09-18 18:08:52
6 | * @Description:
7 | */
8 | module.exports = {
9 | sourceType: 'unambiguous',
10 | presets: ['@babel/preset-env'],
11 | plugins: [
12 | '@babel/transform-runtime',
13 | ['@babel/plugin-proposal-class-properties', {"loose": false}],
14 | '@babel/plugin-syntax-class-properties'
15 | ]
16 | }
--------------------------------------------------------------------------------
/src/utils/paramsFilter.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-11-30 14:45:57
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2021-01-26 12:15:18
6 | * @Description: 处理各个类的参数
7 | */
8 |
9 |
10 | function analy(params) {
11 | return Object.assign({}, params)
12 | }
13 | function color(params) {
14 | return Object.assign({}, params)
15 | }
16 | function group(params) {
17 | return Object.assign({}, params)
18 | }
19 | function count(params) {
20 | return Object.assign({}, params)
21 | }
22 | function point(params) {
23 | return Object.assign({}, params)
24 | }
25 | function main(params) {
26 | return Object.assign({}, params)
27 | }
28 | module.exports = {
29 | analy,
30 | color,
31 | group,
32 | count,
33 | point,
34 | main
35 | }
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Picture2color
9 |
10 |
11 |
12 |
13 |
14 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/lib/Count.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-11-26 16:55:46
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2020-12-23 15:52:56
6 | * @Description: 用于统计count
7 | */
8 |
9 | class Count {
10 | constructor(base = 0) {
11 | this.__count = base;
12 | this.__children = [];
13 | this.__parent = null;
14 | }
15 |
16 | resetCount(count = 0) {
17 | this.__count = count
18 | }
19 |
20 | append(item) {
21 | this.__children.push(item)
22 | item.__parent = this;
23 | this.plus(item.__count)
24 | }
25 |
26 | plus(count = 1) {
27 | this.__count += count
28 | if (this.__parent) {
29 | this.__parent.plus(count)
30 | }
31 | }
32 |
33 | get count() {
34 | return this.__count
35 | }
36 |
37 | get percent() {
38 | if (this.__parent) {
39 | return this.__count / this.__parent.__count * 100
40 | } else {
41 | return 100
42 | }
43 | }
44 |
45 | }
46 |
47 | module.exports = Count
--------------------------------------------------------------------------------
/src/lib/Point.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-11-27 13:35:10
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2021-01-26 11:12:51
6 | * @Description:
7 | */
8 | const utils = require("../utils")
9 | const paramsFilter = require('../utils/paramsFilter')
10 | const defaultOption = {
11 | index: 0,
12 | width: 0,
13 | height: 0,
14 | name: undefined
15 | }
16 | class Point {
17 | constructor(data, option = {}) {
18 | this.option = paramsFilter.point(Object.assign({}, defaultOption, option));
19 | let {index, width, height, name} = this.option
20 | this.data = data
21 | this.index = index
22 | this.width = width
23 | this.height = height
24 | this.__position = null
25 | this.__colorName = name || null
26 | }
27 | get position() {
28 | return this.__position || (this.__position = utils.index2position(this.index, this.width))
29 | }
30 | get colorName() {
31 | return this.__colorName || (this.__colorName = utils.data2color(this.data))
32 | }
33 | }
34 |
35 | module.exports = Point
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 wanxiaodong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/dist/worker.js:
--------------------------------------------------------------------------------
1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("Picture2color",[],t):"object"==typeof exports?exports.Picture2color=t():e.Picture2color=t()}(this,(function(){return function(e){var t={};function r(o){if(t[o])return t[o].exports;var n=t[o]={i:o,l:!1,exports:{}};return e[o].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=e,r.c=t,r.d=function(e,t,o){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(r.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)r.d(o,n,function(t){return e[t]}.bind(null,n));return o},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=11)}({11:function(e,t){console.log("worker")}})}));
--------------------------------------------------------------------------------
/src/lib/Color.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-10-19 16:25:49
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2021-09-08 18:02:21
6 | * @Description:
7 | * @Focus: 注意:如果是需要新增属性和方法,请确认是否需要在ColorGroup的代理中进行设置
8 | */
9 |
10 | const Count = require('./Count');
11 | const utils = require('../utils')
12 | const types = require('../types')
13 | const paramsFilter = require('../utils/paramsFilter')
14 | const defaultOptionColor = {
15 | deepStep: 120,
16 | value: undefined, // 优化字符串处理速度可传入提前处理好的name utils.data2color(data)
17 | type: types.RGBACOLOR
18 | }
19 | const groupMap = new Map()
20 | class Color extends Count {
21 | constructor(data, option = {}) {
22 | super(1) // count = 1
23 | if (data instanceof Color) return data;
24 | this.option = paramsFilter.color(Object.assign({}, defaultOptionColor, option));
25 | this.data = data;
26 | this.__colorName = option.value || null; // 颜色名
27 | }
28 | /**
29 | * 颜色深浅
30 | */
31 | get isDeep() {
32 | return utils.isDeep(this, this.option.deepStep)
33 | }
34 | /**
35 | * 获取颜色rgba名称,有缓存以及懒加载作用
36 | */
37 | get value() {
38 | return this.__colorName || (this.__colorName = this.toColorString())
39 | }
40 | /**
41 | * 转换不同的color string
42 | * @param {*} type
43 | */
44 | toColorString(type = types.RGBACOLOR) {
45 | return utils.data2color(this.data, this.option.type || type)
46 | }
47 | /**
48 | * 克隆color对象
49 | * @param {*} color
50 | * @param {*} option
51 | * @param {*} gid
52 | */
53 | static clone(color, option) {
54 | let _color = new Color(color.data, utils.paramsFilter.color({...color.option, value: color.__colorName, ...option}))
55 | _color.resetCount(color.__count)
56 | return _color
57 | }
58 | }
59 |
60 | module.exports = Color
--------------------------------------------------------------------------------
/examples/assets/style.css:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-10-26 11:38:20
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2020-12-14 17:35:06
6 | * @Description:
7 | */
8 |
9 |
10 | * {
11 | box-sizing: border-box;
12 | }
13 | .container {
14 | display: block;
15 | width: 100%;
16 | padding: 0 15px;
17 | }
18 | img {
19 | display: block;
20 | max-width: 100%;
21 | margin: 0 auto;
22 | }
23 |
24 | iframe {
25 | display: block;
26 | width: 100%;
27 | min-height: 80vh;
28 | border: 1px solid #eee;
29 | border-radius: 4px;
30 | padding: 25px 0;
31 | }
32 | .menu {
33 | display: flex;
34 | justify-content: flex-start;
35 | align-items: center;
36 | flex-wrap: wrap;
37 | }
38 | .menu-item {
39 | display: block;
40 | width: 50%;
41 | color: #666;
42 | font-size: 16px;
43 | font-weight: 600;
44 | transition: all .2s linear;
45 | line-height: 32px;
46 | border-radius: 2px;
47 | padding: 5px 15px;
48 | }
49 | .menu-item:hover {
50 | background-color: #eee;
51 | color: #333;
52 | }
53 |
54 | .demo-block {
55 | background-color: #efefef;
56 | border-radius: 4px;
57 | padding: 15px;
58 | margin: 15px 0;
59 | }
60 | .demo-block img {
61 | max-height: 300px;
62 | }
63 | .demo-block pre {
64 | background-color: #fff;
65 | border-radius: 4px;
66 | padding: 10px;
67 | overflow-x: auto;
68 | }
69 |
70 | .demo-block .button {
71 | width: auto;
72 | display: block;
73 | margin: 10px auto 0 auto;
74 | text-align: center;
75 | font-size: 14px;
76 | color: #333;
77 | padding: 0 15px;
78 | border: 1px solid #999;
79 | border-radius: 3px;
80 | -webkit-user-select: auto;
81 | cursor: pointer;
82 | }
83 |
84 | .demo-title {
85 | color: #333;
86 | font-size: 18px;
87 | font-weight: bold;
88 | line-height: 24px;
89 | }
90 | .demo-sub-title {
91 | color: #666;
92 | font-size: 14px;
93 | font-weight: 500;
94 | line-height: 20px;
95 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "picture2color",
3 | "version": "0.1.06",
4 | "description": "图片颜色获取, 颜色识别,颜色分析,取色",
5 | "keywords": [
6 | "图片",
7 | "颜色",
8 | "颜色识别",
9 | "颜色提取",
10 | "图片颜色",
11 | "rgba",
12 | "rgb",
13 | "picture",
14 | "color",
15 | "color picker",
16 | "picture2color"
17 | ],
18 | "main": "./src/index.js",
19 | "scripts": {
20 | "build": "webpack --config ./build/webpack.build.prod.js",
21 | "dev": "webpack-dev-server --config ./build/webpack.build.dev.js -w",
22 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -w -r 0 -s",
23 | "test": "echo \"Error: no test specified\" && exit 1"
24 | },
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/sheldonWan/picture2color.git"
28 | },
29 | "config": {
30 | "commitizen": {
31 | "extends": [
32 | "cz-conventional-changelog"
33 | ]
34 | }
35 | },
36 | "husky": {
37 | "hooks": {
38 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
39 | }
40 | },
41 | "author": "wanxiaodong",
42 | "license": "MIT",
43 | "bugs": {
44 | "url": "https://github.com/sheldonWan/picture2color/issues"
45 | },
46 | "homepage": "https://github.com/sheldonWan/picture2color#readme",
47 | "dependencies": {
48 | "events": "^3.2.0"
49 | },
50 | "devDependencies": {
51 | "@babel/core": "^7.11.6",
52 | "@babel/plugin-proposal-class-properties": "^7.10.4",
53 | "@babel/plugin-syntax-class-properties": "^7.10.4",
54 | "@babel/plugin-transform-runtime": "^7.11.5",
55 | "@babel/preset-env": "^7.11.5",
56 | "babel-loader": "^8.1.0",
57 | "babel-runtime": "^6.26.0",
58 | "cz-conventional-changelog": "^3.3.0",
59 | "html-webpack-plugin": "^4.4.1",
60 | "husky": "^7.0.2",
61 | "webpack": "^4.43.0",
62 | "webpack-cli": "^3.3.12",
63 | "webpack-dev-server": "^3.11.0"
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/examples/dev-demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Picture2color demo
7 |
8 |
24 |
25 |
26 |
27 |
28 |
主颜色分析
29 |
分析整张图片主要颜色占比
30 |
31 |
32 |
33 |
切换 colorProxy <==> Color
34 |
35 |

36 |
48 |
49 |
50 |
51 |
62 |
63 |
--------------------------------------------------------------------------------
/examples/demo/color-drage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Picture2color
9 |
10 |
11 |
27 |
28 |
29 |
30 |
31 |
获取部分边框颜色配置背景色
32 |
分析整张图片边框向内部分颜色主要占比
33 |
34 |
50 |
51 |
52 |
53 |
64 |
65 |
--------------------------------------------------------------------------------
/examples/demo/color-picker.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Picture2color
9 |
10 |
11 |
27 |
28 |
29 |
30 |
31 |
颜色吸取
32 |
颜色提取工具
33 |
34 |
50 |
51 |
52 |
53 |
64 |
65 |
--------------------------------------------------------------------------------
/examples/demo/color-deep.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Picture2color
9 |
10 |
11 |
27 |
28 |
29 |
30 |
31 |
深浅色判定
32 |
判断颜色是否是深色
33 |
34 |
切换
35 |
52 |
53 |
54 |
55 |
66 |
67 |
--------------------------------------------------------------------------------
/examples/demo/color-point.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Picture2color
9 |
10 |
11 |
27 |
28 |
29 |
30 |
31 |
查询颜色坐标(bate功能)
32 |
查询颜色坐标
33 |
34 |
35 |
36 |
37 |
56 |
57 |
58 |
59 |
70 |
71 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.1.6](https://github.com/sheldonWan/picture2color/compare/v0.1.3...v0.1.6) (2021-09-09)
2 |
3 |
4 | ### Features
5 |
6 | * 静态方法优化 ([e4edb5d](https://github.com/sheldonWan/picture2color/commit/e4edb5da8c174c292042bff2c0552af9b7a7005f))
7 | * build ([91cb7c7](https://github.com/sheldonWan/picture2color/commit/91cb7c72d106f4a4d938ab44eadc5c5912302d2e))
8 | * new version ([da94a96](https://github.com/sheldonWan/picture2color/commit/da94a96ce09c16fe1ed3028ca28b147af4210a1f))
9 |
10 |
11 |
12 | ## [0.1.3](https://github.com/sheldonWan/picture2color/compare/v0.1.2...v0.1.3) (2021-01-26)
13 |
14 |
15 | ### Bug Fixes
16 |
17 | * import ([7240d0c](https://github.com/sheldonWan/picture2color/commit/7240d0c3b1fdd61955116e1d5829e5ffe5ceb4ae))
18 |
19 |
20 |
21 | ## [0.1.2](https://github.com/sheldonWan/picture2color/compare/v0.1.1...v0.1.2) (2021-01-26)
22 |
23 |
24 |
25 | ## [0.1.1](https://github.com/sheldonWan/picture2color/compare/v0.1.0...v0.1.1) (2020-12-23)
26 |
27 |
28 | ### Bug Fixes
29 |
30 | * fixed count bug ([88fb927](https://github.com/sheldonWan/picture2color/commit/88fb927fd34cd2f165dcebbe962b7327bee49b6b))
31 | * fixed count bug ([829c1dc](https://github.com/sheldonWan/picture2color/commit/829c1dcce3be8aa91811da0e325bc2fc78878147))
32 |
33 |
34 | ### Features
35 |
36 | * keyword ([a117dbe](https://github.com/sheldonWan/picture2color/commit/a117dbe84f522205b30ad0dad0cd3bcafa08ec6d))
37 |
38 |
39 |
40 | # [0.1.0](https://github.com/sheldonWan/picture2color/compare/v0.0.10...v0.1.0) (2020-12-23)
41 |
42 |
43 | ### Bug Fixes
44 |
45 | * hex string bug fixed ([152ae7c](https://github.com/sheldonWan/picture2color/commit/152ae7cc80bb28a63d12248fa0263e8bc28ccd8e))
46 |
47 |
48 | ### Features
49 |
50 | * 添加types和paramsFilter ([1540475](https://github.com/sheldonWan/picture2color/commit/1540475e64fbf679e28c6c7165a1d224e2b796f9))
51 | * 重新规划0.1.0版本 ([2c7861f](https://github.com/sheldonWan/picture2color/commit/2c7861f87e89baec82dc5e189ddc4f581967488c))
52 | * 重新设计color proxy ([7581427](https://github.com/sheldonWan/picture2color/commit/758142795585a103984f94dbfcc82e76a485ad54))
53 |
54 |
55 |
56 | ## [0.0.10](https://github.com/sheldonWan/picture2color/compare/v0.0.9...v0.0.10) (2020-11-27)
57 |
58 |
59 | ### Features
60 |
61 | * 添加changelog ([fa7aa18](https://github.com/sheldonWan/picture2color/commit/fa7aa18b8c21810e5fd9c80e898591332ab93094))
62 | * 新添加point类 ([e45f673](https://github.com/sheldonWan/picture2color/commit/e45f673712c75b4c5e2b1638c412a2fd35b74283))
63 | * 新增异步执行参数以及changlog调整 ([a416ee9](https://github.com/sheldonWan/picture2color/commit/a416ee9429c9b9db8fd4cfd2fe37d95275fd1534))
64 | * 新增demo ([6a89dc3](https://github.com/sheldonWan/picture2color/commit/6a89dc3afcb71bd24ad2b56e47f5c993d8c542d9))
65 |
66 |
67 |
68 | ## [0.0.9](https://github.com/sheldonWan/picture2color/compare/v0.0.7...v0.0.9) (2020-11-26)
69 |
70 |
71 |
72 | ## [0.0.7](https://github.com/sheldonWan/picture2color/compare/v0.0.6...v0.0.7) (2020-10-26)
73 |
74 |
75 |
76 | ## [0.0.6](https://github.com/sheldonWan/picture2color/compare/v0.0.5...v0.0.6) (2020-10-26)
77 |
78 |
79 |
80 | ## [0.0.5](https://github.com/sheldonWan/picture2color/compare/0.0.4...v0.0.5) (2020-10-22)
81 |
82 |
83 |
84 | ## [0.0.4](https://github.com/sheldonWan/picture2color/compare/0.0.3...0.0.4) (2020-10-22)
85 |
86 |
87 |
88 | ## 0.0.3 (2020-10-22)
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-10-19 16:38:20
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2021-09-08 18:47:17
6 | * @Description:
7 | */
8 |
9 | const types = require('../types')
10 | const paramsFilter = require('./paramsFilter')
11 | const Color = require('../lib/Color')
12 |
13 | module.exports = {
14 | /**
15 | * 颜色是否相似
16 | * @param {Color} color1
17 | * @param {Color} color2
18 | * @param {number} step
19 | */
20 | isSimilarColor(color1, color2, step = 10) {
21 | color1 = typeof color1 === 'string' ? dataInputFormat(color1) : color1;
22 | color2 = typeof color2 === 'string' ? dataInputFormat(color2) : color2;
23 | let [r1, g1, b1, a1] = Array.isArray(color1) ? color1 : color1.data
24 | let [r2, g2, b2, a2] = Array.isArray(color2) ? color2 : color2.data
25 | a1 = a1 / 255;
26 | a2 = a2 / 255
27 | return Math.sqrt(Math.pow((r1 * a1 - r2 * a2), 2) + Math.pow((g1 * a1 - g2 * a2), 2) + Math.pow((b1 * a1 - b2 * a2), 2)) < step
28 | },
29 | /**
30 | * 是否属于深色
31 | * @param {Color} color
32 | * @param {number} deepStep 0-255
33 | */
34 | isDeep(color, deepStep = 192) {
35 | color = typeof color === 'string' ? dataInputFormat(color) : color;
36 | let [r, g, b, a] = color.data
37 | a = a / 255
38 | return r * a * 0.299 + g * a * 0.587 + b * a * 0.114 < deepStep
39 | },
40 | /**
41 | * 生成唯一id
42 | */
43 | getUniqueId(len = 10) {
44 | return Number(Math.random().toString().split('.')[1].substring(0, len)).toString(32)
45 | },
46 | /**
47 | * color数据转rgba
48 | * @param {*} data
49 | */
50 | data2color(data, type = types.RGBACOLOR) {
51 | try {
52 | let color;
53 | switch (type) {
54 | case types.HEXCOLOR: {
55 | let [r, g, b, a] = data || [];
56 | a = a / 255
57 | r = (r * a).toString(16);
58 | g = (g * a).toString(16);
59 | b = (b * a).toString(16);
60 | r = r.length > 1 ? r : `0${r}`;
61 | g = g.length > 1 ? g : `0${g}`;
62 | b = b.length > 1 ? b : `0${b}`;
63 | let str = ['#', r, g, b]
64 | color = str.join('');
65 | break
66 | }
67 | default: {
68 | // types.RGBACOLOR
69 | let [r, g, b, a] = data || [];
70 | color = `rgba(${r},${g},${b},${a / 255})`;
71 | break
72 | }
73 | }
74 | return color;
75 | } catch (e) {
76 | return ''
77 | }
78 | },
79 | /**
80 | * 通过色值转rgba数组
81 | * @param {*} color
82 | */
83 | color2data: color2data,
84 |
85 | index2position(index, width = 1) {
86 | return [index % width, Math.ceil(index / width)]
87 | },
88 |
89 | paramsFilter
90 | }
91 |
92 |
93 |
94 | /**
95 | * 颜色输入格式化
96 | */
97 | function dataInputFormat(color) {
98 | if (typeof color === 'object') {
99 | return color
100 | } else {
101 | return color2data(color);
102 | }
103 | }
104 |
105 | /**
106 | * 字符串转color data
107 | * @param {*} color
108 | * @returns
109 | */
110 | function color2data(color) {
111 | try {
112 | if (/^rgba?\(.+\)/i.test(color)) {
113 | let data = color.match(/([\d.]+)/g);
114 | if (!data || data.length < 3) throw Error('请传入正确的值')
115 | if (data.length === 3) data.push('1');
116 | let opacity = data.pop();
117 | data.push(opacity * 255);
118 | return data.slice(0, 4).map(item => Number(item));
119 | } else if (/^#(\w+)/ig) {
120 | let data = color.match(/^#(\w+)/);
121 | data = data ? data[1] : '';
122 | if (!data) throw Error('请传入正确的值');
123 | if (data.length === 3) {
124 | return [parseInt(`0x${data[0].repeat(2)}`), parseInt(`0x${data[1].repeat(2)}`), parseInt(`0x${data[2].repeat(2)}`), 255]
125 | } else if (data.length >= 6) {
126 | data = data.substring(0, 6)
127 | return [parseInt(`0x${data.substring(0, 2)}`), parseInt(`0x${data.substring(2, 4)}`), parseInt(`0x${data.substring(4, 6)}`), 255]
128 | } else {
129 | if (!data) throw Error('请传入正确的值')
130 | }
131 | }
132 | } catch (e) {
133 | console.warn('请传入正确的色值')
134 | }
135 | }
--------------------------------------------------------------------------------
/src/lib/Analyse.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-10-19 16:27:03
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2021-01-26 11:11:40
6 | * @Description: 色值分析
7 | */
8 |
9 | const Color = require("./Color");
10 | const ColorGroup = require("./ColorGroup");
11 | const Point = require('./Point')
12 | const utils = require("../utils");
13 | const paramsFilter = require('../utils/paramsFilter')
14 | const types = require('../types')
15 |
16 |
17 | const defaultOptionColorAnalyse = {}
18 | class ColorAnalyse {
19 | constructor(colorData, option = {}) {
20 | this.option = paramsFilter.analy(Object.assign({}, defaultOptionColorAnalyse, option))
21 | this.originData = colorData;
22 | let {width, height} = colorData;
23 | this.width = width
24 | this.height = height
25 | let {colorGroup, translateData} = this.analyse(colorData);
26 | this.colorGroup = colorGroup;
27 | this.translateData = translateData;
28 | }
29 | /**
30 | * 颜色数据分析
31 | * @param {*} data
32 | */
33 | analyse(data) {
34 | let {width, height, data: _data} = data;
35 | data = this.colorFormat(_data, width, height);
36 | return {
37 | translateData: {width, height, data},
38 | colorGroup: this.createColorGroup(data)
39 | }
40 | }/**
41 | * 格式化颜色数据
42 | * @param {*} data
43 | */
44 | colorFormat(data, width, height) {
45 | let list = [],
46 | index = 0;
47 | data = Array.from(data)
48 | while (index * 4 <= data.length - 4) {
49 | let _data = data.slice(index * 4, (index + 1) * 4);
50 | list.push(new Point(_data, {index, width, height}))
51 | index++
52 | }
53 | return list
54 | }
55 | /**
56 | * 创建一个颜色组
57 | * @param {*} data
58 | */
59 | createColorGroup(data) {
60 | let group = new ColorGroup();
61 | data.forEach(item => {
62 | group.concat(item.data, utils.paramsFilter.group({...this.option, value: item.colorName}))
63 | })
64 | return group
65 | }
66 | /**
67 | * 以边框向内方向获取范围主要色值
68 | * @param {*} option
69 | * @param {*} colorAnalyse
70 | */
71 | getFrameColorGroup(option = {}, colorAnalyse) {
72 | let {width, height, translateData} = colorAnalyse || this;
73 | let {size = 0.2} = Object.assign({}, this.option, option)
74 | let leftX = size * width,
75 | rightX = (1 - size) * width,
76 | topY = size * height,
77 | bottomY = (1 - size) * height;
78 | let _data = translateData.data.filter((item, index) => {
79 | let _x = index % width,
80 | _y = Math.ceil(index / width);
81 | return (_x <= leftX || _x >= rightX) && (_y >= bottomY || _y <= topY)
82 | });
83 | return this.createColorGroup(_data)
84 | }
85 | /**
86 | * 根据坐标获取颜色
87 | * @param {*} x
88 | * @param {*} y
89 | * @param {*} colorAnalyse
90 | */
91 | getPositionColor(x, y, colorAnalyse) {
92 | let {width, translateData} = colorAnalyse || this;
93 | let data = translateData.data[y * width + x]
94 | return this.colorGroup.get(data.colorName)
95 | }
96 | /**
97 | * 颜色筛选 性能问题 暂时不对外开放
98 | * @param {Color | [rgba]} color
99 | */
100 | colorFilter_bate(color) {
101 | let {translateData, colorGroup} = this;
102 | let {width, data} = translateData
103 | let {colorType} = this.option
104 | if (color instanceof Color) {
105 | return data.filter((item) => {
106 | return item.colorName === color.value
107 | })
108 | } else if(color.isGroup) {
109 | let list = color.__children.map(item => this.colorFilter_bate(item))
110 | return list.reduce((list, item) => {
111 | return list.concat(...item)
112 | }, [])
113 | } else if (Array.isArray(color)) {
114 | return data.filter((item) => {
115 | return item.colorName === utils.data2color(color, colorType)
116 | })
117 | } else {
118 | // rgba(0,0,0,1)
119 | return data.filter((item) => {
120 | return item.colorName === color
121 | })
122 | }
123 | }
124 | /**
125 | * 销毁
126 | */
127 | destory() {
128 | this.option = null;
129 | this.originData = null;
130 | this.colorGroup = null;
131 | this.mainColor = null;
132 | this.width = null;
133 | this.height = null;
134 | }
135 | }
136 |
137 | module.exports = ColorAnalyse
--------------------------------------------------------------------------------
/src/lib/ColorGroup.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-11-26 14:50:42
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2021-01-26 13:59:18
6 | * @Description:
7 | */
8 | const Color = require('./Color');
9 | const Count = require('./Count');
10 | const utils = require('../utils');
11 | const paramsFilter = require('../utils/paramsFilter')
12 | class ColorGroup extends Count {
13 | constructor(child) {
14 | super();
15 | this.gid = utils.getUniqueId();
16 | this.isGroup = true; // 用于判定color和group
17 | this.__proxy = child || null; // 如果是颜色范围的话,可设置一个颜色代表用以代表这个颜色范围
18 | this.__pool = new Map();
19 | if (child) {
20 | this.concat(child)
21 | // 代理一些Color类上的方法和属性,注意方法是否需要bind
22 | let proxyList = new Set(['value', 'data', 'isDeep', 'toColorString'])
23 | return new Proxy(this, {
24 | get(target, key) {
25 | if (proxyList.has(key)) {
26 | return child[key]
27 | } else {
28 | return target[key]
29 | }
30 | }
31 | })
32 | }
33 | }
34 | /**
35 | * 追加颜色到色池
36 | * @param {Color} color
37 | */
38 | pushPool(color) {
39 | if (color instanceof Color) {
40 | this.__pool.set(color.value, color);
41 | this.append(color)
42 | } else if (color.isGroup){
43 | this.__pool.set(color.__proxy.value, color);
44 | this.append(color)
45 | } else {
46 | throw('数据错误', color)
47 | }
48 | }
49 | /**
50 | * 向色组内追加颜色
51 | * @param {Group || Color || [rgba]} color
52 | * @return {Color}
53 | */
54 | concat(color, option) {
55 | let pool = this.__pool
56 | if (color instanceof Color) {
57 | let data = pool.get(color.value)
58 | if (data) {
59 | data.plus(color.__count)
60 | } else {
61 | this.pushPool(color)
62 | }
63 | } else if (color.isGroup) {
64 | let data = pool.get(color.__proxy.value)
65 | if (data) {
66 | data.plus(color.__count)
67 | } else {
68 | this.pushPool(color)
69 | }
70 | } else {
71 | let colorString = utils.data2color(color);
72 | let data = pool.get(colorString)
73 | if (data) {
74 | data.plus()
75 | } else {
76 | color = new Color(color, paramsFilter.color({
77 | ...option,
78 | value: colorString
79 | }), this)
80 | this.pushPool(color)
81 | }
82 | }
83 | return color
84 | }
85 | /**
86 | * 判断是否包含此颜色
87 | * @param {*} colorName
88 | */
89 | hasChild(colorName) {
90 | return this.__pool.has(colorName)
91 | }
92 |
93 | get(color) {
94 | if (color instanceof Color) {
95 | return this.__pool.get(color.value)
96 | } else if (Array.isArray(color)) {
97 | // rgba
98 | return this.__pool.get(utils.data2color(color))
99 | } else {
100 | return this.__pool.get(color)
101 | }
102 | }
103 | /**
104 | * 创建范围颜色代理
105 | * @param {*} option
106 | * @param {*} colorGroup
107 | */
108 | createColorProxy(option = {}, colorGroup) {
109 | colorGroup = colorGroup || this;
110 | if (colorGroup.__proxy) return this;
111 | let {colorStep = 100} = option;
112 | let map = new Set();
113 | colorGroup.sortList.forEach(item => {
114 | let hasNoColor = true;
115 | map.forEach((value, key) => {
116 | if (utils.isSimilarColor(value.__proxy, item, colorStep) && hasNoColor) {
117 | // 需要注意避免被重复判定为相似颜色
118 | hasNoColor = false
119 | item = Color.clone(item)
120 | value.concat(item, value);
121 | }
122 | })
123 | if (hasNoColor) {
124 | item = Color.clone(item)
125 | map.add(new ColorGroup(item))
126 | }
127 | })
128 | let _group = new ColorGroup();
129 | map.forEach((group) => {
130 | _group.concat(group)
131 | })
132 | return _group
133 | }
134 |
135 | get proxy() {
136 | return this.__proxy
137 | }
138 |
139 | get list() {
140 | return Array.from(this.__pool.values())
141 | }
142 |
143 | get sortList() {
144 | return this.list.sort((item1, item2) => item2.__count - item1.__count)
145 | }
146 | }
147 |
148 | module.exports = ColorGroup
--------------------------------------------------------------------------------
/examples/demo/color-similar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Picture2color
9 |
10 |
11 |
27 |
38 |
39 |
40 |
41 |
42 |
颜色范围判定
43 |
判定两个颜色是否在特定参数范围内属于相同颜色范围, mainColor和borderColor都是返回的近似颜色值
44 |
51 |
58 |
59 |
63 |
64 |
65 | 对比结果:
66 |
67 |
90 |
91 |
92 |
93 |
104 |
105 |
--------------------------------------------------------------------------------
/examples/demo/color-analyse.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Picture2color
9 |
10 |
11 |
27 |
28 |
29 |
30 |
31 |
主颜色分析
32 |
分析整张图片主要颜色占比
33 |
34 |
35 |
36 |
切换 colorProxy <==> Color
37 |
38 |
95 |
96 |
97 |
98 |
109 |
110 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [picture2color](./)
2 |
3 | [![npm version][npm-version-img]][npm-url]
4 | [![npm download][npm-download-img]][npm-download]
5 | [![MIT License][license-image]][license-url]
6 | [![issues-img]][issues]
7 | [![pr-img]][pr]
8 | [![size-img]][size-img]
9 | [![file-img]][file-img]
10 |
11 | ## 简介
12 | 此模块旨在解决前端在图片颜色识别上的问题,有问题可以联系
13 | [我][e-mail]或者提[issues][issues]
14 |
15 | 1、能够分析图片的主要颜色
16 |
17 | 2、根据坐标(像素/百分比)获取对应颜色
18 |
19 | 3、判定单个或者多个颜色深浅判定
20 |
21 | 4、对比两个颜色是否在参数程度范围内是否相似
22 |
23 | 5、获取颜色(或范围颜色)于颜色数据的占比
24 |
25 | 6、获取图片的全部颜色数据
26 |
27 | 7、按图片边框由外到内0-0.5占比的颜色数据
28 |
29 | 8、根据颜色数据创建范围颜色代理colorProxy
30 |
31 | ![img][demo-img]
32 |
33 | ## Demo
34 |
35 | [链接][demo]
36 |
37 | ## install
38 | ``` bash
39 | npm install --save picture2color
40 | ```
41 |
42 | ## import
43 |
44 | ```javascript
45 | import Picture2color from 'Picture2color'
46 | ```
47 |
48 | ## static API
49 |
50 | 1、是否是深色
51 | ```javascript
52 | /**
53 | * pa
54 | * @param {Color || ColorProxy} color
55 | * @param {Number} colorStep 值越小说明深色判定范围越小 默认192
56 | * @return Boolean
57 | */
58 | static isDeep(color, deepStep)
59 | ```
60 | 2、获取坐标位置传入图片的Color instance
61 | ```javascript
62 | /**
63 | * 获取图片坐标内色值
64 | * @param {*} x
65 | * @param {*} y
66 | * @param {*} image
67 | * @param {*} isPiex
68 | * @return Color
69 | */
70 | static getImageColor(x, y, image, isPiex)
71 | ```
72 | 3、判定
73 | ```javascript
74 | /**
75 | * 颜色是否在对应的范围内色值相似
76 | * @param {Color | [r,g,b,a] | colorProxy} color1
77 | * @param {Color | [r,g,b,a] | colorProxy} color2
78 | * @param {number 1++} colorStep 值越小范围越小,默认100
79 | */
80 | static isSimilarColor(color1, color2, colorStep)
81 | ```
82 | 4、颜色类(传入[r,g,b,a])实例化color对象
83 | ```javascript
84 | static Color
85 | ```
86 | ## 实例化
87 |
88 | 1、实例化模块
89 | ```javascript
90 | const image = new Image() || "http://url"
91 | const options = {
92 | async: false, // 是否异步化执行 传入图片为字符串链接也会默认转化为async执行
93 | event: ['click'], // 绑定事件获取颜色信息 然后通过emit=>color向外反馈
94 | deepStep: 192 // 判定深浅色程度,值越大深浅灵敏度越小 --Color
95 | }
96 | let demo = new Picture2color(image, options);
97 | demo instanceof Picture2color // true
98 | demo.on('color', () => {});
99 | ```
100 |
101 | ## 实例化API
102 |
103 | 1、获取当前图片主要色值(数据量大的时候计算时间会比较长)
104 |
105 | ```javascript
106 | /**
107 | * 获取图片占比主要颜色列表
108 | */
109 | instance.getColorGroup() // 返回ColorGroup实例
110 | ```
111 | 2、以边框为界限向内获取主要颜色列表(数据量大的时候计算时间会比较长)
112 | ```javascript
113 | /**
114 | * 以边框为界限向内获取0-0.5范围主要颜色列表
115 | * @param {*} option {size: 0-0.5}
116 | */
117 | instance.getFrameColorGroup() // 返回ColorGroup实例
118 | ```
119 |
120 | ## Color
121 |
122 | 1、实例化
123 | ```javascript
124 | /**
125 | * @params {[r,g,b,a]} rgba色值数组
126 | * @params {*} {deepStep: 0-255} 深色值 值越小说明深色判定范围越小
127 | */
128 | let colorInstance = new Picture2color.Color(data, option)
129 | ```
130 | 2、实例化API
131 |
132 | ```javascript
133 | colorInstance.value // rgba 色值
134 | colorInstance.isDeep // Boolean
135 | colorInstance.count // Number
136 | colorInstance.percent // Number 如果是按组生成的颜色可查看百分比
137 | ```
138 | ## ColorGroup
139 |
140 | 1、实例化
141 | ```javascript
142 | /**
143 | * @params {color} 参数传入color时会将color设置为group的proxy 将color属性代理到颜色组,为空时则纯作为颜色组
144 | * 所谓colorProxy就是将颜色差距极小的颜色归类到一组,然后取组内占比最大的的颜色属性作为代表属性,既包含colorGroup特性也包含color属性
145 | */
146 | let groupInstance = new Picture2color.ColorGroup()
147 | ```
148 | 2、实例化API
149 |
150 | ```javascript
151 | groupInstance.list // Array 颜色组包含的所有颜色列表
152 | groupInstance.sortList // Array 颜色组包含的所有颜色列表-降序排列
153 | groupInstance.count // Number
154 | groupInstance.percent // Number 如果是按组生成的颜色可查看百分比
155 |
156 | const option = {colorStep: 100}
157 | groupInstance.createColorProxy(option) // 将colorGroup生成一个新colorGrou,colorGroup内包的的是ColorProxy
158 | /****如果是colorProxy将额外获得Color实例的主要属性****/
159 |
160 | ```
161 | ## Count(Color和ColorGroup 都继承的类)
162 |
163 | 1、实例化
164 | ```javascript
165 | /**
166 | */
167 | let countInstance = new Count()
168 | ```
169 | 2、实例化API
170 |
171 | ```javascript
172 | countInstance.count // number 数量
173 | countInstance.percent // Number 百分比
174 | ```
175 |
176 | ## 小更新
177 | 1. isSimilarColor等静态方法支持字符串#fff、rgba(255,255,255)
178 |
179 | ## 未来可期
180 |
181 | 1. 同颜色坐标查找
182 | 2. 颜色范围坐标查找
183 | 3. serviceWorker解决性能问题
184 |
185 | [demo-img]: ./assets/color.png
186 | [demo]: https://wanxiaodong404.github.io/picture2color/examples/
187 |
188 | [npm-version-img]: https://img.shields.io/npm/v/picture2color
189 | [npm-url]: https://www.npmjs.com/package/picture2color
190 |
191 | [npm-download-img]: https://img.shields.io/npm/dw/picture2color.svg?style=flat
192 | [npm-download]: https://npmcharts.com/compare/picture2color?minimal=true
193 |
194 | [license-image]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat
195 | [license-url]: LICENSE
196 |
197 | [e-mail]: mailto://729779978@qq.com
198 |
199 | [issues-img]: https://img.shields.io/bitbucket/issues-raw/wanxiaodong404/picture2color
200 | [issues]: https://github.com/wanxiaodong404/picture2color/issues
201 |
202 |
203 | [size-img]: https://img.shields.io/badge/minified%20size-16%20kB-informational
204 | [file-img]: https://img.shields.io/badge/files-36-blue
205 | [pr-img]: https://img.shields.io/bitbucket/pr-raw/wanxiaodong404/picture2color
206 | [pr]: https://github.com/wanxiaodong404/picture2color/pr
207 |
--------------------------------------------------------------------------------
/src/lib/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wanxiaodong
3 | * @Date: 2020-10-19 16:36:09
4 | * @Last Modified by: wanxiaodong
5 | * @Last Modified time: 2021-09-09 10:12:16
6 | * @Description:
7 | */
8 | const events = require('events')
9 | const ColorAnalyse = require('./Analyse')
10 | const Color = require('./Color')
11 | const ColorGroup = require('./ColorGroup')
12 | const utils = require('../utils')
13 | const paramsFilter = require('../utils/paramsFilter')
14 | const types = require('../types')
15 | const defaultOption = {
16 | async: false, // 是否异步化执行 传入图片为字符串链接也会默认转化为async执行
17 | event: ['click'], // 绑定事件获取颜色信息 然后通过emit=>color向外反馈
18 | deepStep: 192 // 判定深浅色程度,值越大深浅灵敏度越小 --Color
19 | }
20 | class Picture2color extends events {
21 | constructor(image, option) {
22 | super();
23 | this.originColorData = null
24 | this.colorData = null;
25 | this.option = paramsFilter.main(Object.assign({}, defaultOption, (option || {})));
26 | this.__el = null;
27 | this.__cache = {};
28 | // this.utils = utils 不对外暴露
29 | if (this.option.async || typeof image === 'string') {
30 | return this.asyncInit(image)
31 | } else {
32 | if (image.complete) {
33 | this.init(image)
34 | } else {
35 | throw Error('图片尚未加载完成')
36 | }
37 | }
38 | }
39 | /**
40 | * 异步初始化
41 | * @param {*} image
42 | */
43 | asyncInit(image) {
44 | let that = this;
45 | return new Promise(function(resolve, reject) {
46 | if (typeof image === 'string') {
47 | let url = image;
48 | image = new Image();
49 | image.setAttribute('crossOrigin', 'anonymous');
50 | image.onload = () => {
51 | that.init(image)
52 | resolve(that)
53 | }
54 | image.onerror = () => {
55 | reject(new Error('图片加载失败'))
56 | }
57 | image.src = url
58 | } else {
59 | that.init(image)
60 | resolve(that)
61 | }
62 | })
63 | }
64 | init(image) {
65 | this.__el = image
66 | this.getImageColor = Picture2color.getImageColor;
67 | this.getColorData(image)
68 | this.destory = Picture2color.destory;
69 | this.option.event && this.bindEvent(this.option.event);
70 | }
71 | /**
72 | * 绑定事件
73 | * @{Array} 事件类型
74 | */
75 | bindEvent(eventType = []) {
76 | let that = this;
77 | let event = eventType.map((type) => {
78 | let callback = function(event) {
79 | var {offsetX, offsetY} = event;
80 | let color = that.getImageColor(offsetX, offsetY, that.__el)
81 | that.emit('color', {
82 | type,
83 | eventPosition: [offsetX, offsetY],
84 | color: color
85 | })
86 | };
87 | this.__el.addEventListener(type, callback)
88 | return {
89 | type,
90 | callback
91 | }
92 | });
93 | this.__cache.event = event
94 | }
95 | /**
96 | * 解绑事件
97 | */
98 | unbindEvent() {
99 | let that = this;
100 | if (this.__cache.event) {
101 | this.__cache.event.forEach(({type, callback}) => {
102 | that.__el.removeEventListener(type, callback)
103 | })
104 | }
105 | }
106 | /**
107 | * 获取图片的颜色数据
108 | * @param {*} image
109 | */
110 | getColorData(image) {
111 | let canvas = document.createElement('canvas');
112 | let {width, height} = image;
113 | canvas.width = width
114 | canvas.height = height
115 | this.__canvas = canvas
116 | let ctx = canvas.getContext('2d')
117 | ctx.drawImage(image, 0, 0);
118 | let data = ctx.getImageData(0, 0, width, height);
119 | this.originColorData = data;
120 | this.analyData = new ColorAnalyse(data, utils.paramsFilter.analy(this.option));
121 | }
122 | /**
123 | * 颜色筛选 性能问题 暂时不对外开放
124 | * @param {Color | [rgba]} color
125 | */
126 | colorFilter_bate(color) {
127 | return this.analyData.colorFilter_bate(color)
128 | }
129 | /**
130 | * 获取图片主要颜色列表
131 | */
132 | getColorGroup() {
133 | return this.analyData.colorGroup
134 | }
135 | /**
136 | * 以边框为界限向内获取0-0.5范围主要颜色列表
137 | * @param {*} option {size: 0-0.5}
138 | */
139 | getFrameColorGroup(option) {
140 | return this.analyData.getFrameColorGroup(option);
141 | }
142 | /**
143 | * 判断颜色或者颜色列表是否是深色
144 | * @param {Color || ColorList} colorList
145 | * @param {Number} deepStep
146 | */
147 | static isDeep(colorList, deepStep) {
148 | if (colorList instanceof Color) {
149 | return deepStep ? utils.isDeep(colorList, deepStep) : colorList.isDeep;
150 | }
151 | return colorList.reduce((percent, color) => {
152 | let isDeep = deepStep ? utils.isDeep(color, deepStep) : color.isDeep;
153 | return percent + (isDeep ? color.percent : 0)
154 | }, 0) > 50
155 | }
156 | /**
157 | * 销毁实例
158 | * @param {*} instance
159 | */
160 | static destory(instance) {
161 | instance = instance || this
162 | instance.unbindEvent()
163 | Object.keys(instance.__cache).forEach(key => {
164 | delete instance.__cache[key]
165 | })
166 | instance.__cache = null;
167 | instance.originColorData = null;
168 | instance.option = null;
169 | instance.__el = null;
170 | instance.off('.inner');
171 | instance.analyData && instance.colorData.destory();
172 | instance.colorData = null;
173 | }
174 | /**
175 | * 获取图片坐标内色值
176 | * @param {*} x
177 | * @param {*} y
178 | * @param {*} image
179 | * @param {*} isPiex
180 | */
181 | static getImageColor(x = 0, y = 0, image, isPiex = true) {
182 | try {
183 | if (this instanceof Picture2color) {
184 | let {width, height, naturalHeight, naturalWidth} = image;
185 | if (!isPiex) {
186 | // 百分比模式
187 | x = Math.round(x * width * 0.01);
188 | y = Math.round(y * height * 0.01);
189 | } else {
190 | x = Math.round(x / width * naturalWidth);
191 | y = Math.round(y / height * naturalHeight);
192 | }
193 | return this.analyData.getPositionColor(x, y)
194 | } else {
195 | let {width, height, naturalHeight, naturalWidth} = image;
196 | let canvas = document.createElement('canvas');
197 | canvas.width = width
198 | canvas.height = height
199 | let ctx = canvas.getContext('2d')
200 | ctx.drawImage(image, 0, 0, naturalWidth, naturalHeight, 0, 0, width, height);
201 | if (!isPiex) {
202 | // 百分比模式
203 | x = Math.round(x * width * 0.01);
204 | y = Math.round(y * height * 0.01);
205 | }
206 | let data = ctx.getImageData(x, y, 1, 1);
207 | let color = new Color(data.data)
208 | return color
209 | }
210 | } catch (e) {
211 | console.warn('获取数据失败')
212 | return {}
213 | }
214 | }
215 | /**
216 | * 颜色是否在对应的范围内色值相似
217 | * @param {Color | [r,g,b,a] | string} color1
218 | * @param {Color | [r,g,b,a] | string} color2
219 | * @param {number 1-255} step
220 | */
221 | static isSimilarColor(color1, color2) {
222 | return utils.isSimilarColor(...arguments)
223 | }
224 | }
225 |
226 | Picture2color.Color = Color
227 | Picture2color.ColorGroup = ColorGroup
228 |
229 | module.exports = Picture2color
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("Picture2color",[],e):"object"==typeof exports?exports.Picture2color=e():t.Picture2color=e()}(this,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=6)}([function(t,e){t.exports={analy:function(t){return Object.assign({},t)},color:function(t){return Object.assign({},t)},group:function(t){return Object.assign({},t)},count:function(t){return Object.assign({},t)},point:function(t){return Object.assign({},t)},main:function(t){return Object.assign({},t)}}},function(t,e,r){const n=r(3),o=r(0);r(2);function i(t){return"object"==typeof t?t:s(t)}function s(t){try{if(/^rgba?\(.+\)/i.test(t)){let e=t.match(/([\d.]+)/g);if(!e||e.length<3)throw Error("请传入正确的值");3===e.length&&e.push("1");let r=e.pop();return e.push(255*r),e.slice(0,4).map(t=>Number(t))}{let e=t.match(/^#(\w+)/);if(e=e?e[1]:"",!e)throw Error("请传入正确的值");if(3===e.length)return[parseInt("0x"+e[0].repeat(2)),parseInt("0x"+e[1].repeat(2)),parseInt("0x"+e[2].repeat(2)),255];if(e.length>=6)return e=e.substring(0,6),[parseInt("0x"+e.substring(0,2)),parseInt("0x"+e.substring(2,4)),parseInt("0x"+e.substring(4,6)),255];if(!e)throw Error("请传入正确的值")}}catch(t){console.warn("请传入正确的色值")}}t.exports={isSimilarColor(t,e,r=10){t="string"==typeof t?i(t):t,e="string"==typeof e?i(e):e;let[n,o,s,a]=Array.isArray(t)?t:t.data,[l,u,c,h]=Array.isArray(e)?e:e.data;return a/=255,h/=255,Math.sqrt(Math.pow(n*a-l*h,2)+Math.pow(o*a-u*h,2)+Math.pow(s*a-c*h,2))Number(Math.random().toString().split(".")[1].substring(0,t)).toString(32),data2color(t,e=n.RGBACOLOR){try{let r;switch(e){case n.HEXCOLOR:{let[e,n,o,i]=t||[];i/=255,e=(e*i).toString(16),n=(n*i).toString(16),o=(o*i).toString(16),e=e.length>1?e:"0"+e,n=n.length>1?n:"0"+n,o=o.length>1?o:"0"+o,r=["#",e,n,o].join("");break}default:{let[e,n,o,i]=t||[];r=`rgba(${e},${n},${o},${i/255})`;break}}return r}catch(t){return""}},color2data:s,index2position:(t,e=1)=>[t%e,Math.ceil(t/e)],paramsFilter:o}},function(t,e,r){const n=r(4),o=r(1),i=r(3),s=r(0),a={deepStep:120,value:void 0,type:i.RGBACOLOR};new Map;class l extends n{constructor(t,e={}){if(super(1),t instanceof l)return t;this.option=s.color(Object.assign({},a,e)),this.data=t,this.__colorName=e.value||null}get isDeep(){return o.isDeep(this,this.option.deepStep)}get value(){return this.__colorName||(this.__colorName=this.toColorString())}toColorString(t=i.RGBACOLOR){return o.data2color(this.data,this.option.type||t)}static clone(t,e){let r=new l(t.data,o.paramsFilter.color({...t.option,value:t.__colorName,...e}));return r.resetCount(t.__count),r}}t.exports=l},function(t,e){t.exports={HEXCOLOR:"HEX",RGBACOLOR:"RGBA"}},function(t,e){t.exports=class{constructor(t=0){this.__count=t,this.__children=[],this.__parent=null}resetCount(t=0){this.__count=t}append(t){this.__children.push(t),t.__parent=this,this.plus(t.__count)}plus(t=1){this.__count+=t,this.__parent&&this.__parent.plus(t)}get count(){return this.__count}get percent(){return this.__parent?this.__count/this.__parent.__count*100:100}}},function(t,e,r){const n=r(2),o=r(4),i=r(1),s=r(0);class a extends o{constructor(t){if(super(),this.gid=i.getUniqueId(),this.isGroup=!0,this.__proxy=t||null,this.__pool=new Map,t){this.concat(t);let e=new Set(["value","data","isDeep","toColorString"]);return new Proxy(this,{get:(r,n)=>e.has(n)?t[n]:r[n]})}}pushPool(t){if(t instanceof n)this.__pool.set(t.value,t),this.append(t);else{if(!t.isGroup)throw t;this.__pool.set(t.__proxy.value,t),this.append(t)}}concat(t,e){let r=this.__pool;if(t instanceof n){let e=r.get(t.value);e?e.plus(t.__count):this.pushPool(t)}else if(t.isGroup){let e=r.get(t.__proxy.value);e?e.plus(t.__count):this.pushPool(t)}else{let o=i.data2color(t),a=r.get(o);a?a.plus():(t=new n(t,s.color({...e,value:o}),this),this.pushPool(t))}return t}hasChild(t){return this.__pool.has(t)}get(t){return t instanceof n?this.__pool.get(t.value):Array.isArray(t)?this.__pool.get(i.data2color(t)):this.__pool.get(t)}createColorProxy(t={},e){if((e=e||this).__proxy)return this;let{colorStep:r=100}=t,o=new Set;e.sortList.forEach(t=>{let e=!0;o.forEach((o,s)=>{i.isSimilarColor(o.__proxy,t,r)&&e&&(e=!1,t=n.clone(t),o.concat(t,o))}),e&&(t=n.clone(t),o.add(new a(t)))});let s=new a;return o.forEach(t=>{s.concat(t)}),s}get proxy(){return this.__proxy}get list(){return Array.from(this.__pool.values())}get sortList(){return this.list.sort((t,e)=>e.__count-t.__count)}}t.exports=a},function(t,e,r){const n=r(7);t.exports=n},function(t,e,r){const n=r(8),o=r(9),i=r(2),s=r(5),a=r(1),l=r(0),u=(r(3),{async:!1,event:["click"],deepStep:192});class c extends n{constructor(t,e){if(super(),this.originColorData=null,this.colorData=null,this.option=l.main(Object.assign({},u,e||{})),this.__el=null,this.__cache={},this.option.async||"string"==typeof t)return this.asyncInit(t);if(!t.complete)throw Error("图片尚未加载完成");this.init(t)}asyncInit(t){let e=this;return new Promise((function(r,n){if("string"==typeof t){let o=t;(t=new Image).setAttribute("crossOrigin","anonymous"),t.onload=()=>{e.init(t),r(e)},t.onerror=()=>{n(new Error("图片加载失败"))},t.src=o}else e.init(t),r(e)}))}init(t){this.__el=t,this.getImageColor=c.getImageColor,this.getColorData(t),this.destory=c.destory,this.option.event&&this.bindEvent(this.option.event)}bindEvent(t=[]){let e=this,r=t.map(t=>{let r=function(r){var{offsetX:n,offsetY:o}=r;let i=e.getImageColor(n,o,e.__el);e.emit("color",{type:t,eventPosition:[n,o],color:i})};return this.__el.addEventListener(t,r),{type:t,callback:r}});this.__cache.event=r}unbindEvent(){let t=this;this.__cache.event&&this.__cache.event.forEach(({type:e,callback:r})=>{t.__el.removeEventListener(e,r)})}getColorData(t){let e=document.createElement("canvas"),{width:r,height:n}=t;e.width=r,e.height=n,this.__canvas=e;let i=e.getContext("2d");i.drawImage(t,0,0);let s=i.getImageData(0,0,r,n);this.originColorData=s,this.analyData=new o(s,a.paramsFilter.analy(this.option))}colorFilter_bate(t){return this.analyData.colorFilter_bate(t)}getColorGroup(){return this.analyData.colorGroup}getFrameColorGroup(t){return this.analyData.getFrameColorGroup(t)}static isDeep(t,e){return t instanceof i?e?a.isDeep(t,e):t.isDeep:t.reduce((t,r)=>t+((e?a.isDeep(r,e):r.isDeep)?r.percent:0),0)>50}static destory(t){(t=t||this).unbindEvent(),Object.keys(t.__cache).forEach(e=>{delete t.__cache[e]}),t.__cache=null,t.originColorData=null,t.option=null,t.__el=null,t.off(".inner"),t.analyData&&t.colorData.destory(),t.colorData=null}static getImageColor(t=0,e=0,r,n=!0){try{if(this instanceof c){let{width:o,height:i,naturalHeight:s,naturalWidth:a}=r;return n?(t=Math.round(t/o*a),e=Math.round(e/i*s)):(t=Math.round(t*o*.01),e=Math.round(e*i*.01)),this.analyData.getPositionColor(t,e)}{let{width:o,height:s,naturalHeight:a,naturalWidth:l}=r,u=document.createElement("canvas");u.width=o,u.height=s;let c=u.getContext("2d");c.drawImage(r,0,0,l,a,0,0,o,s),n||(t=Math.round(t*o*.01),e=Math.round(e*s*.01));let h=c.getImageData(t,e,1,1);return new i(h.data)}}catch(t){return console.warn("获取数据失败"),{}}}static isSimilarColor(t,e){return a.isSimilarColor(...arguments)}}c.Color=i,c.ColorGroup=s,t.exports=c},function(t,e,r){"use strict";var n,o="object"==typeof Reflect?Reflect:null,i=o&&"function"==typeof o.apply?o.apply:function(t,e,r){return Function.prototype.apply.call(t,e,r)};n=o&&"function"==typeof o.ownKeys?o.ownKeys:Object.getOwnPropertySymbols?function(t){return Object.getOwnPropertyNames(t).concat(Object.getOwnPropertySymbols(t))}:function(t){return Object.getOwnPropertyNames(t)};var s=Number.isNaN||function(t){return t!=t};function a(){a.init.call(this)}t.exports=a,t.exports.once=function(t,e){return new Promise((function(r,n){function o(){void 0!==i&&t.removeListener("error",i),r([].slice.call(arguments))}var i;"error"!==e&&(i=function(r){t.removeListener(e,o),n(r)},t.once("error",i)),t.once(e,o)}))},a.EventEmitter=a,a.prototype._events=void 0,a.prototype._eventsCount=0,a.prototype._maxListeners=void 0;var l=10;function u(t){if("function"!=typeof t)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof t)}function c(t){return void 0===t._maxListeners?a.defaultMaxListeners:t._maxListeners}function h(t,e,r,n){var o,i,s,a;if(u(r),void 0===(i=t._events)?(i=t._events=Object.create(null),t._eventsCount=0):(void 0!==i.newListener&&(t.emit("newListener",e,r.listener?r.listener:r),i=t._events),s=i[e]),void 0===s)s=i[e]=r,++t._eventsCount;else if("function"==typeof s?s=i[e]=n?[r,s]:[s,r]:n?s.unshift(r):s.push(r),(o=c(t))>0&&s.length>o&&!s.warned){s.warned=!0;var l=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(e)+" listeners added. Use emitter.setMaxListeners() to increase limit");l.name="MaxListenersExceededWarning",l.emitter=t,l.type=e,l.count=s.length,a=l,console&&console.warn&&console.warn(a)}return t}function p(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function f(t,e,r){var n={fired:!1,wrapFn:void 0,target:t,type:e,listener:r},o=p.bind(n);return o.listener=r,n.wrapFn=o,o}function d(t,e,r){var n=t._events;if(void 0===n)return[];var o=n[e];return void 0===o?[]:"function"==typeof o?r?[o.listener||o]:[o]:r?function(t){for(var e=new Array(t.length),r=0;r0&&(s=e[0]),s instanceof Error)throw s;var a=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw a.context=s,a}var l=o[t];if(void 0===l)return!1;if("function"==typeof l)i(l,this,e);else{var u=l.length,c=g(l,u);for(r=0;r=0;i--)if(r[i]===e||r[i].listener===e){s=r[i].listener,o=i;break}if(o<0)return this;0===o?r.shift():function(t,e){for(;e+1=0;n--)this.removeListener(t,e[n]);return this},a.prototype.listeners=function(t){return d(this,t,!0)},a.prototype.rawListeners=function(t){return d(this,t,!1)},a.listenerCount=function(t,e){return"function"==typeof t.listenerCount?t.listenerCount(e):_.call(t,e)},a.prototype.listenerCount=_,a.prototype.eventNames=function(){return this._eventsCount>0?n(this._events):[]}},function(t,e,r){const n=r(2),o=r(5),i=r(10),s=r(1),a=r(0),l=(r(3),{});t.exports=class{constructor(t,e={}){this.option=a.analy(Object.assign({},l,e)),this.originData=t;let{width:r,height:n}=t;this.width=r,this.height=n;let{colorGroup:o,translateData:i}=this.analyse(t);this.colorGroup=o,this.translateData=i}analyse(t){let{width:e,height:r,data:n}=t;return{translateData:{width:e,height:r,data:t=this.colorFormat(n,e,r)},colorGroup:this.createColorGroup(t)}}colorFormat(t,e,r){let n=[],o=0;for(t=Array.from(t);4*o<=t.length-4;){let s=t.slice(4*o,4*(o+1));n.push(new i(s,{index:o,width:e,height:r})),o++}return n}createColorGroup(t){let e=new o;return t.forEach(t=>{e.concat(t.data,s.paramsFilter.group({...this.option,value:t.colorName}))}),e}getFrameColorGroup(t={},e){let{width:r,height:n,translateData:o}=e||this,{size:i=.2}=Object.assign({},this.option,t),s=i*r,a=(1-i)*r,l=i*n,u=(1-i)*n,c=o.data.filter((t,e)=>{let n=e%r,o=Math.ceil(e/r);return(n<=s||n>=a)&&(o>=u||o<=l)});return this.createColorGroup(c)}getPositionColor(t,e,r){let{width:n,translateData:o}=r||this,i=o.data[e*n+t];return this.colorGroup.get(i.colorName)}colorFilter_bate(t){let{translateData:e,colorGroup:r}=this,{width:o,data:i}=e,{colorType:a}=this.option;if(t instanceof n)return i.filter(e=>e.colorName===t.value);if(t.isGroup){return t.__children.map(t=>this.colorFilter_bate(t)).reduce((t,e)=>t.concat(...e),[])}return Array.isArray(t)?i.filter(e=>e.colorName===s.data2color(t,a)):i.filter(e=>e.colorName===t)}destory(){this.option=null,this.originData=null,this.colorGroup=null,this.mainColor=null,this.width=null,this.height=null}}},function(t,e,r){const n=r(1),o=r(0),i={index:0,width:0,height:0,name:void 0};t.exports=class{constructor(t,e={}){this.option=o.point(Object.assign({},i,e));let{index:r,width:n,height:s,name:a}=this.option;this.data=t,this.index=r,this.width=n,this.height=s,this.__position=null,this.__colorName=a||null}get position(){return this.__position||(this.__position=n.index2position(this.index,this.width))}get colorName(){return this.__colorName||(this.__colorName=n.data2color(this.data))}}}])}));
--------------------------------------------------------------------------------