2012-06-09 9 views
5

У меня есть изображение .bmp с макетом комиксов. В настоящее время мой код работает так. Если я нажимаю правой кнопкой мыши и удерживаю нажатой кнопку мыши, я могу нарисовать поле типа шатер вокруг одного из фреймов на странице комикса. Когда я отпущу кнопку, она будет увеличиваться в этом фрейме. Но его мгновение. И я бы хотел, чтобы у него был эффект анимации.Математика для медленного увеличения изображения

Таким образом, вместо того, чтобы идти и установка значения PicRect к «END VALUE»

PicRect.Left 
PicRect.right 
PicRect.top 
PicRect.bottom 

, как показано в коде ниже, мне нужен способ, чтобы медленно попасть, своего рода в то время как цикл, который устанавливает те значения немного за раз, пока он не достигнет «конечного значения». Но я не уверен на 100% о том, как эта математика работает. Ни один из моих попыток while не делает ничего, кроме масштабирования слишком далеко. Это процедура.

procedure TZImage.MouseUp(Button: TMouseButton; Shift: TShiftState; 
         X, Y: Integer); 
    var coef:Double; 
    t:integer; 
begin 
    if FMouse=mNone then Exit; 
    if x>ShowRect.Right then x:=ShowRect.Right; 
    if y>ShowRect.Bottom then y:=ShowRect.Bottom; 
    if FMouse=mZoom then begin //calculate new PicRect 
    t:=startx; 
    startx:=Min(startx,x); 
    x:=Max(t,x); 
    t:=starty; 
    starty:=Min(starty,y); 
    y:=Max(t,y); 
    FMouse:=mNone; 
    MouseCapture:=False; 
//enable the following if you want to zoom-out by dragging in the opposite direction} 
    {  if Startx>x then begin 
      DblClick; 
      Exit; 
     end;} 
     if Abs(x-startx)<5 then Exit; 
     if (x - startx < y - starty) then 
     begin 
      while (x - startx < y - starty) do 
      begin 
       x := x + 100; 
       startx := startx - 100; 
      end; 
     end 
     else if (x - startx > y - starty) then 
     begin 
      while (x - startx > y - starty) do 
      begin 
       y := y + 100; 
       starty := starty - 100; 
      end; 
     end; 


    //This is were it sets the zoom info. This is were 
    //I have to change to slowly get the PICRECT.Left/right/top/bottom 
     if (PicRect.Right=PicRect.Left) 
     then 
      coef := 100000 
     else 
      coef:=ShowRect.Right/(PicRect.Right-PicRect.Left); 
     PicRect.Left:=Round(PicRect.Left+startx/coef); 
     PicRect.Right:=PicRect.Left+Round((x-startx)/coef); 
     if (PicRect.Bottom=PicRect.Top) 
     then 
      coef := 100000 
     else 
      coef:=ShowRect.Bottom/(PicRect.Bottom-PicRect.Top); 
     PicRect.Top:=Round(PicRect.Top+starty/coef); 
     PicRect.Bottom:=PicRect.Top+Round((y-starty)/coef); 
     end; 
     if FMouse=mDrag then begin 
     FMouse:=mNone; 
     Canvas.Pen.Mode:=pmCopy; 
     Screen.Cursor:=crDefault; 
     end; 
     Invalidate; 
    end; 

Я считаю, что это может быть сделано в коде выше. но также хотел добавить этот incase, который он помогает.

type 
    TZImage = class(TGraphicControl) 
    private 
    FBitmap  : TBitmap; 
    PicRect  : TRect; 
    ShowRect  : TRect; 
    FShowBorder : boolean; 
    FBorderWidth : integer; 
    FForceRepaint : boolean; 
    FMouse   : (mNone, mDrag, mZoom); 
    FProportional : boolean; 
    FDblClkEnable : boolean; 
    startx, starty, 
    oldx, oldy  : integer; 

благодарит за любую помощь в получении этой работы.

ответ

6

