2013-07-29 72 views
3

ScrollViewer中動畫滾動似乎是一項常見任務。我使用定時器實現它,類似於發現的方法here。這種方法工作得很好,非常光滑,看起來很完美。使用計時器以編程方式滾動ScrollViewer變得生澀

但是,現在我的ScrollViewer中包含的對象的複雜性和數量增加了,動畫看起來很生澀。我覺得這很奇怪,因爲如果我手動滾動它可以正常工作。

public void ShiftLeft(int speed = 11) 
    { 
     CustomTimer timer = new CustomTimer(); //DispatchTimer with "life" 
     timer.Interval = new TimeSpan(0, 0, 0, 0, 5); 
     timer.Tick += ((sender, e) => 
     { 
      scrollViewer1.ScrollToHorizontalOffset(
       scrollViewer1.HorizontalOffset - (scrollViewer1.ScrollableWidth/(gridColumnCount - 3)/speed)); 
      if (scrollViewer1.HorizontalOffset == 0) //cant scroll any more 
       ((CustomTimer)sender).Stop(); 
      ((CustomTimer)sender).life++; 
      if (((CustomTimer)sender).life >= speed) //reached destination 
       ((CustomTimer)sender).Stop(); 
     }); 
     timer.Start(); 
    } 

我的方法是否有問題導致這種奇怪的抽搐?任何想法如何解決它?

+0

您是否嘗試過異步操作? System.Timers.Timer是異步的。那麼你只需要處理UI的交叉線程操作。但這並不困難。 – ernest

回答

4

CompositionTarget.Rendering將更適合動畫的東西,因爲它會在每次幀被渲染時觸發。嘗試這樣的事情,而不是:

public void Shift(ScrollViewer target, double speed = 11, double distance = 20) 
    { 
     double startOffset = target.HorizontalOffset; 
     double destinationOffset = target.HorizontalOffset + distance; 

     if (destinationOffset < 0) 
     { 
      destinationOffset = 0; 
      distance = target.HorizontalOffset; 
     } 

     if (destinationOffset > target.ScrollableWidth) 
     { 
      destinationOffset = target.ScrollableWidth; 
      distance = target.ScrollableWidth - target.HorizontalOffset; 
     } 

     double animationTime = distance/speed; 
     DateTime startTime = DateTime.Now; 

     EventHandler renderHandler = null; 

     renderHandler = (sender, args) => 
     { 
      double elapsed = (DateTime.Now - startTime).TotalSeconds; 

      if (elapsed >= animationTime) 
      { 
       target.ScrollToHorizontalOffset(destinationOffset); 
       CompositionTarget.Rendering -= renderHandler; 
      } 

      target.ScrollToHorizontalOffset(startOffset + (elapsed * speed)); 
     }; 

     CompositionTarget.Rendering += renderHandler; 
    } 

編輯:添加範圍檢查

使用負距離值向左滾動。

編輯2:

你可能想用這個CompositionTargetEx實現,而不是CompositionTarget,因爲這隻會火當一個新的框架將實際被渲染線程得出:

https://stackoverflow.com/a/16334423/612510

編輯3:

由於你在WPF(而不是Silverlight,就​​像我更習慣),你可能使用Stopwatch類來測量已過去秒而不是我的DateTime.Now方法。

+0

這樣更好。感謝那。我正在使用像@Austin這樣的計時器事件,它是停止開始生澀。我現在在一個CompositionTarget.Rendering處理程序中運行了幾乎相同的代碼(但補償了變化時間自上次渲染更新),並且它光滑平滑! – Jon