В сообществе разработчиков на Delphi и Lazarus недавно была обнаружена и исправлена ошибка в компоненте TBCFluentProgressRing из библиотеки BGRAControls. Эта ошибка вызывала прерывистую анимацию кольца прогресса. В данной статье мы подробно разберем суть проблемы, предложенное решение и обсудим перспективы развития визуальных компонентов в экосистеме Pascal.
Описание проблемы
Ошибка была обнаружена в коде компонента TBCFluentProgressRing, который реализует анимированное кольцо прогресса в стиле Fluent Design от Microsoft. Пользователь hedgehog заметил, что анимация работает рывками, что значительно ухудшает пользовательский опыт.
Исходный код, содержащий ошибку, находился в методе TimerEvent:
procedure TBCFluentProgressRing.TimerEvent(Sender: TObject);
var
TickCount: QWord;
begin
TickCount:= GetTickCount64;
FAnimationTime:= (TickCount - FStartTickCount) mod FPeriod;
DiscardBitmap;
end;
Проблема заключалась в том, что вычисление времени анимации происходило в обработчике таймера, что могло приводить к нестабильности анимации из-за неравномерных вызовов таймера.
Решение проблемы
Для исправления ошибки было предложено:
Удалить вычисление времени анимации из метода TimerEvent
Перенести это вычисление в метод RedrawBitmapContent, где происходит непосредственная отрисовка компонента
Исправленный код выглядит следующим образом:
procedure TBCFluentProgressRing.TimerEvent(Sender: TObject);
begin
DiscardBitmap;
end;
А в метод RedrawBitmapContent было добавлено вычисление времени анимации:
if FIndeterminate and FTimer.Enabled then
begin
FAnimationTime:= (GetTickCount64 - FStartTickCount) mod FPeriod;
a:= 3*FAnimationTime*pi2/FPeriod - pi;
da:= 2*abs(1 - 2*FAnimationTime/FPeriod);
if da<0.01 then da:=0.01;
DoDrawArc(a-da, a+da, FLineColor);
end
Это изменение обеспечивает более плавную анимацию, так как время теперь вычисляется непосредственно перед отрисовкой, а не в обработчике таймера.
Альтернативное решение
В качестве альтернативного решения можно предложить использование высокоточного таймера с фиксированным интервалом:
Также можно рассмотреть возможность использования анимаций на основе физики, где движение будет более естественным:
procedure TBCFluentProgressRing.UpdateAnimation;
var
CurrentTime: QWord;
Elapsed: Single;
begin
CurrentTime := GetTickCount64;
Elapsed := (CurrentTime - FLastUpdateTime) / 1000; // в секундах
FLastUpdateTime := CurrentTime;
// Применяем физику к анимации
FVelocity := FVelocity + FAcceleration * Elapsed;
FAnimationTime := (FAnimationTime + FVelocity * Elapsed) mod FPeriod;
// Замедление при отсутствии взаимодействия
if Abs(FVelocity) > 0 then
FVelocity := FVelocity * (1 - FFriction * Elapsed);
end;
Обсуждение будущего визуальных компонентов
В ходе обсуждения ошибки участники сообщества затронули важную тему - необходимость модернизации визуальных компонентов для Pascal. Были высказаны следующие идеи:
Стандартные контролы устарели - их внешний вид не соответствует современным требованиям UX/UI.
BGRA позволяет создавать красивые контролы - но текущая реализация BGRAControls представляет собой набор разрозненных элементов без единого стиля.
Необходима новая система визуальных компонентов - с единым стилем, анимациями и поддержкой современных тенденций (Material Design, Fluent Design).
Подходы к модернизации
Темы vs свойства - стоит ли использовать темы (как в CSS) или множество свойств для настройки внешнего вида.
Производительность vs функциональность - как сохранить малый объем памяти при добавлении новых возможностей.
Нативные контролы vs кастомная отрисовка - использовать ли системные контролы или рисовать все самостоятельно.
Пример реализации стилизуемого кнопки:
type
TBCButtonStyle = class(TPersistent)
private
FNormal: TBGRABitmap;
FHover: TBGRABitmap;
FActive: TBGRABitmap;
// ... другие состояния
public
constructor Create;
destructor Destroy; override;
// ... методы загрузки стилей
end;
TBCModernButton = class(TBGRACustomControl)
private
FStyle: TBCButtonStyle;
FState: TBCButtonState;
procedure SetStyle(AValue: TBCButtonStyle);
protected
procedure DrawControl; override;
procedure MouseEnter; override;
procedure MouseLeave; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Style: TBCButtonStyle read FStyle write SetStyle;
// ... другие свойства
end;
Заключение
Исправление ошибки в TBCFluentProgressRing - это не только решение конкретной технической проблемы, но и повод задуматься о будущем визуальных компонентов в экосистеме Pascal. Сообщество стоит перед выбором пути развития: продолжать улучшать существующие решения или создать новую систему компонентов с нуля, учитывая современные тенденции дизайна и UX.
Для разработчиков, использующих BGRAControls, рекомендуется:
Обновить библиотеку до последней версии, чтобы получить исправление анимации
Экспериментировать с созданием собственных стилизованных компонентов на основе BGRA
Участвовать в обсуждении будущего визуальных компонентов для Pascal
Будущее визуальных компонентов в Pascal зависит от активности сообщества. Каждый разработчик может внести свой вклад - будь то исправление ошибок, создание новых компонентов или участие в обсуждении архитектуры.
Исправление ошибки анимации в компоненте TBCFluentProgressRing и обсуждение перспектив развития визуальных компонентов в экосистеме Pascal.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.