В таймере не только Invalidate делать, но ещё и изменять состояние анимации. Поскольку в OnPaint отрисовывается текущее состояние. И отрисовывается не только по событию таймера.
И вот тут уже как раз начинается ООП. Т.е. алгоритм анимации заменяется на объект с состоянием и поведением. Поэтому, кстати, разумно отрисовку поручить самому объекту, давая ему контекст для рисования. Ну а в перспективе вообще выйти на архитектуру Model-View-Controller.
поддерживаю. завести в базовом классе private поля для текущей отрисовки, в OnTimer их заполнять public-методом, в OnPaint отрисовывать объект по значениям его же полей.
Но я в это, равно как и в полиморфизм, даже не вглядывался. Я первым делом увидел OnClick, и этого мне хватило
Для понимания способа реализации полиморфизма в языке программирования, понимания того, что такое виртуальный метод, и чем он отличается от обычного, есть такие ключевые понятия, как "раннее связывание" и "позднее связывание". Можно поискать информацию по ним. А ещё лучше самостоятельно, в обход компилятора запрограммировать механизм виртуализации метода - эта задача позволяет освоить язык досконально.
а там понимать нечего ) объект - указатель. поле объекта - указатель. метод объекта - указатель (адрес точки входа в процедуру). слегка извернувшись, объект можно нарисовать в обычном паскале, по сути - это тупая запись с кучей полей, являющихся указателями. т.е. - обычными интегерами. все остальное делает compiler magic. а эмулировать свою VMT не так уж сложно. но так освоишь не столько язык, сколько специфику багландовского компилятора (и его удвительных багов, когда, например, конструкция из нескольких вложенных with в каком-то методе срабатывает нормально, ты добавляешь лишний метод, меняется указатель в VMT, и в итоге тот метод, который ты не трогал несколько месяцев, вдруг встает раком).
Dimka,
Offtopic:
простенький пример, хоть и не совсем по теме. но поучительный.
procedure TForm1.Button1Click(Sender: TObject);
const
MessageMask = '|%d| = %d';
var
AShortInt: ShortInt;
ASmallInt: SmallInt;
ALongInt: Longint;
AInt64: Int64;
begin
AShortInt := Low (ShortInt);
ASmallInt := Low (SmallInt);
ALongInt := Low (LongInt);
AInt64 := Low (Int64);
ShowMessage (Format (MessageMask, [AShortInt, Abs (AShortInt)]) + #13 +
Format (MessageMask, [ASmallInt, Abs (ASmallInt)]) + #13 +
Format (MessageMask, [ALongInt, Abs (ALongInt)]) + #13 +
Format (MessageMask, [AInt64, Abs (AInt64)]));
end;
смысл очень простой. мы объявляем переменную целочисленного типа. (signed). присваиваем ей минимальное значение для данного типа. оно будет отрицательным, верно? берем от нее модуль (в дельфи это Abs, "абсолютное значение"). и чтобы вы думали мы получим на дельфийском компиляторе, беря модуль от отрицательного числа? ))
это очень классный вопрос на собеседовании. на моей памяти правильно на него никто не ответил (кроме чуваков, знавших ответ заранее). если не знать - врут все, но от того, как они врут - видно, насколько люди знают среду. условно их можно разбить на три категории.
1. самоуверенные дебилы. эти уверены, что это вопрос на знание синтаксиса, синтаксис они знают, и они гордо отвечают, что результат будет равен Low (<имя_типа>), только с плюсом. и в первых двух случаях, для SmallInt (-128..127, -32768..32767) - это действительно так. а вот для остальных...
2. умные парни. эти помнят, что минимальное значение для типа на 1 больше (по модулю), чем максимальное. как задается знаковое (signed) число? вернее, как задается его знак? он задается старшим значащим битом. т.е. 128 у signed byte быть не может, это будет -128, максимальное положительное значение для этого типа - это 127. дальше умные парни строят версии. компилятор заругается, будет ошибка выхода за границы диапазона, будет ошибка переполнения памяти, в конце концов - будет максимальное значение, т.е. на 1 меньше, но положительное. все они по-своему правы, но багландовская реальность намного более сурова.
3. гении. это люди, не только знающие синтаксис, не только понимающие, как в памяти хранятся знаковые числа, но и понимающие, как компилятор выделяет память под вычисление выражения, и, главное, какими блоками выделяется эта память. а в версиях дельфи, старше семерки память под вычисление выражений выделяется блоками, кратными 4 байтам.
и что получается. для вычисления выражения менеджер памяти дельфи выделит память, достаточную, для хранения самого большого выражения. в первых двух случаях это один байт и два байта соответственно. но выделено будет значение, кратное 4, т.е. реально будет выделено 4 байта, модуль |-128| = 128, и он прекрасно в эти 4 байта ляжет. компилятор покажет правильный ответ (хотя и нечайно). а вот для хранения лонгинт или инт64 нужны 4 и 8 байт соответственно. и для вычисления выражения ровно столько им и будет выделено. что произойдет? модуль |-2147483648| = 2147483648, но это значение "не влезет" в отведенный объем памяти, потому что лишняя единица - это та самая единица, что отвечает за значащий бит. и что мы получим в итоге? правильно:
скажу честно, я на этом тесте позорно облажался. потому что надо быть не умником, и не гением, надо быть конченным параноиком, чтобы предположить, что компилятор не самой захудалой среды, вычисляя модуль отрицательного числа, может получить отрицательное число. а вы говорите - полиморфизм ))))
[/offtopic]