У меня есть несколько предложений; Я не уверен, что их будет достаточно, чтобы решить вашу проблему, но я надеюсь, что это поможет вам добраться туда.

Во-первых, ваши while петли делают изрядное количество смешных пустячный:

if (x - startx < y - starty) then 
    begin 
     while (x - startx < y - starty) do 
     begin 
      x := x + 100; 
      startx := startx - 100; 
     end; 
    end 
else if (x - startx > y - starty) then 
    /* similar code */ 

Обратите внимание, что x - start == y - starty дело находится полностью упускается из виду. Я не знаю, имеет ли это значение.

Во-вторых, это, вероятно, может быть переписано без цикла. Я предполагаю, что здесь, это займет немного тестирование, чтобы убедиться, что это правильно, но это чувствует, как правильный путь:

foo := (x - startx) - (y - starty) 
if (foo > 200 || foo < -200) 
    bar = foo/200 # I assume integer truncation 
    x += bar * 100 
    startx += bar * 100 

Я не совсем уверен, почему вы пытаетесь получить (x-startx) - (y-starty) к в пределах 200 друг от друга; может быть что-то еще лучше.

Этот раздел кода немного запутанным:

if (PicRect.Right=PicRect.Left) 
    then 
     coef := 100000 
    else 
     coef:=ShowRect.Right/(PicRect.Right-PicRect.Left); 
    PicRect.Left:=Round(PicRect.Left+startx/coef); 
    PicRect.Right:=PicRect.Left+Round((x-startx)/coef); 
    if (PicRect.Bottom=PicRect.Top) 
    then 
     coef := 100000 
    else 
     coef:=ShowRect.Bottom/(PicRect.Bottom-PicRect.Top); 
    PicRect.Top:=Round(PicRect.Top+starty/coef); 
    PicRect.Bottom:=PicRect.Top+Round((y-starty)/coef); 
    end; 

должен ли coef быть перезаписаны из ранее? Или, если вы вместо этого вычислите coefx и 10, а затем выберите значение (больше? Меньше? Ближе к 100000?) Для обслуживания как для вычислений .Left, .Right, .Top, так и .Bottom? Я должен думать, что этот код, в его нынешнем виде, скорее всего приведет к неудобному растяжению вашего контента таким образом, который, вероятно, будет раздражать пользователей и авторов обоих.

Теперь, чтобы решить причину реального, почему ты здесь, оживляющий зум - вы, вероятно, нужно коренным образом изменить что-то . Я чувствую, что ваши петли while, вероятно, были предназначены для масштабирования, но они пришли после расчетов coef, поэтому я предположил, что они предназначены для чего-то другого.Но как только вы выясните, где именно поместить цикл для вычисления разных значений coef в диапазоне от «без масштабирования» до «конечного увеличения», вам также необходимо будет добавить звонки в repaint дисплей - или, в зависимости от вашей среды, может потребоваться добавить код обратного вызова, запускаемый таймером каждые 50 мс или что-то перерисовать с обновленными значениями coef.

+0

