【问题标题】:How to manipulate colors to give a real-time glowing effect?如何操纵颜色以提供实时发光效果?
【发布时间】:2011-12-29 00:37:05
【问题描述】:

我正在尝试制作一个当鼠标指向它时会发光的按钮。背景中必须有一个计时器来控制这些颜色的平滑淡入淡出。它不会从一种颜色跳到另一种颜色,它会逐渐消失到下一种颜色。

现在我的问题是,当它变回正常颜色时,它不会锁定到最终颜色。事实上,它一直在从亮到暗。

这是我的一个自定义按钮,我不打算发布它的代码,但是这个代码可以通过鼠标进入/退出事件和颜色属性放置到任何东西上。

它通过设置变量FMenuDestColor 来工作。 ExtractColor 根据颜色获取 RGB 值。计时器比较当前颜色和目标颜色之间的每个 RGB 通道。然后计时器修改当前颜色以淡入下一个颜色。这些计算在一个名为 CalcColorFade 的函数中完成 - 每个通道运行 3 次。

procedure ExtractColor(const Color: TColor; var R, G, B: Byte);
begin
  R:= GetRValue(Color);
  G:= GetGValue(Color);
  B:= GetBValue(Color);
end;

function CalcColorFade(const C1, C2: Byte): Byte;
const
  RGB_MULT = 1.2;
  RGB_SENS = 5;
begin
  if C1 <> C2 then begin    
    if (C1 >= C2 - RGB_SENS) and (C1 <= C2 + RGB_SENS) then
      Result:= C2
    else
      if C1 > C2 then
        Result:= EnsureRange(Trunc(C1 / RGB_MULT), 0, 255)
      else
        Result:= EnsureRange(Trunc(C1 * RGB_MULT), 0, 255);
  end else begin
    Result:= C2;
  end;
end;

procedure TfrmMain.tmrMenuGlowTimer(Sender: TObject);
var
  R1, G1, B1: Byte;
  R2, G2, B2: Byte;
  R3, G3, B3: Byte;
begin
  if MenuButton.Color <> FMenuDestColor then begin
    ExtractColor(MenuButton.Color, R1, G1, B1);
    ExtractColor(FMenuDestColor, R2, G2, B2);
    R3:= CalcColorFade(R1, R2);
    G3:= CalcColorFade(G1, G2);
    B3:= CalcColorFade(B1, B2);
    MenuButton.Color:= RGB(R3, G3, B3);
  end;
end;

procedure TfrmMain.MenuButtonMouseEnter(Sender: TObject);
begin
  FMenuDestColor:= clBlue;
end;

procedure TfrmMain.MenuButtonMouseLeave(Sender: TObject);
begin
  FMenuDestColor:= clNavy;
end;

将鼠标指向它,它会淡出到下一个颜色。但是把鼠标移开,它就不会锁定在原来颜色的位置——它在明暗之间来回摇晃。

我假设必须有一种更简洁的方法来完成我正在做的事情,我也愿意接受这些建议。

定时器的时间间隔是70,常数是1.2,改成1.1就可以了。因此,将其更改为 1.2 的一些事情搞砸了。

【问题讨论】:

  • 我认为如果您将处理 R、G 和 B 的相同代码放入一个过程中并单独调用它 3 次,您会发现您的代码更易于阅读:传入 @第一次运行时为 987654325@ 和 R2,第二次运行时为 G1G2,等等
  • @sarnold 好建议,尽管与问题无关,但无论如何我都会这样做。
  • 确实如此,这就是我将其作为评论留下的原因——我无法发现您实际上试图解决的问题。只是暗示如果它在那些块中,只修复一次而不是三次会更容易...... :)
  • @sarnold 我采纳了你的建议并做到了 - 更新了有问题的代码,更简洁:D
  • 事实上,我正在考虑像这样在自己的线程中运行每个 R/G/B 通道的想法:P 但它必须是更大范围的东西才能需要 3 个线程.

标签: delphi math colors delphi-7


【解决方案1】:

为什么不直接切换到 HSB/HSV 模式?

然后您可以设置色调 (0-359),并控制饱和度、亮度和 Alpha。

后三个中的每一个都为您提供滚动发光效果的选项。

【讨论】:

  • 很酷,但它是否包括从一种颜色混合到另一种颜色,例如从蓝色到绿色?我的目标是从一种颜色变为另一种颜色,而不仅仅是更亮或更暗。
  • 如果滚动色调,颜色会改变,但保持相对亮度和饱和度不变。
  • 明白了,但是如何从任何一种颜色变为任何其他颜色呢?红色到绿色,蓝色到紫色,青色到黑色,白色到天蓝色,任何指定的 2 种颜色,我不知道它会如何工作。
  • 如果颜色在频率上相邻,您可以滚动这些值。如果你想做一个不相邻的渐变,你必须在数学上多思考一些。我想,找到颜色 A 的色调范围,找到颜色 B 的色调范围,然后在旧电影中制作类似的东西,并使用 alpha(并且可能是饱和/明亮)在它们之间混合两个重叠的矩形。您还可以在色调范围内抖动。多多!
  • @gotafex 毫无疑问它应该可以工作,但听起来有点复杂(至少对我来说),因为主要是 - 我从未使用过这些属性。我尽量不去“修复”不是“损坏”的东西,这意味着它可以工作,而且我对它很满意,所以我不会把它改成我不太满意的东西,除非我看到证据这比我现在拥有的要好一些(坦率地说,我希望按照您的建议查看一些示例):D
【解决方案2】:

通过将另一个常量 RGB_SENS 调整为 7 解决了问题。

所以似乎我选择RGB_MULT 越高,我就必须选择越高RGB_SENS

首先获取控件的当前颜色,然后获取目标颜色,将这些颜色中的每一个拆分为 RGB 通道,然后对于这些通道中的每一个,它执行此函数以获取“新”RGB 值...

function DoCalc(const Curr, Dest: Byte): Byte;
const
  RGB_MULT = 1.2; //How much color change per step
  RGB_SENS = 10;  //Distance from dest +/- until "locked" into dest
begin
  if Curr <> Dest then begin
    if (Curr >= Dest - RGB_SENS) and (Curr <= Dest + RGB_SENS) then begin
      Result:= Dest; //Lock color into destination (sensitivity)
    end else begin
      if Curr > Dest then begin //Needs to go down
        Result:= EnsureRange(Trunc(Curr / RGB_MULT), 0, 255);
      end else begin            //Needs to go up
        Result:= EnsureRange(Trunc(Curr * RGB_MULT), 0, 255);
      end;
    end;
  end else begin
    Result:= Dest; //Just return the dest color, no change
  end;
end;

以前,RGB_MULT 曾经设置为1.1,效果很好。但是当我将它增加到1.2 时,我的问题就开始了。当时,RGB_SENS 被设置为5RGB_SENS 确定距离目标颜色多远,直到颜色应“锁定”到位。显然,1.2 导致它永远不会落在这个范围内。将RGB_SENS 扩展为7(现在是10)后,它工作正常。

【讨论】:

  • 有趣的是我为这个东西写了数学,但无法用语言解释它实际上是如何工作的:P 我倾向于用代码语言阅读/说话,我无法将代码翻译成普通的英语那么好...
  • 这很常见。不要试图强迫它变成语言,有时它会破坏逻辑的美感。 :P
猜你喜欢
  • 2016-07-05
  • 2015-11-28
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 2011-02-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多