【问题标题】:PHP generate rgb color to object based on unique valuePHP根据唯一值为对象生成rgb颜色
【发布时间】:2018-12-06 16:25:13
【问题描述】:

我知道以前有人问过这个问题,但我需要比 SO 提供的其他答案更多的帮助。

我目前有一个循环遍历我的数据库中的记录的 foreach 循环。每行包含一个唯一的“id”主要属性(1、2、3、4、5 等)。

在我的循环中,我需要根据其 ID 为每条记录生成一个 UNIQUE RGB 值。生成的 RGB 值将应用于绑定到该记录的 HTML 元素的文本。生成的颜色对于记录本身必须是唯一的(通过“id”),这就是我不使用循环迭代器的原因。

我已经为我创建了一个工作函数来执行此操作,但我还需要一件事 - 我需要 rgb 值在白色背景上具有大于 4:5:1 的对比度。我的功能会生成太亮的颜色,使文本难以阅读。如何修改我的函数以生成与白色背景形成鲜明对比的较暗颜色?

function makeRgbFromValue(int $value){
    $hash = md5($value);

    return implode(", ", [
        hexdec(substr($hash, 0, 2)), // r
        hexdec(substr($hash, 2, 2)), // g
        hexdec(substr($hash, 4, 2))  // b
    ]);
}

// Example output: "100, 201, 20"

// My html, using Laravel Blade syntax:

@foreach($categories as $cat)
    <a 
        href="/blog/channels/{{ $cat->slug }}"
        style="color: rgb(
           {{ makeRgbFromValue($cat->id) }}
        )"
    >
        {{ $cat->name }}
    </a>
@endforeach

也许这是不可能的......但我希望你们中的一位数学天才可以帮助我:)

【问题讨论】:

  • 使用 md5,你的增量值会在某个时候发生冲突,所以你不能保证唯一..
  • 如果他每次都使用唯一的ID,会如何碰撞? @LawrenceCherone
  • 它不必是 100% 唯一的,但我更喜欢更少的冲突而不是更多的冲突。 md5现在适合
  • 为什么不手动定义 50 或 100 种颜色并在记录中循环呢? id 1 得到颜色 1,id 2 得到颜色 2,id 101 得到颜色 1。它可以让您更好地控制输出
  • 我希望您从用户体验的角度来考虑这一点。人类的感知只能合理区分有限数量的颜色(有人说最多 7 +- 2)。因此,预先指定调色板(例如来自ColorBrewer)并根据 id 从中选择颜色可能更合理、更容易。

标签: php algorithm function math colors


【解决方案1】:

虽然翻译起来很麻烦,但 HSV 可能是一个更好的色彩空间。我喜欢选择一组饱和度 [0.75 很好],然后调整色调和数值。

this gist提升HSV/RGB转换功能:

function HSV_TO_RGB ($H, $S, $V) {
    $RGB = array(); 
    if($S == 0) { 
        $R = $G = $B = $V * 255; 
    } else { 
        $var_H = $H * 6; 
        $var_i = floor( $var_H ); 
        $var_1 = $V * ( 1 - $S ); 
        $var_2 = $V * ( 1 - $S * ( $var_H - $var_i ) ); 
        $var_3 = $V * ( 1 - $S * (1 - ( $var_H - $var_i ) ) ); 
        if       ($var_i == 0) { $var_R = $V     ; $var_G = $var_3  ; $var_B = $var_1 ; } 
        else if  ($var_i == 1) { $var_R = $var_2 ; $var_G = $V      ; $var_B = $var_1 ; } 
        else if  ($var_i == 2) { $var_R = $var_1 ; $var_G = $V      ; $var_B = $var_3 ; } 
        else if  ($var_i == 3) { $var_R = $var_1 ; $var_G = $var_2  ; $var_B = $V     ; } 
        else if  ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1  ; $var_B = $V     ; } 
        else                   { $var_R = $V     ; $var_G = $var_1  ; $var_B = $var_2 ; } 
        $R = $var_R * 255; 
        $G = $var_G * 255; 
        $B = $var_B * 255; 
    } 
    $RGB['R'] = $R; 
    $RGB['G'] = $G; 
    $RGB['B'] = $B; 
    return $RGB; 
}

然后:

$fmt = '<span style="background-color: #%s" title="%s">%s</span>' . "\n";
$fill = str_repeat('&nbsp;', 5);

$S = 0.75;
for($i=0; $i<25; $i++) {
    $hash = md5(mt_rand());
    $H =   hexdec(substr($hash, 0, 2))/255;
    $V = ( hexdec(substr($hash, 2, 2))/255 ) / 2 + 0.5; // pick from the brighter half
    $rgb = implode('', array_map(
    function($a){
        return str_pad(dechex(intval($a)), 2, '0', STR_PAD_LEFT);
    }, HSV_TO_RGB($H, $S, $V)));
    printf($fmt, $rgb, sprintf("HSV(%0.2f,%0.2f,%0.2f)", $H, $S, $V), $fill);
}

给了我们类似的东西:

【讨论】:

    【解决方案2】:

    你可以计算亮度:

    function lumdiff($R1,$G1,$B1,$R2,$G2,$B2){
        $L1 = 0.2126 * pow($R1/255, 2.2) +
              0.7152 * pow($G1/255, 2.2) +
              0.0722 * pow($B1/255, 2.2);
    
        $L2 = 0.2126 * pow($R2/255, 2.2) +
              0.7152 * pow($G2/255, 2.2) +
              0.0722 * pow($B2/255, 2.2);
    
        if($L1 > $L2){
            return ($L1+0.05) / ($L2+0.05);
        }else{
            return ($L2+0.05) / ($L1+0.05);
        }
    }
    

    返回值应大于 5 以获得最佳可读性。 发现函数here

    但是您应该从哈希中获取另一个字符以获取另一种颜色,因此更有可能发生碰撞。

    function makeRgbFromValue($value){
        $hash = md5($value);
        $brightness = 0;
        $shift = 0;
    
        while($brightness < 5 && $shift<26) {
            $color = [
                hexdec(substr($hash, $shift, 2)), // r
                hexdec(substr($hash, $shift+2, 2)), // g
                hexdec(substr($hash, $shift+4, 2))  // b
            ];
            $brightness = lumdiff($color[0], $color[1], $color[2], 255,255,255);
            $shift++;
        }
    
        return implode(", ", $color);
    }
    

    我相信在 25 个周期内会找到一种颜色 :)

    【讨论】:

    • 谢谢,我已经通过重复该函数直到亮度为“ok”,但在某些情况下,如果它在前几次尝试中没有产生足够好的颜色,循环可能会永远持续下去。
    • @amcardwell 添加了一个带有 while 循环的函数。看看吧
    猜你喜欢
    • 1970-01-01
    • 2012-06-15
    • 1970-01-01
    • 2021-07-20
    • 2016-02-29
    • 2018-01-13
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    相关资源
    最近更新 更多