Проблема была бы растянута, с этим добавленным циклом он теперь останавливает растяжку. X - start == y - starty работает так же, если закомментирован. Я попробую это, пока цикл не увидит, как это работает, спасибо за это. Я не уверен, что КОЭФ предположил бы даже сделать :(Я решил, что цикл while заменит раздел кода, который вы назвали немного запутанным. .. В то время как цикл был выше, нужно было удалить растягивание. –

+0

будет принимать, как это помогло мне получить все от работы! –

+0

Что было бы _awesome_, если вы добавите ответ с кодом _fixed_, кто-то другой, пытающийся сделать анимацию масштабирования изображения, вероятно, очень хотел бы знать, что конкретно необходимо изменить. – sarnold

0

Я на самом деле работаю с Гленом в этом проекте. Я написал часть кода, поэтому я хотел бы уточнить, что он делает. Я оставил комментарии, потому что я быстро бросил их вместе. Большая часть кода здесь используется через открытую лицензию, которую мы нашли. Код изначально не имеют петель:

if (x - startx < y - starty) then 
     begin 
     while (x - startx < y - starty) do 
     begin 
      x := x + 100; 
      startx := startx - 100; 
     end; 
     end 
     else if (x - startx > y - starty) then 
     begin 
      while (x - startx > y - starty) do 
      begin 
       y := y + 100; 
       starty := starty - 100; 
      end; 
     end; 

Это то, что я добавил, и он был добавлен, потому что исходный код не работает так, как мы думали, что это будет работать. В основном вы выбираете область с помощью перетаскивания. Выбранная область будет увеличена, но вместо того, чтобы отображать выбранную полную область, она будет меньше, чем x-startx или y-starty для области просмотра. Поэтому, чтобы попытаться прояснить это, если вы выбрали выделенную область размером 50pix и шириной 100pix, она будет масштабироваться и вписываться в верхнюю часть области 50pix, заполняя область просмотра, но 100 пикселей получаются обрезанными. Стороны падают с зоны просмотра. Таким образом, код, который был добавлен здесь, заключается в том, чтобы исправить проблему, сделав то, что было меньше из двух, большего из двух. Делая это, на самом деле это будет соответствовать представлению о том, что было оригинальным, большим из двух, которое теперь является меньшим из двух. Это невероятно неряшливое исправление, но оно работает. Ваш метод также исправит эту проблему для нас, и может сделать это лучше. Большая проблема заключается в том, что если область является очень большой зоной, то 200pix не может быть достаточно, чтобы исправить разницу. Для наших целей он может работать на 85% +, но не на всех, поэтому этот код по-прежнему нуждается в работе.

Другой код, на ваш вопрос, на самом деле то, что раньше приводило нас в норму. На протяжении всего документа есть полное отсутствие комментариев, и мы все еще пытаемся собрать вместе то, что все это значит. Коф на самом деле то, что сводит меня с ума. Я даже не совсем уверен, что он должен делать в первую очередь. Я попробовал разделить coefx и coefy, и это действительно нарушает его. Коробка масштабирования намного отличается от того, что предполагалось. Насколько я могу судить, текущий метод не добавляет никакого нечетного растяжения, почему я не уверен.

Вот ссылка на код, который мы используем, но если вы хотите взглянуть на него в полном масштабе. http://www.torry.net/authorsmore.php?id=986 Zimage на этой странице.

Что касается актуального вопроса, мы не совсем понимаем, что именно делает кофр в первую очередь, поэтому внесение изменений в него приводит к тому, что мы просто нарушаем вещи и не работаем в пробной и ошибочной форме. Если вы не возражаете взглянуть на это, чтобы мы могли точно определить, что он делает, это позволит нам изменить его на правильные значения и избавиться от моего кода slop в этом процессе. Это позволило бы нам продвинуться в анимации масштабирования.

Чтобы добавить еще один вопрос об анимации. При этом это также позволит нам добавить анимацию при перемещении из одной точки масштабирования изображения в другую точку масштабирования. Для нашего приложения это будет от одной комической панели до другой, либо ниже, либо сбоку, и в большинстве случаев другого размера. Будет ли загружать между значениями слева, справа, сверху и снизу лучший способ показать этот тип анимации? Если это так, я думаю, что это также будет работать при переходе от полного изображения к первой увеличенной панели.

3

Я написал ответ на ваш предыдущий вопрос (который был удален навсегда некоторым, вероятно, хорошим смыслом, модератором stackoverflow).

Предложение в моем предыдущем ответе, что вы смотрите на примере glflow код:

http://code.google.com/p/glflow/

пример использует библиотеку GLScene, и иллюстрирует как загрузки изображений, масштабирование изображения и анимацию.

Даже если вы не используете библиотеку GLScene, я думаю, вы можете получить вдохновение для части анимации, посмотрев пример кода.

Суть его в том, что вам нужно использовать таймер для выполнения перерисовки.

Сначала разделите расстояние между начальным уровнем масштабирования и конечным уровнем масштабирования на отдельные этапы. Затем используйте таймер для выполнения этих шагов и выполните перерисовку на каждом шаге.

+0

+1 Для упоминания решения таймера. Однако я не согласен с делением расстояния на фиксированное количество интервалов таймера, поскольку эти интервалы будут неточно. – NGLN

+0

@NGLN Да, я согласен. Разделение расстояния на заранее является своего рода грубым подходом, и это может привести к неточности в некоторых случаях. Я думаю, что лучший подход в целом - использовать точное время, затраченное на вычисление позиций промежуточных кадров. – Elling

5

Не используйте время цикла, чтобы обновить уровень масштабирования, из-за двух проблем:

  1. Различные анимации длительностей, так как скорость операции масштабирования зависит от скорости кода, текущее использование CPU , модель ЦП и т. д.
  2. Блокировка графического интерфейса, потому что даже когда вы используете задержку (например, с Sleep), код работает в основном потоке, и программа оказывается неактивной.

Как sarnold и Elling уже было сказано: использовать временную устройство (например, TTimer), чтобы выполнить часть общей операции масштабирования на каждом интервале. Теперь, есть два способа вычислить эти части:

  1. Разделить общее расстояние до моста в фиксированное количество малых расстояний, установить интервал таймера общей продолжительности, разделенной на это число и обрабатывать сумму все обработанные расстояния на каждом интервале. Недостаток этого метода является двояким:
    • заданного интервал времени таймера является приблизительным и точно не будет из-за различные причинами, одна из которых являются зависимым от в системе обмена сообщений Windows,
    • Возможной грубая или негладкая анимация из-за этого.
  2. Пересчитать часть расстояния до моста за каждый интервал. Таким образом, анимация будет казаться гладкой всегда, будет ли следующий интервал в два раза больше или нет.

Я использовал второе решение в this answer к вашему related question, из которого взяты следующие соответствующие фрагменты:

procedure TZImage.Animate(Sender: TObject); 
var 
    Done: Single; 
begin 
    Done := (GetTickCount - FAnimStartTick)/FAnimDuration; 
    if Done >= 1.0 then 
    begin 
    FAnimTimer.Enabled := False; 
    FAnimRect := FCropRect; 
    end 
    else 
    with FPrevCropRect do 
     FAnimRect := Rect( 
     Left + Round(Done * (FCropRect.Left - Left)), 
     Top + Round(Done * (FCropRect.Top - Top)), 
     Right + Round(Done * (FCropRect.Right - Right)), 
     Bottom + Round(Done * (FCropRect.Bottom - Bottom))); 
    Invalidate; 
end; 

procedure TZImage.Zoom(const ACropRect: TRect); 
begin 
    FPrevCropRect := FCropRect; 
    FAnimRect := FPrevCropRect; 
    FCropRect := ACropRect; 
    FAnimStartTick := GetTickCount; 
    FAnimTimer.Enabled := True; 
end; 

Объяснение:

  • FCropRect это новый трансфокации прямоугольник, и FPrevCropRect является предшествующим,
  • FAnimRect - это прямоугольник между и, в зависимости от хода анимации,
  • FAnimStartTick это время, на котором операция масштабирования запускается с помощью вызова Zoom,
  • на каждом интервале таймера (набор до 15 мс, ~ частота обновления 67Гц), Animate называется,
  • Done процент прогресса анимации,
  • Invalidate вызывает перерисовку, который рисует графику в FAnimRect.
+3

Это ответ _superb_. Отличная информация, хороший совет по пересчету анимации каждый такт и очень хороший пример кода. (_Elegant_ пример кода.) – sarnold

+1

@sarnold Вау, щедрость! Это очень щедро, спасибо заранее. – NGLN

+2

Время от времени я сталкиваюсь с чем-то - элегантным - ему просто нужно какое-то особое признание. :) – sarnold

Смежные вопросы