我正在使用一般函数/程序,根据提供的数据计算淡入淡出时间和值,如下所示:
我在字节数组中保存了字节值:这些是起始值。然后我在其他一些数组中有一些记忆值:这些是新值。然后我有时间提供,这需要一个起始值来获得新值。
我需要在每次更改时获得值的更新(准确度高达0.1秒)。我知道,如果值 A 更改为10而值 B 同时更改为100,那么让我们说1秒,我会获得价值 A 更新了10次,而值 B 将更新100次。
到目前为止,我一直在计划使用计时器,间隔时间为50毫秒,这将根据变化的值和所需的时间不断计算差异,例如:change step := (Difference between start and new value / {divided by} (fade time / timer interval) )
。< / p>
但鉴于价值变化不同,衰落时间以及在第一次衰落结束之前我可以执行另一个值衰落的事实,这让我感到困惑和困难。
所以,我需要的是一个选项,让我们说,给定的索引1,2,3,4,5,6和7的值在30秒内改为新值,然后介于两者之间的某个地方,我可以将索引11,13和17的值排序为在9秒内更改为新值,等等......
此外,如果值 A 正在逐渐消失 B 正在进行中,而另一个淡出 A 到 C 将被订购,我希望它被添加到队列列表中,在第一个淡入淡出完成后立即执行。此时,第一个命令的 B 值将成为第二个命令中的 A 值。这是由于以下事实:上面示例中的 A 应始终在淡入淡出开始时读取。这样,无论在淡入淡出之前做什么,还是在淡入淡出命令和淡入淡出执行之间,它都是起始值。因此,我可以将Fade1设置为 Current - &gt; B @ 10s 并为 Current - &gt;排队Fade2 C @ 10s ,而第二种情况下的 Current 实际上是值,否则保存为 B ,让我们假设 Current < fade1中的/ em>与保存为 C 的值相同。这样,该值将在环回中,每10秒更改一次。所以基本上,添加淡入淡出的命令应该只有 SetNewFade:Dest:= B;时间:= 10;
所以我可以添加 - &gt; B @ 10s , - &gt; C @ 10s , - &gt; B @ 10s ,< em> - &gt; C @ 10s ,它只是从 B 循环到 C 并向后循环,直到队列列表为空。我希望我能够明确地说明这一点。我真的无法更好地描述我需要实现的目标。
此外,由于所有淡入淡出都将在列表框中提供,我希望能够根据需要删除队列中的淡入淡出。但是,如果当前正在运行的淡入淡出被删除,则该值应该跳到一个新值,就像淡入淡出已经完成一样,然后通常然后启动新的淡入淡入队列列表,如果有的话。任何。
如何最容易创建?使用具有固定间隔的Timer是一个好主意吗?如果很多值将等待淡入淡出会导致任何延迟吗?是使用动态数组的值和时间(并在 StartFade 事件上填充它们并在淡入淡出完成后释放它们)在黑暗中拍摄或猜测好吗?
这里有一个我希望更清楚的例子:
A: array [0..20] of Byte;
B: array [0..20] of Byte;
C: array [0..20] of Byte;
Current: array [0..20] of Byte;
Button1将 A 值应用于当前值,Button2应用 B 值,Button3应用 C em>值,依此类推......
所以我在编辑框中设置时间,让我们说5秒,然后点击Button1。有了这个,我将 Current 的淡入淡出添加到数组 A 中的值,时间为5秒。由于它是队列中的第一个,因此它立即开始执行。在淡入淡出实际完成之前,我将时间设置为20秒并按下Button2。所以我只是在队列列表中添加了另一个淡入淡出:从 Current 到数组 B 中的值。由于我改变了相同的值(索引0..20),因此在第一个淡入淡出完成之后开始执行。注意:淡入淡出过程会不断更新 Current 数组,直到它与fade命令的数组具有相同的值!因此,第二个淡入淡出将再次从当前淡化为 B , Current 实际上与 A 相同。< / p>
现在事情变得更加复杂的是当我实际设置从数组中索引0,1,2,3和4的值被褪色@ 5秒到 A ,然后我应用将索引为5,6,7,8和9的值在10秒内消隐为 B 值:在这种情况下,由于我正在衰落的索引是不同的,所以两个淡入淡出命令都应该执行右远 如果一个值都在两个渐变中(例如,如果我将第二个淡入淡出的值加上4),则只需要将此值添加到队列列表中。所以另一个消失了,而那个已经在第一个淡入淡出时消失的那个等待它完成,然后按照第二个命令开始消失。 其他一些信息: 此刻,数组的长度不固定,但如果这很重要,可以设置固定。它肯定是乘数512,最大值为2047。 数组的数量未知,需要在运行时进行修改。它们可能会被存储为记录(例如 根据上面的描述,数组的长度并不总是相等,因为它们并不总是改变相同数量的值。 在初始化时从数据库填充数组。由于它们可以在运行时创建,因此它们可以从 Current 值填充并存储为新数组。但是这也总是写在数据库中。它们是一种记忆值,所以我可以用按钮回忆它们。就此而言,我希望有一个选项可以立即回忆这些值(正如我现在所做的那样)或通过淡入淡出选项。现在,为了避免队列中的值出现问题,我还考虑通过淡入淡出过程发送立即更改,仅使用时间0秒。这样,将不会立即应用不在队列中的值,而如果某个值当前正在衰落,则将在该淡入淡出完成后应用它。也就是说,这个淡入淡出过程一直都在命令流程中。 如果需要进一步澄清,请不要犹豫! 我知道这真的很复杂,这就是我寻找你帮助的原因。任何部分帮助或建议将不胜感激。
StoredVals: array of record;
,Index: array of Integer
(值的索引;这是为了告诉哪些值存储在此记录中),以及Value: array of Byte;
(这些是例如,基于Current[StoredVals[0].Index[0]]
的淡出实际值。当前保留所有值的数据,同时记录 A , B , C 等...仅保留在该记录中索引的值的值。
答案 0 :(得分:2)
我是在一般职能/程序之后...
实际上,你似乎完成了一个完整的程序。你正在考虑整体解决这个问题,这就是为什么你有这么多问题。您需要学习在较小的部分中完成此任务,并更清楚地总结要求。目前形式的问题接近于偏离主题,它可能更适合SE Programmers。但由于这适合我的小巷,我想引导你。
我很确定这是您问题的正确摘要。
您建议使用计时器在每个间隔执行总转换的件是合理的。现在,有两种方法可以计算件:
第二种解决方案是首选,这转化为您正在寻找的以下一般程序。让我们通过假设一个项目开始简单:
function InBetweenValue(BeginValue, EndValue: Byte; Duration,
StartTick: Cardinal): Byte
var
Done: Single;
begin
Done := (GetTickCount - StartTick) / Duration;
if Done >= 1.0 then
Result := EndValue
else
Result := Round(BeginValue + (EndValue - BeginValue) * Done);
end;
使用固定间隔的定时器是个好主意吗?
使用这种方法,计时器的间隔不会影响计算:在任何给定时间InBetweenValue
的结果都是正确的。需要Timer的唯一事物正在推动进步。如果需要67 Hz刷新率,则将其间隔设置为15毫秒。如果要刷新率为20 Hz,则将间隔设置为50毫秒。
如果很多值会等待淡入淡出会导致任何延迟吗?
不,不是因为隐含的原因。所有计算所需的时间可能取决于队列的大小,但这肯定不是一个重要因素。 (如果是这样,那么你就会遇到更令人不安的问题)。可能&#34;延迟&#34;由于错过或合并的Windows计时器消息,刷新率会降低,具体取决于计算机与其执行的所有操作的繁忙程度。
正在使用动态数组来表示值和时间(并在#34; StartFade&#34;事件中填充它们并在淡入淡出完成后释放它们)在黑暗中拍摄或猜测好吗?
让我们首先分析需要处理哪些数据。存在一组任意长度的中间当前值,每个值都有自己的四个属性:开始值,结束值,转换持续时间和转换开始时间。所以你可以选择:
第一种解决方案需要保持所有五组同步的麻烦。第二个需要另一个维度。我更喜欢后者。
您是否使用数组或其他内容取决于您。选择最适合您的,最适合的目的或最佳匹配输入或所需输出的内容。是否选择静态动态数组取决于输入的可变性,并且在性能上没有任何可测量的差异。动态数组需要运行时长度管理,而静态数组则不需要。
但是既然你需要一个动态的解决方案,那么我建议不要在盒子外思考。例如,RTL没有为数组提供默认的内置管理工具,但它确实有集合类,例如, TList
。
对于本答案的其余部分,我将假设使用Object作为元素和List来决定跟踪它们。
既然已经解决了两个最紧迫的问题,那么设计就可以解决了。
列表中包含项目,每个项目都有其当前值和四个属性:开始,结束,持续时间和开始时间。每个项目都必须能够获取新的属性值。根据属性,有一个计算当前值的公式。并且有一个Timer应该自动执行这些计算的多个。
此外,应为项目存储多个转换命令。由于我们已经有一个包含成员的项目,因此我们也将这些命令添加为项目的成员。
缺少什么?不,我们走了。
我们需要:
这应该可以帮助您设置代码的接口部分。灵儿,并且渴望开始编写实现代码。
我的试用版,如上所述:
unit Modulation;
interface
uses
System.SysUtils, System.Classes, System.Generics.Collections, WinAPI.Windows,
VCL.ExtCtrls;
type
TTransition = record
EndValue: Byte;
Duration: Cardinal;
end;
TTransitions = class(TQueue<TTransition>);
TByte = class(TObject)
private
FBeginValue: Byte;
FCurrentValue: Byte;
FEndValue: Byte;
FDuration: Cardinal;
FStartTick: Cardinal;
FTransitions: TTransitions;
procedure PopTransition;
public
procedure AddTransition(ATransition: TTransition);
constructor Create;
destructor Destroy; override;
function HasTransition: Boolean;
function InTransition: Boolean;
procedure Recalculate;
property CurrentValue: Byte read FCurrentValue;
end;
TBytes = class(TObjectList<TByte>);
TByteModulator = class(TObject)
private
FItems: TBytes;
FOnProgress: TNotifyEvent;
FTimer: TTimer;
function Finished: Boolean;
function GetCurrentValue(Index: Integer): Byte;
function GetItemCount: Integer;
procedure SetItemCount(Value: Integer);
procedure Proceed(Sender: TObject);
protected
procedure DoProgress;
public
procedure AddTransition(Index: Integer; ATransition: TTransition);
constructor Create;
destructor Destroy; override;
property CurrentValues[Index: Integer]: Byte read GetCurrentValue; default;
property ItemCount: Integer read GetItemCount write SetItemCount;
property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
end;
implementation
{ TByte }
procedure TByte.AddTransition(ATransition: TTransition);
begin
if ATransition.Duration < 1 then
ATransition.Duration := 1;
FTransitions.Enqueue(ATransition);
Recalculate;
end;
constructor TByte.Create;
begin
inherited Create;
FTransitions := TTransitions.Create;
FDuration := 1;
end;
destructor TByte.Destroy;
begin
FTransitions.Free;
inherited Destroy;
end;
function TByte.HasTransition: Boolean;
begin
Result := FTransitions.Count > 0;
end;
function TByte.InTransition: Boolean;
begin
Result := FCurrentValue <> FEndValue;
end;
procedure TByte.PopTransition;
var
Transition: TTransition;
begin
Transition := FTransitions.Dequeue;
FBeginValue := FCurrentValue;
FEndValue := Transition.EndValue;
FDuration := Transition.Duration;
FStartTick := GetTickCount;
end;
procedure TByte.Recalculate;
var
Done: Single;
begin
Done := (GetTickCount - FStartTick) / FDuration;
if Done >= 1.0 then
begin
FCurrentValue := FEndValue;
if HasTransition then
PopTransition;
end
else
FCurrentValue := Round(FBeginValue + (FEndValue - FBeginValue) * Done);
end;
{ TByteModulator }
const
RefreshFrequency = 25;
procedure TByteModulator.AddTransition(Index: Integer;
ATransition: TTransition);
begin
FItems[Index].AddTransition(ATransition);
FTimer.Enabled := True;
end;
constructor TByteModulator.Create;
begin
inherited Create;
FItems := TBytes.Create(True);
FTimer := TTimer.Create(nil);
FTimer.Enabled := False;
FTimer.Interval := MSecsPerSec div RefreshFrequency;
FTimer.OnTimer := Proceed;
end;
destructor TByteModulator.Destroy;
begin
FTimer.Free;
FItems.Free;
inherited Destroy;
end;
procedure TByteModulator.DoProgress;
begin
if Assigned(FOnProgress) then
FOnProgress(Self);
end;
function TByteModulator.Finished: Boolean;
var
Item: TByte;
begin
Result := True;
for Item in FItems do
if Item.InTransition or Item.HasTransition then
begin
Result := False;
Break;
end;
end;
function TByteModulator.GetCurrentValue(Index: Integer): Byte;
begin
Result := FItems[Index].CurrentValue;
end;
function TByteModulator.GetItemCount: Integer;
begin
Result := FItems.Count;
end;
procedure TByteModulator.Proceed(Sender: TObject);
var
Item: TByte;
begin
for Item in FItems do
Item.Recalculate;
DoProgress;
FTimer.Enabled := not Finished;
end;
procedure TByteModulator.SetItemCount(Value: Integer);
var
I: Integer;
begin
for I := FItems.Count to Value - 1 do
FItems.Add(TByte.Create);
FItems.DeleteRange(Value, FItems.Count - Value);
end;
end.
一个小小的即插即用演示程序(注意标签只显示最后一个请求):
unit Unit2;
interface
uses
System.SysUtils, System.Classes, Vcl.Controls, Vcl.Forms,
VCL.ComCtrls, VCL.StdCtrls, Modulation;
type
TForm2 = class(TForm)
private
FBars: array of TProgressBar;
FLabels: array of TLabel;
FByteModulator: TByteModulator;
procedure FormClick(Sender: TObject);
procedure Progress(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
{ TForm2 }
const
Count = 10;
constructor TForm2.Create(AOwner: TComponent);
var
I: Integer;
begin
inherited Create(AOwner);
FByteModulator := TByteModulator.Create;
FByteModulator.ItemCount := Count;
FByteModulator.OnProgress := Progress;
SetLength(FBars, Count);
SetLength(FLabels, Count);
for I := 0 to Count - 1 do
begin
FBars[I] := TProgressBar.Create(Self);
FBars[I].SetBounds(10, 10 + 30 * I, 250, 25);
FBars[I].Smooth := True;
FBars[I].Max := High(Byte);
FBars[I].Parent := Self;
FLabels[I] := TLabel.Create(Self);
FLabels[I].SetBounds(270, 15 + 30 * I, 50, 25);
FLabels[I].Parent := Self;
end;
OnClick := FormClick;
end;
destructor TForm2.Destroy;
begin
FByteModulator.Free;
inherited Destroy;
end;
procedure TForm2.FormClick(Sender: TObject);
var
Transition: TTransition;
Index: Integer;
begin
Transition.EndValue := Random(High(Byte) + 1);
Transition.Duration := Random(3000);
Index := Random(Count);
FLabels[Index].Caption := Format('%d > %d @ %f',
[FByteModulator.CurrentValues[Index], Transition.EndValue,
Transition.Duration / MSecsPerSec]);
FByteModulator.AddTransition(Index, Transition);
end;
procedure TForm2.Progress(Sender: TObject);
var
I: Integer;
begin
for I := 0 to Count - 1 do
FBars[I].Position := FByteModulator.CurrentValues[I];
end;
initialization
Randomize;
end.
更迭。