2 using System.Threading.Tasks;
4 using Tizen.NUI.BaseComponents;
5 using System.Collections.Generic;
8 namespace Tizen.NUI.Samples
10 enum TOUCH_ANIMATION_STATE
15 END_ANIMATION = NO_ANIMATION
18 public class FrameCallbackTest : IExample
21 private static string resourcePath = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
22 private static string[] BACKGROUND_IMAGE_PATH = {
23 resourcePath + "/images/FrameCallbackTest/launcher_bg_02_nor.png",
24 resourcePath + "/images/FrameCallbackTest/launcher_bg_03_nor.png",
25 resourcePath + "/images/FrameCallbackTest/launcher_bg_04_nor.png",
26 resourcePath + "/images/FrameCallbackTest/launcher_bg_05_nor.png",
27 resourcePath + "/images/FrameCallbackTest/launcher_bg_06_nor.png",
28 resourcePath + "/images/FrameCallbackTest/launcher_bg_apps_nor.png"
31 private static string[] APPS_IMAGE_PATH = {
32 resourcePath + "/images/FrameCallbackTest/launcher_ic_culinary_nor.png",
33 resourcePath + "/images/FrameCallbackTest/launcher_ic_family_nor.png",
34 resourcePath + "/images/FrameCallbackTest/launcher_ic_ent_nor.png",
35 resourcePath + "/images/FrameCallbackTest/launcher_ic_homecare_nor.png"
38 private static string[] APPS_ICON_NAME = {
45 private static string[] CONTROL_IMAGE_PATH = {
46 resourcePath + "/images/FrameCallbackTest/launcher_ic_apps_nor.png",
47 resourcePath + "/images/FrameCallbackTest/launcher_ic_settings_nor.png",
48 resourcePath + "/images/FrameCallbackTest/launcher_ic_viewinside_nor.png",
49 resourcePath + "/images/FrameCallbackTest/launcher_ic_timer_nor.png",
50 resourcePath + "/images/FrameCallbackTest/launcher_ic_internet_nor.png"
53 private static string[] CONTROL_ICON_NAME = {
61 private const int FRAME_RATE = 60;
62 private const int OBJECT_DELAY = 30;
63 private const int OBJECT_SIZE = 150;
64 private const int INITIAL_POSITION = 46;
65 private const int DEFAULT_SPACE = 9;
66 private const int DEVIDE_BAR_SIZE = 4;
68 private TextLabel text;
69 public class FrameCallback : FrameCallbackInterface
71 private int mTimeInterval;
73 private uint mContainerId;
74 private List<uint> mViewId; ///< Container of Actor IDs.
75 private List<float> mViewPosition;
77 // Movement is position difference of Container from start position of this animation to current position.
78 // Time interval between each mMovement entry is 16 milliseconds(about 60 fps)
80 // mMovement[i] + mContainerStartPosition is the position of container after i*16 milliseconds from this animation started.
81 private List<float> mMovement;
83 // mLatestMovement is actually current movement of container.
84 private float mLatestMovement;
86 // An icon of mTouchedViewIndex moves with container at the same time.
87 // Each icon of (mTouchedViewIndex -/+ i) moves as following container after i*OBJECT_DELAY milliseconds.
88 private int mTouchedViewIndex;
90 // If every icon between mLeftIndext and mRightIndex is stopped, this frame callback can be reset.
91 // Then mIsResetTouchedViewPossible becomes true.
92 private bool mIsResetTouchedViewPossible;
93 private int mLeftIndex;
94 private int mRightIndex;
96 // Total animation time from start to current.
97 private float mTotalAnimationTime;
98 // Total animation time from start to the time that last movement is added.
99 private float mPreviousTotalAnimationTime;
101 // Start position of container in this animation.
102 private float mContainerStartPosition;
103 // Position of container at the time that last movement is added.
104 private float mPreviousContainerPosition;
106 // This animation only need to save movement about (the number of view * OBJECT_DELAY) milliseconds.
107 // and this size is updated when new view is added.
108 // and, If the list of mMovement size become larger than this size, remove unnecessary entries.
109 private int mRequiredMovementSize;
110 private bool mNeedUpdateMovementSize;
113 private float mVelocity;
117 public FrameCallback()
119 mViewId = new List<uint>();
120 mMovement = new List<float>();
121 mRequiredMovementSize = 0;
122 mNeedUpdateMovementSize = false;
123 mIsResetTouchedViewPossible = false;
126 public void ResetAnimationData()
128 SetLatestMovement(0.0f);
130 mTotalAnimationTime = 0.0f;
131 mPreviousTotalAnimationTime = 0.0f;
135 public void SetTimeInterval(int timeInterval)
137 mNeedUpdateMovementSize = true;
138 mTimeInterval = timeInterval;
141 public void AddId(uint id)
144 mNeedUpdateMovementSize = true;
147 public void SetContainerId(uint id)
152 public void SetContainerStartPosition(float position)
154 mContainerStartPosition = position;
157 public void SetLatestMovement(float movement)
159 mLatestMovement = movement;
162 public void ResetViewPosition()
164 mViewPosition = Enumerable.Repeat(0.0f, mViewId.Count).ToList();
167 public void SetViewPosition(int index, float position)
169 mViewPosition[index] = position;
172 public void SetTouchedViewIndex(int controlIndex)
174 mTouchedViewIndex = controlIndex;
177 public void AddMovement(float movement)
179 mMovement.Add(movement);
182 public void SetLeftIndex(int leftIndex)
184 mLeftIndex = leftIndex;
187 public void SetRightIndex(int rightIndex)
189 mRightIndex = rightIndex;
192 public bool IsResetTouchedViewPossible()
194 return mIsResetTouchedViewPossible;
201 public bool IsDirty()
206 public float GetVelocity()
211 private void ComputeNewPositions(int totalTime)
213 // save latestMovement to avoid interference between thread.
214 float latestMovement = mLatestMovement;
215 bool isResetTouchedViewPossible = true;
216 for (int i = 0; i < mViewId.Count; ++i)
218 if (i == mTouchedViewIndex)
223 // compute delay of view of i.
224 int totalDelay = Math.Abs(i - mTouchedViewIndex) * OBJECT_DELAY;
225 if (totalDelay > totalTime)
230 int actorTime = totalTime - totalDelay;
231 int movementIndex = actorTime / mTimeInterval;
232 float factor = (float)(actorTime - (movementIndex * mTimeInterval)) / (float)mTimeInterval;
234 if (movementIndex >= mMovement.Count - 1)
236 // 1. delay is zero(every view moves with container at the same time)
237 // 2. after every icons are stopped and the finger is stopped to move, the movement is still not added more.
238 // than the view has latestMovement.
239 movement = latestMovement;
241 else if (movementIndex < 0)
243 // If this animation is just staarted and the view need to wait more.
249 // Get the movement of ith view by interpolating mMovement
250 movement = factor * mMovement[movementIndex + 1] + (1.0f - factor) * mMovement[movementIndex];
253 // Prevent to overlap of each views.
254 float currentSpace = Math.Abs((mViewPosition[i] + movement) - (mViewPosition[mTouchedViewIndex] + latestMovement));
255 float minimumSpace = (float)Math.Abs(mViewPosition[i] - mViewPosition[mTouchedViewIndex]);
256 if (currentSpace < minimumSpace)
258 movement = latestMovement;
261 // check views in screen are still moving or stopped.
262 float newPosition = mViewPosition[i] + movement - latestMovement;
263 if (i >= mLeftIndex && i <= mRightIndex)
265 Vector3 previousPosition = new Vector3();
266 GetPosition(mViewId[i], previousPosition);
267 if (Math.Abs(previousPosition.X - newPosition) >= 1.0f)
269 isResetTouchedViewPossible = false;
272 // update new position.
273 SetPosition(mViewId[i], new Vector3(newPosition, 0.0f, 0.0f));
275 mIsResetTouchedViewPossible = isResetTouchedViewPossible;
278 public override void OnUpdate(float elapsedSeconds)
280 // second -> millisecond
281 mTotalAnimationTime += elapsedSeconds * 1000.0f;
283 Vector3 currentPosition = new Vector3();
284 GetPosition(mContainerId, currentPosition);
286 // Add new Movement, if there is change in position.
287 // 1. if dirty(there is reserved event)
288 // 2. if container position is changed.
289 // 3. if every icons in screen is stopped
290 if (mDirty || currentPosition.X != mPreviousContainerPosition || mIsResetTouchedViewPossible)
293 if (mTotalAnimationTime >= mMovement.Count * mTimeInterval)
295 // If the passed time is larger than mTimeInterval, add new movements.
296 // If we need to add more than one, compute each movement by using interpolation.
297 while (mMovement.Count <= mTotalAnimationTime / mTimeInterval)
299 float factor = ((float)(mMovement.Count * mTimeInterval) - mPreviousTotalAnimationTime) / (mTotalAnimationTime - mPreviousTotalAnimationTime);
300 float movement = (float)(factor * currentPosition.X + (1.0f - factor) * mPreviousContainerPosition) - mContainerStartPosition;
301 AddMovement(movement);
304 // We need to compute velocity here to get reasonable value.
305 mVelocity = (currentPosition.X - mPreviousContainerPosition) / (mTotalAnimationTime - mPreviousTotalAnimationTime);
306 mPreviousTotalAnimationTime = mTotalAnimationTime;
307 mPreviousContainerPosition = currentPosition.X;
310 float currentMovement = currentPosition.X - mContainerStartPosition;
311 SetLatestMovement(currentMovement);
313 // Compute positions of each icon
314 ComputeNewPositions((int)mTotalAnimationTime);
316 // compute mRequiredMovementSize
317 if (mRequiredMovementSize == 0 || mNeedUpdateMovementSize)
319 mNeedUpdateMovementSize = false;
320 mRequiredMovementSize = mViewId.Count * OBJECT_DELAY / mTimeInterval;
323 // Remove unnecessary movement for memory optimization.
324 if (mMovement.Count > mRequiredMovementSize * 2)
326 int movementNumberToRemove = mMovement.Count - mRequiredMovementSize;
327 mMovement.RemoveRange(0, movementNumberToRemove);
328 mTotalAnimationTime -= (float)(mTimeInterval * movementNumberToRemove);
333 private Window mWindow;
335 private FrameCallback mFrameCallback; ///< An instance of our implementation of the FrameCallbackInterface.
337 // Views for launcher
338 private View mBaseView;
339 private View mControlView;
340 private View mLayoutView;
342 // Variables for animation
343 private float mPreviousTouchedPosition;
344 private int mTouchedViewIndex;
345 private TOUCH_ANIMATION_STATE mAnimationState;
347 private float mLeftDirectionLimit;
348 private float mRightDirectionLimit;
351 // Variables for Finish animation
352 // These variables are for deceleration curve.
353 // If we want to use another curve like bezier, uses different variables
354 private delegate float UserAlphaFunctionDelegate(float progress);
355 private UserAlphaFunctionDelegate customScrollAlphaFunction;
356 private float mAbsoluteVelocity = 0.0f;
357 private float mFinishAnimationDuration = 0.0f;
358 private float mFinishAnimationDelta = 0.0f;
359 private float mLogDeceleration = 0.0f;
360 private float mDecelerationRate = 0.99f;
361 private float mEasingThreshold = 0.1f;
363 private Animation mFinishAnimation;
364 private Timer mAnimationOffTimer; // timer to end animation after the easing animation is finished
367 private View mContentsView;
369 public void Activate()
371 mFrameCallback = new FrameCallback();
375 public void Deactivate()
381 // Set the stage background color and connect to the stage's key signal to allow Back and Escape to exit.
382 mWindow = Window.Instance;
383 mWindow.BackgroundColor = Color.White;
385 mRightDirectionLimit = INITIAL_POSITION;
389 mContentsView = new View();
390 mContentsView.BackgroundColor = new Color(0.921568f, 0.9098039f, 0.890196f, 0.5f);
391 mContentsView.ParentOrigin = ParentOrigin.TopLeft;
392 mContentsView.PivotPoint = PivotPoint.TopLeft;
393 mContentsView.PositionUsesPivotPoint = true;
394 mContentsView.WidthResizePolicy = ResizePolicyType.FillToParent;
395 mContentsView.HeightResizePolicy = ResizePolicyType.FillToParent;
396 mWindow.GetDefaultLayer().Add(mContentsView);
399 mBaseView = new View();
400 mBaseView.ParentOrigin = ParentOrigin.BottomLeft;
401 mBaseView.PivotPoint = PivotPoint.BottomLeft;
402 mBaseView.PositionUsesPivotPoint = true;
403 mBaseView.Size = new Size(mWindow.Size.Width, 278);
404 mBaseView.Position = new Position(0, 0);
405 mWindow.GetDefaultLayer().Add(mBaseView);
407 View iconBackgroundView = new View();
408 iconBackgroundView.BackgroundColor = new Color(0.921568f, 0.9098039f, 0.890196f, 0.5f);
409 iconBackgroundView.ParentOrigin = ParentOrigin.BottomLeft;
410 iconBackgroundView.PivotPoint = PivotPoint.BottomLeft;
411 iconBackgroundView.PositionUsesPivotPoint = true;
412 iconBackgroundView.Size = new Size(mWindow.Size.Width, 278);
413 iconBackgroundView.Position = new Position(0, 0);
414 mBaseView.Add(iconBackgroundView);
416 mControlView = new View();
417 mControlView.ParentOrigin = ParentOrigin.CenterLeft;
418 mControlView.PivotPoint = PivotPoint.CenterLeft;
419 mControlView.PositionUsesPivotPoint = true;
420 mControlView.Position = new Position(mRightDirectionLimit, 0);
421 mBaseView.Add(mControlView);
422 mFrameCallback.SetContainerId(mControlView.ID);
424 mLayoutView = new View();
425 mLayoutView.ParentOrigin = ParentOrigin.CenterLeft;
426 mLayoutView.PivotPoint = PivotPoint.CenterLeft;
427 mLayoutView.PositionUsesPivotPoint = true;
428 mLayoutView.Layout = new LinearLayout()
430 LinearOrientation = LinearLayout.Orientation.Horizontal,
431 CellPadding = new Size2D(DEFAULT_SPACE, 0),
433 mLayoutView.Position = new Position(0, 0);
434 mControlView.Add(mLayoutView);
436 for (int i = 0; i < 4; ++i)
438 AddIcon(BACKGROUND_IMAGE_PATH[i], APPS_IMAGE_PATH[i], APPS_ICON_NAME[i], Color.White);
441 View divideBar = new View();
442 divideBar.BackgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.1f);
443 divideBar.ParentOrigin = ParentOrigin.CenterLeft;
444 divideBar.PivotPoint = PivotPoint.CenterLeft;
445 divideBar.PositionUsesPivotPoint = true;
446 divideBar.Size = new Size(DEVIDE_BAR_SIZE, OBJECT_SIZE);
447 mLayoutView.Add(divideBar);
448 mFrameCallback.AddId(divideBar.ID);
451 for (int i = 0; i < iconNumber; ++i)
453 AddIcon(BACKGROUND_IMAGE_PATH[5], CONTROL_IMAGE_PATH[i % 5], CONTROL_ICON_NAME[i % 5], new Color(0.0f, 0.0f, 0.0f, 0.5f));
456 mFrameCallback.ResetViewPosition();
457 mFrameCallback.SetTimeInterval(1000 / FRAME_RATE);
459 mAnimationState = TOUCH_ANIMATION_STATE.NO_ANIMATION;
461 mAnimationOffTimer = new Timer(16);
462 mAnimationOffTimer.Tick += OffAnimatable;
464 mBaseView.TouchEvent += OnTouch;
466 mFinishAnimation = new Animation();
467 mFinishAnimation.Finished += EasingAnimationFinishedCallback;
468 mLogDeceleration = (float)Math.Log(mDecelerationRate);
473 void AddIcon(string background, string icon, string text, Color textColor)
475 ImageView backgroundView = new ImageView();
476 backgroundView.ResourceUrl = background;
477 backgroundView.Size = new Size(OBJECT_SIZE, OBJECT_SIZE);
478 backgroundView.ParentOrigin = ParentOrigin.CenterLeft;
479 backgroundView.PivotPoint = PivotPoint.CenterLeft;
480 backgroundView.PositionUsesPivotPoint = true;
481 mLayoutView.Add(backgroundView);
482 mFrameCallback.AddId(backgroundView.ID);
484 ImageView iconView = new ImageView();
485 iconView.ResourceUrl = icon;
486 iconView.Position = new Position(0, -15);
487 iconView.ParentOrigin = ParentOrigin.Center;
488 iconView.PivotPoint = PivotPoint.Center;
489 iconView.PositionUsesPivotPoint = true;
490 backgroundView.Add(iconView);
492 TextLabel label = new TextLabel(text);
493 label.Position = new Position(0, 30);
494 label.HorizontalAlignment = HorizontalAlignment.Center;
495 label.TextColor = textColor;
496 label.FontFamily = "SamsungOneUI";
497 label.PointSize = 12;
498 label.ParentOrigin = ParentOrigin.Center;
499 label.PivotPoint = PivotPoint.Center;
500 label.PositionUsesPivotPoint = true;
501 backgroundView.Add(label);
504 // Set frame callback to start drag animation.
505 private void SetFrameCallback(float position)
507 // remove frame callback if it is already added.
508 mWindow.RemoveFrameCallback(mFrameCallback);
510 mFrameCallback.ResetAnimationData();
511 mFrameCallback.AddMovement(0.0f); // Add first movement.
513 // Set container start position and start positions of each icon(and vertical bar)
514 // And compute total container size.
515 float totalSize = 0.0f;
516 mFrameCallback.SetContainerStartPosition(mControlView.Position.X);
517 for (int i = 0; i < mLayoutView.ChildCount; ++i)
519 mFrameCallback.SetViewPosition(i, mLayoutView.Children[i].Position.X);
520 totalSize += (float)(mLayoutView.Children[i].Size.Width + DEFAULT_SPACE);
522 totalSize -= (float)DEFAULT_SPACE;
525 for (int i = (int)mLayoutView.ChildCount - 1; i >= 0; --i)
527 if (position >= mLayoutView.Children[i].Position.X + mControlView.Position.X)
529 mFrameCallback.SetTouchedViewIndex(i);
530 mTouchedViewIndex = i;
534 if (position < mLayoutView.Children[0].Position.X + mControlView.Position.X)
536 mFrameCallback.SetTouchedViewIndex(0);
537 mTouchedViewIndex = 0;
540 mPreviousTouchedPosition = position;
542 // Add frame callback on window.
543 // OnUpdate callback of mFrameCallback will be called before every render frame.
544 mWindow.AddFrameCallback(mFrameCallback);
546 // compute limit position the container could go.
547 mLeftDirectionLimit = (float)mWindow.Size.Width - (totalSize + (float)(INITIAL_POSITION));
549 mWindow.RenderingBehavior = RenderingBehaviorType.Continuously; // make rendering be done for upto 60 fps even though there is no update in main thread.
550 mAnimationState = TOUCH_ANIMATION_STATE.ON_ANIMATION; // make rendering state on.
553 private bool OnTouch(object source, View.TouchEventArgs e)
555 Vector2 position = e.Touch.GetScreenPosition(0);
557 PointStateType state = e.Touch.GetState(0);
558 if (PointStateType.Down == state)
560 if (mAnimationState == TOUCH_ANIMATION_STATE.ON_FINISH_ANIMATION)
562 // re-birth current animation
563 // in case of touch during finish animation,
564 // quit easingAnimation and AnimationOffTimer because animation ownership is returned to the touch event again.
565 // AND, DO NOT RESET ALL PROPERTIES OF FRAMECALLBACK.
566 // because, for example, if touched icon index is changed, the movement is wrong and the animation can be not continous.
567 // This re-birthed animation is just for smooth moving during complex user interaction.
568 // during complex and fast interaction, this is not so noticeable.
569 // and reset of such properties will be done in the below Motion state
570 mFinishAnimation.Stop();
571 mAnimationOffTimer.Stop();
573 // Set Animation State to ON_ANIMATION again
574 mAnimationState = TOUCH_ANIMATION_STATE.ON_ANIMATION;
575 // Set previousTouchPosition
576 mPreviousTouchedPosition = position.X;
580 // in case of stable state
581 // just set new framecallback for this touched position.
582 SetFrameCallback(position.X);
585 else if (PointStateType.Motion == state)
587 // if framecallback can be reset, quit current frame callback and re-launch new frame callback.
588 // because, if current frame callback is re-birthed one, the animation is not totally re-created one.
589 // So, some properties like touched icon index can be wrong for the continuous animation.
590 // But, some case like that finger is stopped and restart to move, this could make weired feeling.
591 // We reset mFrameCallback as soon as possible we can. And the conditions are ...
592 // 1. icons in screen is stopped.
593 // 2. velocity of frame callback is 0.0 (this frame callback will not move again instantly)
594 // 3. frame callback is not dirty (there is no reserved action)
595 if (mFrameCallback.IsResetTouchedViewPossible() && mFrameCallback.GetVelocity() == 0.0f && !mFrameCallback.IsDirty())
597 SetFrameCallback(position.X);
600 // Set new controlView(container) position
601 // in here, we need to consider the container is not go outside of limits.
602 float containerPosition = mControlView.Position.X + (position.X - mPreviousTouchedPosition);
603 containerPosition = Math.Min(containerPosition, mRightDirectionLimit);
604 containerPosition = Math.Max(containerPosition, mLeftDirectionLimit);
605 float adjustedPosition = containerPosition - mControlView.Position.X + mPreviousTouchedPosition;
606 mPreviousTouchedPosition = adjustedPosition;
607 mControlView.Position.X = containerPosition;
609 else if ((PointStateType.Up == state || PointStateType.Leave == state || PointStateType.Interrupted == state) &&
610 mAnimationState == TOUCH_ANIMATION_STATE.ON_ANIMATION)
612 mAnimationState = TOUCH_ANIMATION_STATE.ON_FINISH_ANIMATION;
614 // To launch finish animation, we get latest velocty from frame callback
615 float velocity = mFrameCallback.GetVelocity();
618 // This is just for turning of finish animation.
619 // change the values if you want.
620 velocity = Math.Max(velocity, -3.5f);
621 velocity = Math.Min(velocity, 3.5f);
622 if (Math.Abs(velocity) < 0.0001f)
624 // If velocity is zero. just start animationOfftimer.
625 mAnimationOffTimer.Start();
629 // If velocity is not zero, make decelerating animation.
630 Decelerating(velocity);
633 // set currently visible icons for optimization
635 // make frame callback dirty.
636 mFrameCallback.Dirty();
640 private void SetVisibleLimit()
642 int leftViewIndex = mTouchedViewIndex;
643 for (; leftViewIndex >= 0; --leftViewIndex)
645 float newPosition = mLayoutView.Children[leftViewIndex].Position.X + mControlView.Position.X;
646 if (newPosition + (float)mLayoutView.Children[leftViewIndex].Size.Width < 0.0f)
651 leftViewIndex = Math.Max(leftViewIndex, 0);
652 int rightViewIndex = mTouchedViewIndex;
653 for (; rightViewIndex < mLayoutView.ChildCount; ++rightViewIndex)
655 float newPosition = mLayoutView.Children[rightViewIndex].Position.X + mControlView.Position.X;
656 if (newPosition > mWindow.Size.Width)
661 rightViewIndex = Math.Min(rightViewIndex, (int)mLayoutView.ChildCount - 1);
663 mFrameCallback.SetLeftIndex(leftViewIndex);
664 mFrameCallback.SetRightIndex(rightViewIndex);
667 // set decelerating properties
668 // in this example, we used decelerate animation in "https://medium.com/@esskeetit/scrolling-mechanics-of-uiscrollview-142adee1142c"
669 // But, if this method is problematic or violate some patent of other company, change this other way.
670 // We didn't checked anything.
671 // Only thing we need to remember when we change this animation is to add "EasingAnimationFinishedCallback" for the new animation.
672 private void Decelerating(float velocity)
674 mAbsoluteVelocity = Math.Abs(velocity);
675 mFinishAnimationDelta = (mAbsoluteVelocity * mDecelerationRate) / (1 - mDecelerationRate);
676 float destination = (velocity > 0) ? mControlView.Position.X + mFinishAnimationDelta : mControlView.Position.X - mFinishAnimationDelta;
678 if (destination < mLeftDirectionLimit || destination > mRightDirectionLimit)
680 mFinishAnimationDelta = velocity > 0 ? (mRightDirectionLimit - mControlView.Position.X) : (mControlView.Position.X - mLeftDirectionLimit);
681 destination = velocity > 0 ? mRightDirectionLimit : mLeftDirectionLimit;
682 if (mFinishAnimationDelta == 0)
684 mFinishAnimationDuration = 0.0f;
688 mFinishAnimationDuration = (float)Math.Log((mFinishAnimationDelta * mLogDeceleration / mAbsoluteVelocity + 1), mDecelerationRate);
693 if (mFinishAnimationDelta == 0)
695 mFinishAnimationDuration = 0.0f;
699 mFinishAnimationDuration = (float)Math.Log(-mEasingThreshold * mLogDeceleration / mAbsoluteVelocity) / mLogDeceleration;
703 mFinishAnimation.Clear();
704 customScrollAlphaFunction = new UserAlphaFunctionDelegate(CustomScrollAlphaFunction);
705 mFinishAnimation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
706 GC.KeepAlive(customScrollAlphaFunction);
707 mFinishAnimation.Duration = (int)mFinishAnimationDuration;
708 mFinishAnimation.AnimateTo(mControlView, "PositionX", destination);
709 mFinishAnimation.Play();
712 private float CustomScrollAlphaFunction(float progress)
714 if (mFinishAnimationDelta == 0)
720 float realDuration = progress * mFinishAnimationDuration;
721 float realDistance = mAbsoluteVelocity * ((float)Math.Pow(mDecelerationRate, realDuration) - 1) / mLogDeceleration;
722 float result = Math.Min(realDistance / Math.Abs(mFinishAnimationDelta), 1.0f);
728 private void EasingAnimationFinishedCallback(object sender, EventArgs e)
730 if (mAnimationState != TOUCH_ANIMATION_STATE.ON_FINISH_ANIMATION)
735 // start Animation Off Timer
736 mFinishAnimation.Clear();
738 mAnimationOffTimer.Start();
741 // Check each icons in screen is not moving.
742 // If it is, finish all animation and make animationstate End_animation(NO_ANIMATION)
743 private bool OffAnimatable(object target, Timer.TickEventArgs args)
745 if (mFrameCallback.IsResetTouchedViewPossible())
747 mWindow.RenderingBehavior = RenderingBehaviorType.IfRequired;
748 mWindow.RemoveFrameCallback(mFrameCallback);
749 mAnimationOffTimer.Stop();
750 mAnimationState = TOUCH_ANIMATION_STATE.END_ANIMATION;