【问题标题】:Getting Multiple Distinct Colors获得多种不同的颜色
【发布时间】:2022-01-22 00:10:48
【问题描述】:

我有一个折线图,它允许根据项目在同一个图表上显示多条线。但是,物品的数量是不同的,因为它是基于个人的。因此,我需要为所有项目获得不同的颜色。用户可以选择要在图表上显示的项目。因此,当用户选择更多可见的项目时,所需的颜色数量将增加。随着颜色数量的增加,每种颜色起初应该是不同的和可区分的,但逐渐变得彼此相似。想知道是否有实现上述目标的库或代码段?

【问题讨论】:

    标签: javascript reactjs colors


    【解决方案1】:

    编辑:用 ES6 箭头函数语法重写了函数。新增randomColours(quan) 功能。为 Method 1Method 2 添加了 sn-ps 以更好地展示它们的工作原理。为方法2函数添加了更多参数。

    试试这样的:

    方法一:返回一个随机颜色,用一个rgb()颜色值表示。这种方法的缺点:因为颜色是随机的,你可以得到非常不同的颜色,但也可以得到非常相似的颜色。如果您想要一种更严格的方法来生成在色轮上等间距的颜色,请参阅方法 2

    function randomColour() {
        return `rgb(${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)})`;
    }
    
    // ES6 Arrow Function Syntax (still does same thing)
    const randomColour = () => `rgb(${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)})`;
    

    使用:

    let colOne = randomColour();
    // colOne = "rbg(236, 174, 254)"
    
    let colTwo = randomColour();
    // colTwo = "rbg(47, 83, 164)"
    
    let colThree = randomColour();
    // colThree = "rbg(145, 214, 39)"
    

    使用与方法一相同的原理修改函数,但不是返回单一随机颜色,而是返回一个随机颜色数组

    function randomColours(quan) {
        let colours = [];
        for (let i = 0; i < quan; i++) {
            colours.push(`rgb(${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)})`);
        }
        return colours;
    }
    
    // ES6 Arrow Function Syntax (still does same thing)
    const randomColours = quan => {
        let colours = [];
        for (let i = 0; i < quan; i++) {
            colours.push(`rgb(${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)})`);
        }
        return colours;
    }
    

    使用:

    const coloursArr = randomColours(5);
    
    // coloursArr = [
    //     "rgb(183, 110, 102)", 
    //     "rgb(59, 250, 185)", 
    //     "rgb(188, 160, 67)", 
    //     "rgb(241, 15, 18)", 
    //     "rgb(136, 125, 129)"
    // ]
    

    方法 1 的片段:

    const inp = document.querySelector("input");
    const btn = document.querySelector("button");
    const parentDiv = document.querySelector("div.container");
    
    const validNumber = ({ value, min = undefined, max = undefined, integer = false, ignoreStrings = [] }) => {
        if (typeof value != "string" && typeof value != "number") return console.error(`Value must be a number or string.`);
    
        let valid = true;
        let messages = [];
    
        for (let i = 0; i < ignoreStrings.length; i++) {
            value = String(value).split(ignoreStrings[i]).join("");
        }
    
        if (String(value) == "" || value == null || isNaN(value)) {
            valid = false;
            messages.push("Not a Number");
        }
        if (!messages.includes("Not a Number") && min && Number(value) < min) {
            valid = false;
            messages.push(`Minimum Value is ${min}`);
        }
        if (!messages.includes("Not a Number") && max && Number(value) > max) {
            valid = false;
            messages.push(`Maximum Value is ${max}`);
        }
        if (!messages.includes("Not a Number") && integer && !(!isNaN(value) && parseInt(Number(value)) == value && !isNaN(parseInt(value, 10)))) {
            valid = false;
            messages.push("Not an integer");
        }
    
        return { valid, messages };
    }
    
    // returns random colour!
    const randomColour = () => `rgb(${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)})`;
    
    // returns array of random colours!
    const randomColours = quan => {
        let colours = [];
        for (let i = 0; i < quan; i++) {
            colours.push(`rgb(${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)}, ${Math.round(Math.random() * 255)})`);
        }
        return colours;
    }
    
    const generateCircles = quan => {
        // get an array of random colours
        const colours = randomColours(quan);
    
        parentDiv.innerHTML = "";
    
        for (let i = 0; i < quan; i++) {
            const elem = document.createElement("div");
            elem.className = "circle";
    
            // random colour for a circle
            elem.style.backgroundColor = colours[i];
            parentDiv.appendChild(elem);
        }
    }
    
    btn.addEventListener("click", () => {
        let details = validNumber({ value: inp.value, min: 1, max: 1000, integer: true, ignoreStrings: [" "] });
        
        if (details.valid) {
            generateCircles(Number(inp.value));
        } else {
            details.messagesStr = "";
            details.messages.forEach((str, i) => details.messagesStr += `    ${i+1}. ${str}\n`);
            alert(`Field_1 - Quantity:\n${details.messagesStr}`);
        }
    });
    *, ::before, ::after {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }
    
    body {
        min-height: 100vh;
        height: fit-content;
    }
    
    div.form {
        padding: 2rem;
        display: flex;
        flex-direction: column;
        align-items: start;
    }
    div.form > * { margin-bottom: 1rem; }
    div.form input, div.form button {
        padding: 0.6rem 0.8rem;
        outline: none;
        border-radius: 0.2rem;
        font-family: sans-serif;
        font-size: inherit;
        font-weight: 500;
    }
    div.form input {
        box-sizing: border-box;
        height: fit-content;
        border: 2px solid rgb(179, 179, 179);
        transition: border-color 200ms ease-out;
    }
    div.form input:focus {
        border-color: rgb(45, 160, 255);
    }
    div.form button {
        border: 2px solid rgb(78, 175, 255);
        background-color: rgb(78, 175, 255);
        transition: background-color 200ms ease-out;
    }
    div.form button:hover {
        border-color: rgb(45, 160, 255);
        background-color: rgb(45, 160, 255);
    }
    div.form button:active {
        transform: scale(0.98);
    }
    
    div.container {
        padding: 0rem 1.5rem 2rem 1.5rem;
        display: flex;
        flex-wrap: wrap;
    }
    
    div.container div.circle {
        width: 5rem;
        height: 5rem;
        border-radius: 100%;
        margin: 0.5rem;
    }
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Colour Generation</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div class="form">
            <input type="number" placeholder="no. of circles">
            <button>Generate circles with random colours</button>
        </div>
        
        <div class="container">
        </div>
    
        <script src="script.js"></script>
    </body>
    </html>

    方法二

    function generateColours(quan, shuffle=false) {
        let colours = [];
        for (let i = 0; i < quan; i++) {
            colours.push(`hsl(${(360/quan)*(quan-i)}, 80%, 50%)`);
        }
    
        if (shuffle) {
            // uses the Fisher-Yates Shuffle to shuffle the colours
            let currentIndex = colours.length, randomIndex;
    
            while (currentIndex != 0) {
                randomIndex = Math.floor(Math.random() * currentIndex);
                currentIndex--;
                [colours[currentIndex], colours[randomIndex]] = [colours[randomIndex], colours[currentIndex]];
            }
        }
    
        return colours;
    }
    
    // ES6 Arrow Function Syntax (does same thing)
    const generateColours = (quan, shuffle=false) => {
        let colours = [];
        for (let i = 0; i < quan; i++) {
            colours.push(`hsl(${(360/quan)*(quan-i)}, 80%, 50%)`);
        }
    
        if (shuffle) {
            // uses the Fisher-Yates Shuffle to shuffle the colours
            let currentIndex = colours.length, randomIndex;
    
            while (currentIndex != 0) {
                randomIndex = Math.floor(Math.random() * currentIndex);
                currentIndex--;
                [colours[currentIndex], colours[randomIndex]] = [colours[randomIndex], colours[currentIndex]];
            }
        }
    
        return colours;
    }
    

    使用:

    let coloursArr = generateColours(5);
    
    // coloursArr = [
    //     "hsl(360, 80%, 50%)", 
    //     "hsl(288, 80%, 50%)", 
    //     "hsl(216, 80%, 50%)", 
    //     "hsl(144, 80%, 50%)", 
    //     "hsl(72, 80%, 50%)"
    // ]
    

    颜色同样可区分。

    我在上面的函数中增加了一些参数。它接受一个包含其参数的对象。还利用了 ES6 附带的对象解构,允许使用许多默认参数。这就是我想出的:

    const generateColours = ({ quantity = 1, shuffle = false, order = "0,360", offset = 0, saturation = 80, lightness = 50 }) => {
        let colours = [];
        for (let i = 0; i < quantity; i++) {
            let hue;
            if (order == "0,360") hue = ((360/quantity) * (quantity+i)) - 360;
            if (order == "360,0") hue = (360/quantity) * (quantity-i);
    
            hue += offset;
    
            colours.push(`hsl(${hue}, ${saturation}%, ${lightness}%)`);
        }
    
        if (shuffle) {
            // uses the Fisher-Yates Shuffle to shuffle the colours
            let currentIndex = colours.length, randomIndex;
    
            while (currentIndex != 0) {
                randomIndex = Math.floor(Math.random() * currentIndex);
                currentIndex--;
                [colours[currentIndex], colours[randomIndex]] = [colours[randomIndex], colours[currentIndex]];
            }
        }
    
        return colours;
    }
    

    使用:

    // all default parameters (one colour, shuffle set to false, order is '0,360', hue offset is 0, saturation is 80, lightness is 50)
    
    generateColours({  });
    
    // will return
    // => [
    //     'hsl(0, 80%, 50%)' // red
    // ]
    
    // ----------
    
    // default parameters other than quantity is 6 (default is 1)
    generateColours({ quantity: 6 });
    
    // will return
    // => [
    //     'hsl(0, 80%, 50%)',   // red
    //     'hsl(60, 80%, 50%)',  // yellow
    //     'hsl(120, 80%, 50%)', // green
    //     'hsl(180, 80%, 50%)', // cyan
    //     'hsl(240, 80%, 50%)', // blue
    //     'hsl(300, 80%, 50%)'  // magenta
    // ]
    

    随机播放参数。默认情况下,它设置为 false。当设置为 true 时,它​​使用 Fisher-Yates Shuffle(一种无偏的随机播放算法)在返回颜色数组之前对其进行随机播放。

    generateColours({ quantity: 6, shuffle: true });
    
    // might return
    // => [
    //     'hsl(60, 80%, 50%)',  // yellow
    //     'hsl(180, 80%, 50%)', // cyan
    //     'hsl(0, 80%, 50%)',   // red
    //     'hsl(240, 80%, 50%)', // blue
    //     'hsl(300, 80%, 50%)', // magenta
    //     'hsl(120, 80%, 50%)'  // green
    // ]
    

    订单参数。更改数组中颜色的顺序(始终从红色开始 - 如果偏移量为 0)。订单可以是“0,360”或“360,0”。默认值为“0,360”。 注意:如果 shuffle 设置为 true,顺序将无效。

    generateColours({ quantity: 6, order: "360,0" });
    
    // will return
    // => [
    //     'hsl(360, 80%, 50%)', // red
    //     'hsl(300, 80%, 50%)', // magenta
    //     'hsl(240, 80%, 50%)', // blue
    //     'hsl(180, 80%, 50%)', // cyan
    //     'hsl(120, 80%, 50%)', // green
    //     'hsl(60, 80%, 50%)'   // yellow
    // ]
    

    偏移参数。按指定量偏移每种颜色的色调。默认为 0。偏移量可以是正数或负数。

    generateColours({ quantity: 3, offset: 30 });
    
    // will return
    // => [
    //     'hsl(30, 80%, 50%)'  // orange
    //     'hsl(150, 80%, 50%)' // cyan - green
    //     'hsl(270, 80%, 50%)' // violet
    // ]
    

    饱和度和亮度是不言自明的。我在下面附加了一个 sn-p,以便您可以使用不同的参数。享受。希望它可以帮助任何人。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Colour Generation</title>
        <style>
        *, ::before, ::after {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
    
        body {
            min-height: 100vh;
            height: fit-content;
        }
    
        div.form {
            padding: 2rem;
            display: flex;
            flex-direction: column;
            align-items: start;
        }
        div.form div.inputs {
            display: grid;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: 1fr 1fr;
            grid-gap: 1rem;
        }
        div.form div.inputs div.wrapper { width: fit-content; }
        div.form div.inputs div.wrapper h2 { display: inline-block; }
        div.form div.buttons { display: flex; }
        div.form div.buttons > div { margin-right: 2rem; padding-left: 0.6rem; border-left: 3px solid rgb(78, 175, 255); }
        div.form div.buttons > div > label { margin-left: 0.3rem; }
        div.form > * { margin-bottom: 1rem; }
        div.form input[type="number"], div.form button {
            padding: 0.6rem 0.8rem;
            outline: none;
            border-radius: 0.2rem;
            font-family: sans-serif;
            font-size: inherit;
            font-weight: 500;
        }
        div.form input[type="number"] {
            box-sizing: border-box;
            height: fit-content;
            border: 2px solid rgb(179, 179, 179);
            transition: border-color 200ms ease-out;
        }
        div.form input[type="number"]:focus {
            border-color: rgb(45, 160, 255);
        }
        div.form button {
            border: 2px solid rgb(78, 175, 255);
            background-color: rgb(78, 175, 255);
            transition: background-color 200ms ease-out, border-colour 200ms ease-out;
        }
        div.form button:hover {
            border-color: rgb(45, 160, 255);
            background-color: rgb(45, 160, 255);
        }
        div.form button:active {
            transform: scale(0.98);
        }
        div.form h3 { margin-bottom: 0.25rem; font-family: sans-serif }
    
        div.container {
            padding: 0rem 1.5rem 2rem 1.5rem;
            display: flex;
            flex-wrap: wrap;
        }
    
        div.container div.circle {
            width: 5rem;
            height: 5rem;
            border-radius: 100%;
            margin: 0.5rem;
        }
    
      </style>
    </head>
    <body>
        <div class="form">
            <div class="inputs">
                <div class="wrapper">
                    <h3>Amount</h3>
                    <input type="number" placeholder="no. of circles (def. = 1)" id="amount" value="1">
                </div>
                <div class="wrapper">
                    <h3>Lightness</h3>
                    <input type="number" placeholder="lightness (def. = 50)" id="lightness" value="50">%
                </div>
                <div class="wrapper">
                    <h3>Hue Offset</h3>
                    <input type="number" placeholder="hue offset (def. = 0)" id="offset" value="0">
                </div>
                <div class="wrapper">
                    <h3>Saturation</h3>
                    <input type="number" placeholder="saturation (def. = 80)" id="saturation" value="80">%
                </div>
            </div>
            <div class="buttons">
                <div class="checkboxes">
                    <h3>Shuffle</h3>
                    <input type="radio" id="shuffle-first" name="shuffle" checked>
                    <label for="shuffle-first">False</label><br>
                    <input type="radio" id="shuffle-second" name="shuffle">
                    <label for="shuffle-second">True</label>
                </div>
                <div class="radio-buttons">
                    <h3>Order</h3>
                    <input type="radio" id="order-first" name="order" checked>
                    <label for="order-first">0,360</label><br>
                    <input type="radio" id="order-second" name="order">
                    <label for="order-second">360,0</label>
                </div>
            </div>
    
            <button>Generate circles with equally spaced colours</button>
        </div>
        
        <div class="container">
        </div>
    
        <script>
          const inpAmount = document.querySelector("input#amount");
          const inpOffset = document.querySelector("input#offset");
          const inpLightness = document.querySelector("input#lightness");
          const inpSaturation = document.querySelector("input#saturation");
          const radioShuffleFirst = document.querySelector("input#shuffle-first");
          const radioOrderFirst = document.querySelector("input#order-first");
          const btn = document.querySelector("button");
          const parentDiv = document.querySelector("div.container");
    
          const validNumber = ({ value, min = undefined, max = undefined, integer = false, ignoreStrings = [] }) => {
              if (typeof value != "string" && typeof value != "number") return console.error(`Value must be a number or string.`);
    
              let valid = true;
              let messages = [];
    
              for (let i = 0; i < ignoreStrings.length; i++) {
                  value = String(value).split(ignoreStrings[i]).join("");
              }
    
              if (String(value) == "" || value == null || isNaN(value)) {
                  valid = false;
                  messages.push("Not a Number");
              }
              if (!messages.includes("Not a Number") && min && Number(value) < min) {
                  valid = false;
                  messages.push(`Minimum Value is ${min}`);
              }
              if (!messages.includes("Not a Number") && max && Number(value) > max) {
                  valid = false;
                  messages.push(`Maximum Value is ${max}`);
              }
              if (!messages.includes("Not a Number") && integer && !(!isNaN(value) && parseInt(Number(value)) == value && !isNaN(parseInt(value, 10)))) {
                  valid = false;
                  messages.push("Not an integer");
              }
    
              return { valid, messages };
          }
    
          // returns an array of equally spaced colours!
          const generateColours = ({ quantity = 1, shuffle = false, order = "0,360", offset = 0, saturation = 80, lightness = 50 }) => {
              let colours = [];
              for (let i = 0; i < quantity; i++) {
                  let hue;
                  if (order == "0,360") hue = ((360/quantity) * (quantity+i)) - 360;
                  if (order == "360,0") hue = (360/quantity) * (quantity-i);
    
                  hue += offset;
    
                  colours.push(`hsl(${hue}, ${saturation}%, ${lightness}%)`);
              }
    
              if (shuffle) {
                  // uses the Fisher-Yates Shuffle to shuffle the colours
                  let currentIndex = colours.length, randomIndex;
    
                  while (currentIndex != 0) {
                      randomIndex = Math.floor(Math.random() * currentIndex);
                      currentIndex--;
                      [colours[currentIndex], colours[randomIndex]] = [colours[randomIndex], colours[currentIndex]];
                  }
              }
    
              return colours;
          }
    
          const generateCircles = (args) => {
              // get an array of equally spaced colours
              const colours = generateColours(args);
    
              parentDiv.innerHTML = "";
    
              for (let i = 0; i < args.quantity; i++) {
                  const elem = document.createElement("div");
                  elem.className = "circle";
    
                  // random colour for a circle
                  elem.style.backgroundColor = colours[i];
                  parentDiv.appendChild(elem);
              }
          }
    
          btn.addEventListener("click", () => {
              let quanInput = validNumber({ value: inpAmount.value, min: 1, max: 1000, integer: true, ignoreStrings: [" "] });
              let offsetInput = validNumber({ value: inpOffset.value, min: -360, max: 360, ignoreStrings: [" "] });
              let lightInput = validNumber({ value: inpLightness.value, min: 0, max: 100, ignoreStrings: ["%", " "] });
              let satInput = validNumber({ value: inpSaturation.value, min: 0, max: 100, ignoreStrings: ["%", " "] });
    
              if (quanInput.valid && offsetInput.valid && lightInput.valid && satInput.valid) {
                  let arguments = {
                      quantity: Number(inpAmount.value),
                      shuffle: (radioShuffleFirst.checked) ? false : true,
                      order: (radioOrderFirst.checked) ? "0,360" : "360,0",
                      offset: Number(inpOffset.value),
                      saturation: Number(inpSaturation.value),
                      lightness: Number(inpLightness.value)
                  }
    
                  generateCircles(arguments);
              }
              else {
                  quanInput.messagesStr = "";
                  quanInput.messages.forEach((str, i) => quanInput.messagesStr += `    ${i+1}. ${str}\n`);
                  offsetInput.messagesStr = "";
                  offsetInput.messages.forEach((str, i) => offsetInput.messagesStr += `    ${i+1}. ${str}\n`);
                  satInput.messagesStr = "";
                  satInput.messages.forEach((str, i) => satInput.messagesStr += `    ${i+1}. ${str}\n`);
                  lightInput.messagesStr = "";
                  lightInput.messages.forEach((str, i) => lightInput.messagesStr += `    ${i+1}. ${str}\n`);
                  alert(`Field_1 - Quantity:\n${quanInput.messagesStr}\nField_2 - Offset:\n${offsetInput.messagesStr}\nField_3 - Lightness:\n${lightInput.messagesStr}\nField_4 - Saturation:\n${satInput.messagesStr}`);
              }
          });
        </script>
    </body>
    </html>

    【讨论】:

    • 方法2很好的解决了上面的问题,只是颜色数量限制在360以内。是吗?
    • 想走多高就走多远,只需要一双很好的眼睛就可以360度或更多的颜色分辨。我将使用这两种方法添加 2 个 sn-ps,以便您查看。
    【解决方案2】:

    我想到的一种方法:

    如果您以相等的间隔绕过 HSV 色轮,那么您会得到不同的颜色。 HSV 在 [0,1] 的范围内。除非您需要几十种颜色,否则将 Saturation 和 Value 设置为 1。

    创建您想要的颜色数量数组的方法:

    const colorCount = 24;
    const dh = 1 / colorCount;
    let colors = [];
    for(let i=0;i<colorCount;i++) {
         let rgb = HSVtoRGB(dh*i,1,1);
         colors.push(rgb);
    }
    
    // And our helper function:
    
    /* accepts parameters
     * h  Object = {h:x, s:y, v:z}
     * OR 
     * h, s, v
    */
    function HSVtoRGB(h, s, v) {
        var r, g, b, i, f, p, q, t;
        if (arguments.length === 1) {
            s = h.s, v = h.v, h = h.h;
        }
        i = Math.floor(h * 6);
        f = h * 6 - i;
        p = v * (1 - s);
        q = v * (1 - f * s);
        t = v * (1 - (1 - f) * s);
        switch (i % 6) {
            case 0: r = v, g = t, b = p; break;
            case 1: r = q, g = v, b = p; break;
            case 2: r = p, g = v, b = t; break;
            case 3: r = p, g = q, b = v; break;
            case 4: r = t, g = p, b = v; break;
            case 5: r = v, g = p, b = q; break;
        }
        return {
            r: Math.round(r * 255),
            g: Math.round(g * 255),
            b: Math.round(b * 255)
        };
    }
    

    来源:Javascript convert HSB/HSV color to RGB accurately

    【讨论】:

      猜你喜欢
      • 2023-03-21
      • 1970-01-01
      • 2016-08-07
      • 1970-01-01
      • 2017-02-07
      • 2010-09-15
      • 2015-05-29
      • 1970-01-01
      • 2022-10-16
      相关资源
      最近更新 更多