优化颜色排序方法,根据多重判断距离方式

This commit is contained in:
rucky 2022-02-16 19:04:07 +08:00
parent 145fc58de7
commit 44208b367f
5 changed files with 206 additions and 286 deletions

View File

@ -16,8 +16,8 @@ module.exports = {
// }, // },
test: { test: {
// 环境对象 // 环境对象
name: 'szxgl测试环境', // 环境名称 name: 'szxgl测试环境:pro', // 环境名称
script: 'npm run build:dev', // 打包命令 script: 'npm run build:pro', // 打包命令
host: '39.108.110.167', // 服务器地址 host: '39.108.110.167', // 服务器地址
port: 22, // 服务器端口号 port: 22, // 服务器端口号
username: 'front', // 服务器登录用户名 username: 'front', // 服务器登录用户名

View File

@ -69,6 +69,7 @@
"jsmpeg": "^1.0.0", "jsmpeg": "^1.0.0",
"meshline": "^2.0.3", "meshline": "^2.0.3",
"qrcode": "^1.4.4", "qrcode": "^1.4.4",
"snapsvg-cjs": "^0.0.6",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"style-resources-loader": "^1.3.2", "style-resources-loader": "^1.3.2",
"three": "^0.133.0", "three": "^0.133.0",

View File

@ -1,6 +1,5 @@
@charset "utf-8"; @charset "utf-8";
@import "~@/assets/font/project_iconfont.css";
/*-------------------- /*--------------------
通用 通用
--------------------*/ --------------------*/

View File

@ -177,6 +177,7 @@ html,body{
color: rgba(0,0,0,0.8); color: rgba(0,0,0,0.8);
text-align: center; text-align: center;
animation: hide .5s ease 3s both; animation: hide .5s ease 3s both;
font-size: 0.3rem;
} }
#svg{ #svg{
animation: hide .5s ease 3s both; animation: hide .5s ease 3s both;

View File

@ -31,27 +31,7 @@
></div> ></div>
</div> </div>
</div> </div>
<div class="show-con cluster" ref="clusterCon">
<div
class="color-group"
v-for="(item, index) in clusters"
:key="index"
>
<div class="header">
<div class="span" :style="rennderHeaderCss(item)"></div>
{{ item.name }} Group
</div>
<div
class="color-span"
v-for="(item, index) in item.colors"
:key="index"
:style="rennderCssHex(item)"
></div>
</div>
</div>
<div class="btn-groups"> <div class="btn-groups">
<!-- <div class="btn" @click="sortColor">颜色归类</div> -->
<div class="first">
<div class="p">根据Hue色调分组</div> <div class="p">根据Hue色调分组</div>
<div class="ios-checkbox"> <div class="ios-checkbox">
<input <input
@ -65,7 +45,6 @@
/> />
<label for="ios-checkbox" class="emulate-ios-button"></label> <label for="ios-checkbox" class="emulate-ios-button"></label>
</div> </div>
</div>
<!-- <div class="p">根据固定颜色分组</div> <!-- <div class="p">根据固定颜色分组</div>
<div class="ios-checkbox"> <div class="ios-checkbox">
@ -80,29 +59,13 @@
/> />
<label for="ios-checkbox2" class="emulate-ios-button"></label> <label for="ios-checkbox2" class="emulate-ios-button"></label>
</div> --> </div> -->
<div
class="btn"
@click="sortColorSaturation"
:class="{ active: active1 }"
ref="btn1"
>
根据饱和度排序
</div>
<div <div
class="btn" class="btn"
@click="sortColorLight" @click="sortColorLight"
:class="{ active: active2 }" :class="{ active: active2 }"
ref="btn2" ref="btn2"
> >
根据明度排序 排序
</div>
<div class="show-color" ref="showColor">
<div
class="color-span"
v-for="(item, index) in clusters"
:key="index"
:style="rennderHeaderCss(item)"
></div>
</div> </div>
</div> </div>
</div> </div>
@ -111,6 +74,7 @@
// @ is an alias to /src // @ is an alias to /src
import colorUtil from "color-util"; import colorUtil from "color-util";
import colors from "@/data/color.json"; import colors from "@/data/color.json";
import Snap from "snapsvg-cjs";
import gsap from "gsap"; import gsap from "gsap";
export default { export default {
name: "ColorSort", name: "ColorSort",
@ -118,38 +82,23 @@ export default {
data() { data() {
return { return {
hideOrigin: false, hideOrigin: false,
hideOrigin2: false,
colorArr: colors, colorArr: colors,
active1: false, active1: false,
active2: false, active2: false,
hlsColorArr: [], hlsColorArr: [],
clustersColorArr: [], newColorArr: [[], [], [], [], [], [], [], [], [], [], [], []],
newColorArr: [[], [], [], [], [], [], [], [], [], [], [], [], [], [], []], sortedColorArr: [],
// 1-12 hue 30 | 13 - | 14 - | 15 - // 1-12 hue 30 | 13 - | 14 - | 15 -
clusters: [ balance: [0.9, 0, 0.01],
// balance2: [0.3, 0, 0],
{ name: "red", leadColor: [255, 0, 0], colors: [] }, balance3: [0.5, 0.2, 0.1],
{ name: "orange", leadColor: [255, 128, 0], colors: [] }, caclulTime: 0,
{ name: "yellow", leadColor: [255, 255, 0], colors: [] },
{ name: "chartreuse", leadColor: [128, 255, 0], colors: [] },
{ name: "green", leadColor: [0, 255, 0], colors: [] },
{ name: "spring green", leadColor: [0, 255, 128], colors: [] },
{ name: "cyan", leadColor: [0, 255, 255], colors: [] },
{ name: "azure", leadColor: [0, 127, 255], colors: [] },
{ name: "blue", leadColor: [0, 0, 255], colors: [] },
{ name: "violet", leadColor: [127, 0, 255], colors: [] },
{ name: "magenta", leadColor: [255, 0, 255], colors: [] },
{ name: "rose", leadColor: [255, 0, 128], colors: [] },
{ name: "black", leadColor: [0, 0, 0], colors: [] },
{ name: "grey", leadColor: [235, 235, 235], colors: [] },
// { name: "white", leadColor: [255, 255, 255], colors: [] },
],
}; };
}, },
computed: { computed: {
rennderCssHSL() { rennderCssHSL() {
return function (item) { return function (item) {
return `background-color: ${colorUtil.hsl.to.csshsl(item)};`; return `background-color: ${item.csshsl};`;
}; };
}, },
rennderCssHex() { rennderCssHex() {
@ -159,130 +108,89 @@ export default {
}, },
rennderHeaderCss() { rennderHeaderCss() {
return function (item) { return function (item) {
return `background-color: ${colorUtil.rgb.to.cssrgb({ return `background-color: ${item.csshsl};`;
r: item.leadColor[0],
g: item.leadColor[1],
b: item.leadColor[2],
a: 255,
})};`;
}; };
}, },
}, },
mounted() { mounted() {
// console.log(this.newColorArr);
this.reHandleColor(); this.reHandleColor();
}, },
methods: { methods: {
// //
showGroup(index) { showGroup(index) {
let target = this.$refs.sortCon; gsap.to(this.$refs.sortCon, {
gsap.to(target, {
autoAlpha: 1, autoAlpha: 1,
x: this.hideOrigin ? 0 : "100%", x: this.hideOrigin ? 0 : "100%",
zIndex: this.hideOrigin ? 2 : 10, zIndex: this.hideOrigin ? 2 : 10,
onStart: () => { onStart: () => {
if (this.active1) { this.active2 = false;
this.sortColorSaturation(); // if (this.active1) {
} // this.sortColorSaturation();
if (this.active2) { // }
this.sortColorLight(); // if (this.active2) {
} // this.sortColorLight();
// }
}, },
}); });
if (this.hideOrigin) {
this.hideOrigin2 = false;
gsap.to(this.$refs.clusterCon, {
autoAlpha: 0,
zIndex: 2,
x: 0,
});
gsap.to([this.$refs.btn1, this.$refs.btn2], { autoAlpha: 1 });
// gsap.to(this.$refs.showColor, { autoAlpha: 0 });
}
},
// 2
showGroup2(index) {
let target = this.$refs.clusterCon;
gsap.to(target, {
autoAlpha: 1,
zIndex: this.hideOrigin2 ? 2 : 10,
x: this.hideOrigin2 ? 0 : "100%",
});
if (this.hideOrigin2) {
this.hideOrigin = false;
gsap.to(this.$refs.sortCon, {
autoAlpha: 0,
zIndex: 2,
x: 0,
});
gsap.to(this.$refs.showColor, { autoAlpha: 1 });
gsap.to([this.$refs.btn1, this.$refs.btn2], { autoAlpha: 0 });
} else {
gsap.to(this.$refs.showColor, { autoAlpha: 0 });
gsap.to([this.$refs.btn1, this.$refs.btn2], { autoAlpha: 1 });
}
}, },
// hsl // hsl
reHandleColor() { reHandleColor() {
// this.colorArr
this.colorArr.forEach((item, index) => { this.colorArr.forEach((item, index) => {
if (item.R && item.G && item.B) {
this.hlsColorArr.push( this.hlsColorArr.push(
// this.rgbToHsl(item.R, item.G, item.B) colorUtil.color(item.HEX) //item.HEX
colorUtil.rgb.to.hsl({
r: item.R,
g: item.G,
b: item.B,
a: 255,
})
); );
}
}); });
console.log(this.hlsColorArr); // console.log(this.hexColorArr);
//hue //hue
this.sortColorByHue(); this.sortColorByHue();
//
// this.sortColorByClusters();
}, },
// //
sortColorByHue() { sortColorByHue() {
this.hlsColorArr.forEach((item, index) => { this.hlsColorArr.forEach((item, index) => {
if (item.h) { let color = item.hsl;
let _index = 11 - Math.floor((item.h * 360) / 30); //36012 if (color.h) {
let _index = 8 - Math.floor((color.h * 360) / 40); //36012
// //
// if (
// // 0
// color.s <= 0.3 &&
// color.l <= 0.7 &&
// color.l >= 0.1
// ) {
// _index = 11;
// }
// //
// if (color.l < 0.4 && color.s < 0.4) {
// _index = 9;
// }
// //
// if (color.l > 0.7 && color.s < 0.3) {
// _index = 10;
// }
// //
if ( if (
// 0 // 0
item.s <= 0.15 && item.l <= 0.75 && item.l >= 0.2 color.s <= 0.15 &&
color.l <= 0.75 &&
color.l >= 0.2
) { ) {
_index = 14; _index = 11;
} }
// //
if (item.l < 0.2 && item.s < 0.18) { if (color.l < 0.2 && color.s < 0.18) {
_index = 12; _index = 9;
} }
// //
if (item.l > 0.75 && item.s < 0.15) { if (color.l > 0.75 && color.s < 0.15) {
_index = 13; _index = 10;
} }
this.newColorArr[_index].push(item); this.newColorArr[_index].push(item);
} }
}); });
// console.log(this.newColorArr); // console.log(this.newColorArr);
}, },
//
sortColorByClusters() {
const sortedClusters = this.sortWithClusters(this.hlsColorArr);
const sortedColors = sortedClusters.reduce((acc, curr) => {
const colors = curr.colors.map((color) => color.hex);
return [...acc, ...colors];
}, []);
this.clustersColorArr = sortedColors;
console.log(this.clustersColorArr);
},
// //
sortColorSaturation() { sortColorSaturation() {
this.active1 = true; this.active1 = true;
@ -291,12 +199,12 @@ export default {
// //
this.newColorArr.forEach((item, index) => { this.newColorArr.forEach((item, index) => {
item.sort((a, b) => { item.sort((a, b) => {
return b.s - a.s; return b.hsl.s - a.hsl.s;
}); });
}); });
} else { } else {
this.hlsColorArr.sort((a, b) => { this.hlsColorArr.sort((a, b) => {
return b.s - a.s; return b.hsl.s - a.hsl.s;
}); });
} }
@ -304,156 +212,166 @@ export default {
}, },
// //
sortColorLight() { sortColorLight() {
this.active1 = false;
this.active2 = true; this.active2 = true;
this.caclulTime++;
this.$Utils.showTips({
message: `正在第${this.caclulTime}次计算,请稍等...<br/>再次点击排序按钮<br/>可重复计算优化排序`,
});
setTimeout(() => {
if (this.hideOrigin) { if (this.hideOrigin) {
// //
this.newColorArr.forEach((item, index) => { this.newColorArr.forEach((item, index) => {
//
// item = this.sortColor(item); // item = this.sortColor(item);
// console.log(item) // console.log(item)
item.sort((a, b) => { // item.forEach((t, i) => {
// let deltaH = b.h - a.h; // let rgb = t.rgb;
// let deltaS = b.s - a.s; // t.grey = rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114;
let deltaL = b.l - a.l; // });
// let aRgb = colorUtil.hsl.to.rgb({h: a.h, s: a.s, l: a.l, a: 1}); // item.sort((a, b) => {
// let bRgb = colorUtil.hsl.to.rgb({h: b.h, s: b.s, l: b.l, a: 1}); // let deltaGrey = b.grey - a.grey;
// let distance = this.colorDistance(aRgb,bRgb); // let result = deltaGrey;
return ( // return (
// Math.random() > 0.5 ? 1 : -1 // // Math.random() > 0.5 ? 1 : -1
deltaL // result
); // );
}); // });
this.newColorArr[index] = [];
let sortedArr = this.sortColors(item);
this.newColorArr[index].push(...sortedArr);
}); });
} else { } else {
this.hlsColorArr.sort((a, b) => { // this.hlsColorArr.sort((a, b) => {
return b.l - a.l; // return b.hsl.l - a.hsl.l;
}); // });
this.hlsColorArr = this.sortColors(this.hlsColorArr);
} }
this.active2 = false;
}, 1000);
// console.log(this.newColorArr); // console.log(this.newColorArr);
}, },
// // hsl
colorDistance(color1, color2) { colorDistanceHsl(_color1, _color2) {
const x = let color1 = _color1;
Math.pow(color1.r - color2.r, 2) + let color2 = _color2;
Math.pow(color1.g - color2.g, 2) + let result = 0;
Math.pow(color1.b - color2.b, 2); color1 = color1.hsl;
return x; //Math.sqrt(x) color2 = color2.hsl;
}, Object.keys(color1).forEach((key, index) => {
// if (key != "a") {
oneDimensionSorting(colors, dim) { result +=
return colors.sort((colorA, colorB) => { (color1[key] - color2[key]) *
if (colorA.hsl[dim] < colorB.hsl[dim]) { (color1[key] - color2[key]) *
return -1; this.balance2[index];
} else if (colorA.hsl[dim] > colorB.hsl[dim]) { }
return 1; });
} else { if (Math.floor(color1.r / 30) !== Math.floor(color2.r / 30)) {
return 0; return 0;
} }
}); return result;
}, },
// // rgb
blendRgbaWithWhite(rgba) { colorDistanceRgb(_color1, _color2) {
const color = colorUtil.color(rgba); let color1 = _color1;
const a = color.rgb.a / 255; let color2 = _color2;
const r = Math.floor(color.rgb.r * a + 0xff * (1 - a)); let result = 0;
const g = Math.floor(color.rgb.g * a + 0xff * (1 - a)); color1 = color1.rgb;
const b = Math.floor(color.rgb.b * a + 0xff * (1 - a)); color2 = color2.rgb;
return "#" + ((r << 16) | (g << 8) | b).toString(16); Object.keys(color1).forEach((key, index) => {
}, if (key != "a") {
sortWithClusters(colorsToSort) { result +=
const mappedColors = colorsToSort (color1[key] - color2[key]) *
// .map((color) => { (color1[key] - color2[key]) *
// console.log(color); this.balance3[index];
// const isRgba = color.includes("rgba");
// if (isRgba) {
// return blendRgbaWithWhite(color);
// } else {
// return color;
// }
// })
.map(colorUtil.color);
mappedColors.forEach((color) => {
let minDistance;
let minDistanceClusterIndex;
this.clusters.forEach((cluster, clusterIndex) => {
const colorRgbArr = [color.rgb.r, color.rgb.g, color.rgb.b];
const distance = this.colorDistance(
colorRgbArr,
cluster.leadColor
);
if (
typeof minDistance === "undefined" ||
minDistance > distance
) {
minDistance = distance;
minDistanceClusterIndex = clusterIndex;
} }
}); });
return result;
this.clusters[minDistanceClusterIndex].colors.push(color);
});
this.clusters.forEach((cluster) => {
const dim = ["white", "grey", "black"].includes(cluster.name)
? "l"
: "s";
cluster.colors = this.oneDimensionSorting(cluster.colors, dim);
});
return this.clusters;
}, },
sortColor(colors){ // hsv rgb hsl
colorDistance(_color1, _color2) {
let color1 = _color1;
let color2 = _color2;
let result = 0;
result += this.colorDistanceRgb(color1, color2);
result += this.colorDistanceHsl(color1, color2);
color1 = color1.hsv;
color2 = color2.hsv;
Object.keys(color1).forEach((key, index) => {
if (key != "a") {
result +=
(color1[key] - color2[key]) *
(color1[key] - color2[key]) *
this.balance[index];
}
});
return result;
},
//
sortColors(colors) {
// Calculate distance between each color // Calculate distance between each color
var distances = []; console.log("origin length =", colors.length);
for (var i = 0; i < colors.length; i++) { if (colors.length === 0) return colors;
const distances = [];
for (let i = 0; i < colors.length; i++) {
distances[i] = []; distances[i] = [];
for (var j = 0; j < i; j++){ for (let j = 0; j < i; j++) {
let aRgb = colorUtil.hsl.to.rgb({h: colors[i].h, s: colors[i].s, l: colors[i].l, a: 1}); distances.push([
let bRgb = colorUtil.hsl.to.rgb({h: colors[j].h, s: colors[j].s, l: colors[j].l, a: 1}); colors[i],
distances.push([colors[i], colors[j], this.colorDistance(aRgb, bRgb)]); colors[j],
this.colorDistance(colors[i], colors[j]),
]);
} }
} }
distances.sort(function(a, b) { distances.sort((a, b) => a[2] - b[2]);
return a[2] - b[2];
}); // console.log(distances.length);
// Put each color into separate cluster initially // Put each color into separate cluster initially
var colorToCluster = {}; const colorToCluster = {};
for (var i = 0; i < colors.length; i++) for (let i = 0; i < colors.length; i++) {
colorToCluster[colors[i]] = [colors[i]]; colorToCluster[colors[i].int] = [colors[i]];
}
// Merge clusters, starting with lowest distances // Merge clusters, starting with lowest distances
var lastCluster=[]; let lastCluster;
for (var i = 0; i < distances.length; i++) { for (let i = 0; i < distances.length; i++) {
var color1 = distances[i][0]; const color1 = distances[i][0];
var color2 = distances[i][1]; const color2 = distances[i][1];
var cluster1 = colorToCluster[color1];
var cluster2 = colorToCluster[color2]; const cluster1 = colorToCluster[color1 && color1.int];
if (!cluster1 || !cluster2 || cluster1 == cluster2) const cluster2 = colorToCluster[color2 && color2.int];
if (!cluster1 || !cluster2 || cluster1 === cluster2) {
continue; continue;
}
// Make sure color1 is at the end of its cluster and // Make sure color1 is at the end of its cluster and
// color2 at the beginning. // color2 at the beginning.
if (color1 != cluster1[cluster1.length - 1]) if (color1 !== cluster1[cluster1.length - 1]) {
// cluster1 = this.colorReverse(cluster1);
cluster1.reverse(); cluster1.reverse();
if (color2 != cluster2[0]) }
if (color2 !== cluster2[0]) {
cluster2.reverse(); cluster2.reverse();
// cluster2 = this.colorReverse(cluster2);
}
// Merge cluster2 into cluster1 // Merge cluster2 into cluster1
cluster1.push.apply(cluster1, cluster2); cluster1.push(...cluster2);
delete colorToCluster[color1]; delete colorToCluster[color1.int];
delete colorToCluster[color2]; delete colorToCluster[color2.int];
colorToCluster[cluster1[0]] = cluster1; colorToCluster[cluster1[0].int] = cluster1;
colorToCluster[cluster1[cluster1.length - 1]] = cluster1; colorToCluster[cluster1[cluster1.length - 1].int] = cluster1;
lastCluster = cluster1; lastCluster = cluster1;
} }
console.log(lastCluster) console.log("sorted length =", lastCluster.length);
// By now all colors should be in one cluster // By now all colors should be in one cluster
return lastCluster; return lastCluster;
} },
}, },
}; };
</script> </script>
@ -545,9 +463,10 @@ export default {
text-align: center; text-align: center;
border-radius: 20px; border-radius: 20px;
padding: 0 20px; padding: 0 20px;
margin: 10px; margin: 40px;
background: linear-gradient(315deg, #ec7070, #c75f5f); background: linear-gradient(315deg, #ec7070, #c75f5f);
box-shadow: -14px -14px 28px #b15454, 14px 14px 28px #ff7e7e; box-shadow: -14px -14px 28px #b15454, 14px 14px 28px #ff7e7e;
cursor: pointer;
&.active { &.active {
color: rgba(255, 255, 255, 1); color: rgba(255, 255, 255, 1);
background: linear-gradient(315deg, #c75f5f, #ec7070); background: linear-gradient(315deg, #c75f5f, #ec7070);