【问题标题】:How to create color gradation for HTML shape based on scale of values using Perl如何使用 Perl 根据值的比例为 HTML 形状创建颜色渐变
【发布时间】:2013-11-28 14:06:33
【问题描述】:

我有以下数据:

my %col1 = ( 'foo' => 1,
             'bar' => 1 );

my %col2  = ('foo' => ['cat1','cat1','cat2'],
             'bar' => ['cat3','cat2','cat3'] );

my %col3  = ('foo' => [2.3,1.2,1.0],
             'bar' => [7.4,4.3,2.2]);

我想做的是创建一个看起来像这样的 HTML 表格 但在第 4 列中,它包含渐变颜色的形状。 颜色的强度由col3 中的分数给出。 形状可能只是一个简单的项目符号(• HTML:•

foo cat1,cat1,cat2 2.3,1.2,1    
bar cat3,cat2,cat3 7.4,4.3,2.2

下图说明了我打算做什么:

每个颜色类别(第 3 列)的强度范围为 1-10。

最好的方法是什么?

我遇到问题的关键不是创建 HTML 表,我可以做到。 但更多的是如何使用渐变颜色创建基于 HTML 的形状。

我被以下代码卡住了:

foreach my $nm (keys %col1) {
   my @cats = @{$col2{$nm}};
   my @vals = @{$col3{$nm}};

   print $nm, " ", join(",",@cats), " " ,join(",",@vals), "\n";
}

【问题讨论】:

标签: html perl html-table cgi


【解决方案1】:

如果我正确理解了您的问题,那么您并不真正想要具有颜色渐变的形状。您真正想要的是纯色对象,其颜色由两个参数决定:

  • 类别(在您的示例中为 cat1、cat2 和 cat3)
  • 着色程度(第 3 列中的权重)

此外,您示例中的颜色似乎遵循一个模型,其中较小的数字更接近白色,较大的数字更接近黑色,中间的数字是饱和颜色。在色彩空间方面,这表明类似于Hue, Saturation, Lightness color space,如维基百科的这张图片所示:

为了将其映射回上面的示例,我怀疑接近 0.0 的权重会靠近双锥体的顶部,接近 10.0 的权重会靠近双锥体的底部,并且您的类别所代表的颜色有由这些权重调整的固定色调和饱和度(理论上是在双锥赤道附近)。

以下 perl 代码将 HSL 颜色转换为 RGB。它是adapted from this StackOverflow answer,它又改编自我上面链接的维基百科页面。这只是必要解决方案的一部分:

##  
##  Converts an HSL color value to RGB. Conversion formula
##  adapted from http://en.wikipedia.org/wiki/HSL_color_space.
##  Assumes h, s, and l are contained in the set [0, 1] and
##  returns r, g, and b in the set [0, 255].
##  
##  @param   Number  h       The hue
##  @param   Number  s       The saturation
##  @param   Number  l       The lightness
##  @return  Array           The RGB representation
##  
sub hsl_to_rgb
{
    my ($h, $s, $l) = @_;
    my ($r, $g, $b);

    if ($s == 0)
    {
        $r = $g = $b = $l; ## achromatic
    } else
    {
        sub hue2rgb
        {
            my ($p, $q, $t) = @_;

            while ($t < 0) { $t += 1;                                   }
            while ($t > 1) { $t -= 1;                                   }
            if ($t < 1/6)  { return $p + ($q - $p) * 6 * $t;            }
            if ($t < 1/2)  { return $q;                                 }
            if ($t < 2/3)  { return $p + ($q - $p) * (2/3 - $t) * 6;    }
            return $p;
        }

        my $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
        my $p = 2 * $l - $q;
        $r = hue2rgb($p, $q, $h + 1/3);
        $g = hue2rgb($p, $q, $h);
        $b = hue2rgb($p, $q, $h - 1/3);
    }

    return [ int($r * 255), int($g * 255), int($b * 255) ];
}

此函数采用形式为 (0, 1.0, 0.5) 的 HSL 颜色表示纯红色,并返回形式为 [ 255, 0, 0 ] 的 RGB 三元组。这给了你一个难题。

根据我从您的问题陈述中可以看出,您有一组基色,您可能可以用色调和饱和度来表示,按类别排序。像这样的:

my %cat_colors = 
(           #   Hue   Saturation
    cat1 => [ 0.00000, 1.00000 ],   # Red
    cat2 => [ 0.16667, 1.00000 ],   # Yellow
    cat3 => [ 0.33333, 1.00000 ],   # Green
);

OP 更新:这太棒了。要在更多类别中添加颜色,可以在此网站中查看“色调”值:http://www.color-hex.com/。请注意,例如,该网站声明红色的 HSL 为:0.00 1.00 0.50

注意:您肯定会想玩这些颜色。如果您有 Photoshop 或其他可以让您根据不同颜色空间查看颜色的工具,那么这可能会帮助您为每个类别选择“基本”颜色,使其完全符合您的要求。对于上面的 hsl_to_rgb 代码,您需要将色调和饱和度值缩放到 [0, 1] 范围内。

现在我们如何将它应用到您上面给出的 weight 参数上?正如我上面所说,它似乎直接映射到 HSL 中的 lightness 参数,权重 0.0 映射到白色,权重 10.0 映射到黑色。 (至少,首先,考虑到你的草图,这似乎是一个很好的起点。)然而,在 HSL 中,1.0 是白色的,0.0 是黑色的。因此,要将重量映射到亮度,您需要这样的表达式:

$lightness = 1 - ($weight / 10);

您可以将其包装在一个函数中:

sub weight_to_light
{
    return 1 - ($_[0] / 10);
}

等式中的最后一部分是将hsl_to_rgb 返回的RGB 映射到HTML 可以使用的东西。这实际上很简单。以下简短函数将 R、G、B 值数组从 hsl_to_rgb 转换为 HTML 就绪的十六进制字符串:

sub rgb_to_html_hex
{
    return sprintf "#%.2X%.2X%.2X", @{ $_[0] };
}

所以,把它们放在一起:如果你想生成一个名为 %col4 的哈希,给出 %col2%col3 中的详细信息,其中包含你需要在 HTML 中根据着色模型为对象着色的字符串上面,你需要上面的两个函数,%cat_colors 映射,还有一些这样的代码:

my %col4;

foreach my $key (keys %col2)
{
    foreach my $i ( 0 .. scalar( @{ $col2{$key} } ) - 1 )
    {
        my $base_color = $cat_colors{ $col2{$key}->[$i] };
        my $rgb_color  = hsl_to_rgb( @$base_color,
                                     weight_to_light( $col3{$key}->[$i] ) );

        my $html_color = rgb_to_html_hex( $rgb_color );

        $col4{$key}->[$i] = $html_color;
    }
}

生成%col4 后,您可以使用生成的 HTML 字符串为各个实体着色。我的现代 HTML 有点生疏,但我知道像 &lt;FONT COLOR="#xxxxxx"&gt; 这样的构造曾经一度有效,并且可能仍然有效。我让你根据计算的值构造语法上有效且足够现代的 HTML。

现在,您必须承认,我对您要解决的问题以及需要如何解决它做出了一些大胆的假设。不过,我认为,我在一般情况下。如果您有任何问题,请随时离开 cmets。

并且...为了您的剪切和粘贴方便,这是一个快速测试程序,我将上述所有位以及您的示例数据集复制到一个独立的、可测试的 perl 程序中。享受!最后两行使用 YAML::XS 转储 %col4 的计算值,以便我查看它们是否合理。

#!/usr/bin/perl -w

use Modern::Perl;

##  
##  Converts an HSL color value to RGB. Conversion formula
##  adapted from http://en.wikipedia.org/wiki/HSL_color_space.
##  Assumes h, s, and l are contained in the set [0, 1] and
##  returns r, g, and b in the set [0, 255].
##  
##  @param   Number  h       The hue
##  @param   Number  s       The saturation
##  @param   Number  l       The lightness
##  @return  Array           The RGB representation
##  
sub hsl_to_rgb
{
    my ($h, $s, $l) = @_;
    my ($r, $g, $b);

    if ($s == 0)
    {
        $r = $g = $b = $l; ## achromatic
    } else
    {
        sub hue2rgb
        {
            my ($p, $q, $t) = @_;

            while ($t < 0) { $t += 1;                                   }
            while ($t > 1) { $t -= 1;                                   }
            if ($t < 1/6)  { return $p + ($q - $p) * 6 * $t;            }
            if ($t < 1/2)  { return $q;                                 }
            if ($t < 2/3)  { return $p + ($q - $p) * (2/3 - $t) * 6;    }
            return $p;
        }

        my $q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
        my $p = 2 * $l - $q;
        $r = hue2rgb($p, $q, $h + 1/3);
        $g = hue2rgb($p, $q, $h);
        $b = hue2rgb($p, $q, $h - 1/3);
    }

    return [ int($r * 255), int($g * 255), int($b * 255) ];
}

sub rgb_to_html_hex
{
    return sprintf "#%.2X%.2X%.2X", @{ $_[0] };
}

my %col1 = ( 'foo' => 1, 'bar' => 1 );

my %col2  = ('foo' => ['cat1','cat1','cat2'],
             'bar' => ['cat3','cat2','cat3'] );

my %col3  = ('foo' => [2.3,1.2,1.0],
             'bar' => [7.4,4.3,2.2]);


my %cat_colors =
(            #   Hue   Saturation
     cat1 => [ 0.00000, 1.00000 ],   # Red
     cat2 => [ 0.16667, 1.00000 ],   # Yellow
     cat3 => [ 0.33333, 1.00000 ],   # Green
);

sub weight_to_light
{
    return 1 - ($_[0] / 10);
}

my %col4;

foreach my $key (keys %col2)
{
    foreach my $i ( 0 .. scalar( @{ $col2{$key} } ) - 1 )
    {
        my $base_color = $cat_colors{ $col2{$key}->[$i] };
        my $rgb_color  = hsl_to_rgb( @$base_color,
                                     weight_to_light( $col3{$key}->[$i] ) );

        my $html_color = rgb_to_html_hex( $rgb_color );

        $col4{$key}->[$i] = $html_color;
    }
}


use YAML::XS;
say Dump( \%col4 );

【讨论】:

  • 附录:如果您希望颜色渐变的端点在加权结束时在黑白之前停止一点,您可以更改weight_to_light 函数以稍微限制范围。例如,0.2 + 0.06 * $_[0] 会将权重 0 到 10 映射到亮度值 0.2 到 0.8,这实际上可能会为您的目的提供更好的效果。
  • 附录2:我稍微修改了上面的脚本以输出一个粗略的HTML表格。我的 HTML 技能很容易过时了十年或更长时间,但它仍然可以在现代 Web 浏览器中正确呈现。您可以在此处找到代码和输出:spatula-city.org/~im14u2c/stackoverflow/gradation 您会注意到,出于某种原因,我主要关注问题的 perl 部分而不是 HTML。 :-)
  • 在上面附录 1 中给出的公式中,我应该说 0.8 - 0.6 * $_[0]。上面链接中的文件“output2.html”显示了公式给出的替代颜色。除了我的基本颜色与您上面的模型不完全匹配之外,整体行为似乎与您的规格相匹配。如果我没有正确解释您的问题,请告诉我。
  • Erf... 我在 2 天后回到这个问题,注意到你的体重是 1..10 而不是 0..10。这很容易通过调整函数weight_to_light 来解决。例如,我修改后的函数变成了 0.13333 + 0.06667 * $_[0] 之类的东西。正如我上面所说,如果上述解决方案的任何方面需要重新检查,请在此处发表评论,我可以解决。
  • @neversaint:很高兴我能帮上忙。 :-) 这相当微妙,但至少我认识到你在模型扫描中试图做的事情。我自己也有过几次错误的开始,直到我完全意识到你在做什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多