-using System;
-using System.Threading.Tasks;
-using Tizen.NUI;
-using Tizen.NUI.BaseComponents;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace Tizen.NUI.Samples
-{
- enum TOUCH_ANIMATION_STATE
- {
- NO_ANIMATION = 0,
- ON_ANIMATION,
- ON_FINISH_ANIMATION,
- END_ANIMATION = NO_ANIMATION
- };
-
- public class FrameCallbackTest : IExample
- {
-
- private static string resourcePath = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
- private static string[] BACKGROUND_IMAGE_PATH = {
- resourcePath + "/images/FrameCallbackTest/launcher_bg_02_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_bg_03_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_bg_04_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_bg_05_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_bg_06_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_bg_apps_nor.png"
- };
-
- private static string[] APPS_IMAGE_PATH = {
- resourcePath + "/images/FrameCallbackTest/launcher_ic_culinary_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_ic_family_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_ic_ent_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_ic_homecare_nor.png"
- };
-
- private static string[] APPS_ICON_NAME = {
- "Culinary",
- "Family",
- "Entertainment",
- "Homecare"
- };
-
- private static string[] CONTROL_IMAGE_PATH = {
- resourcePath + "/images/FrameCallbackTest/launcher_ic_apps_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_ic_settings_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_ic_viewinside_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_ic_timer_nor.png",
- resourcePath + "/images/FrameCallbackTest/launcher_ic_internet_nor.png"
- };
-
- private static string[] CONTROL_ICON_NAME = {
- "Apps",
- "Settings",
- "ViewInside",
- "Timer",
- "Internet"
- };
-
- private const int FRAME_RATE = 60;
- private const int OBJECT_DELAY = 30;
- private const int OBJECT_SIZE = 150;
- private const int INITIAL_POSITION = 46;
- private const int DEFAULT_SPACE = 9;
- private const int DEVIDE_BAR_SIZE = 4;
-
- private TextLabel text;
- public class FrameCallback : FrameCallbackInterface
- {
- private int mTimeInterval;
-
- private uint mContainerId;
- private List<uint> mViewId; ///< Container of Actor IDs.
- private List<float> mViewPosition;
-
- // Movement is position difference of Container from start position of this animation to current position.
- // Time interval between each mMovement entry is 16 milliseconds(about 60 fps)
- // For example.
- // mMovement[i] + mContainerStartPosition is the position of container after i*16 milliseconds from this animation started.
- private List<float> mMovement;
-
- // mLatestMovement is actually current movement of container.
- private float mLatestMovement;
-
- // An icon of mTouchedViewIndex moves with container at the same time.
- // Each icon of (mTouchedViewIndex -/+ i) moves as following container after i*OBJECT_DELAY milliseconds.
- private int mTouchedViewIndex;
-
- // If every icon between mLeftIndext and mRightIndex is stopped, this frame callback can be reset.
- // Then mIsResetTouchedViewPossible becomes true.
- private bool mIsResetTouchedViewPossible;
- private int mLeftIndex;
- private int mRightIndex;
-
- // Total animation time from start to current.
- private float mTotalAnimationTime;
- // Total animation time from start to the time that last movement is added.
- private float mPreviousTotalAnimationTime;
-
- // Start position of container in this animation.
- private float mContainerStartPosition;
- // Position of container at the time that last movement is added.
- private float mPreviousContainerPosition;
-
- // This animation only need to save movement about (the number of view * OBJECT_DELAY) milliseconds.
- // and this size is updated when new view is added.
- // and, If the list of mMovement size become larger than this size, remove unnecessary entries.
- private int mRequiredMovementSize;
- private bool mNeedUpdateMovementSize;
-
- // current velocity.
- private float mVelocity;
- // dirty flag.
- private bool mDirty;
-
- public FrameCallback()
- {
- mViewId = new List<uint>();
- mMovement = new List<float>();
- mRequiredMovementSize = 0;
- mNeedUpdateMovementSize = false;
- mIsResetTouchedViewPossible = false;
- }
-
- public void ResetAnimationData()
- {
- SetLatestMovement(0.0f);
- mMovement.Clear();
- mTotalAnimationTime = 0.0f;
- mPreviousTotalAnimationTime = 0.0f;
- mDirty = true;
- }
-
- public void SetTimeInterval(int timeInterval)
- {
- mNeedUpdateMovementSize = true;
- mTimeInterval = timeInterval;
- }
-
- public void AddId(uint id)
- {
- mViewId.Add(id);
- mNeedUpdateMovementSize = true;
- }
-
- public void SetContainerId(uint id)
- {
- mContainerId = id;
- }
-
- public void SetContainerStartPosition(float position)
- {
- mContainerStartPosition = position;
- }
-
- public void SetLatestMovement(float movement)
- {
- mLatestMovement = movement;
- }
-
- public void ResetViewPosition()
- {
- mViewPosition = Enumerable.Repeat(0.0f, mViewId.Count).ToList();
- }
-
- public void SetViewPosition(int index, float position)
- {
- mViewPosition[index] = position;
- }
-
- public void SetTouchedViewIndex(int controlIndex)
- {
- mTouchedViewIndex = controlIndex;
- }
-
- public void AddMovement(float movement)
- {
- mMovement.Add(movement);
- }
-
- public void SetLeftIndex(int leftIndex)
- {
- mLeftIndex = leftIndex;
- }
-
- public void SetRightIndex(int rightIndex)
- {
- mRightIndex = rightIndex;
- }
-
- public bool IsResetTouchedViewPossible()
- {
- return mIsResetTouchedViewPossible;
- }
- public void Dirty()
- {
- mDirty = true;
- }
-
- public bool IsDirty()
- {
- return mDirty;
- }
-
- public float GetVelocity()
- {
- return mVelocity;
- }
-
- private void ComputeNewPositions(int totalTime)
- {
- // save latestMovement to avoid interference between thread.
- float latestMovement = mLatestMovement;
- bool isResetTouchedViewPossible = true;
- for (int i = 0; i < mViewId.Count; ++i)
- {
- if (i == mTouchedViewIndex)
- {
- continue;
- }
-
- // compute delay of view of i.
- int totalDelay = Math.Abs(i - mTouchedViewIndex) * OBJECT_DELAY;
- if (totalDelay > totalTime)
- {
- continue;
- }
-
- int actorTime = totalTime - totalDelay;
- int movementIndex = actorTime / mTimeInterval;
- float factor = (float)(actorTime - (movementIndex * mTimeInterval)) / (float)mTimeInterval;
- float movement;
- if (movementIndex >= mMovement.Count - 1)
- {
- // 1. delay is zero(every view moves with container at the same time)
- // 2. after every icons are stopped and the finger is stopped to move, the movement is still not added more.
- // than the view has latestMovement.
- movement = latestMovement;
- }
- else if (movementIndex < 0)
- {
- // If this animation is just staarted and the view need to wait more.
- // movement is 0.
- movement = 0.0f;
- }
- else
- {
- // Get the movement of ith view by interpolating mMovement
- movement = factor * mMovement[movementIndex + 1] + (1.0f - factor) * mMovement[movementIndex];
- }
-
- // Prevent to overlap of each views.
- float currentSpace = Math.Abs((mViewPosition[i] + movement) - (mViewPosition[mTouchedViewIndex] + latestMovement));
- float minimumSpace = (float)Math.Abs(mViewPosition[i] - mViewPosition[mTouchedViewIndex]);
- if (currentSpace < minimumSpace)
- {
- movement = latestMovement;
- }
-
- // check views in screen are still moving or stopped.
- float newPosition = mViewPosition[i] + movement - latestMovement;
- if (i >= mLeftIndex && i <= mRightIndex)
- {
- Vector3 previousPosition = new Vector3();
- GetPosition(mViewId[i], previousPosition);
- if (Math.Abs(previousPosition.X - newPosition) >= 1.0f)
- {
- isResetTouchedViewPossible = false;
- }
- }
- // update new position.
- SetPosition(mViewId[i], new Vector3(newPosition, 0.0f, 0.0f));
- }
- mIsResetTouchedViewPossible = isResetTouchedViewPossible;
- }
-
- public override void OnUpdate(float elapsedSeconds)
- {
- // second -> millisecond
- mTotalAnimationTime += elapsedSeconds * 1000.0f;
-
- Vector3 currentPosition = new Vector3();
- GetPosition(mContainerId, currentPosition);
-
- // Add new Movement, if there is change in position.
- // 1. if dirty(there is reserved event)
- // 2. if container position is changed.
- // 3. if every icons in screen is stopped
- if (mDirty || currentPosition.X != mPreviousContainerPosition || mIsResetTouchedViewPossible)
- {
- mDirty = false;
- if (mTotalAnimationTime >= mMovement.Count * mTimeInterval)
- {
- // If the passed time is larger than mTimeInterval, add new movements.
- // If we need to add more than one, compute each movement by using interpolation.
- while (mMovement.Count <= mTotalAnimationTime / mTimeInterval)
- {
- float factor = ((float)(mMovement.Count * mTimeInterval) - mPreviousTotalAnimationTime) / (mTotalAnimationTime - mPreviousTotalAnimationTime);
- float movement = (float)(factor * currentPosition.X + (1.0f - factor) * mPreviousContainerPosition) - mContainerStartPosition;
- AddMovement(movement);
- }
- // Compute velocity.
- // We need to compute velocity here to get reasonable value.
- mVelocity = (currentPosition.X - mPreviousContainerPosition) / (mTotalAnimationTime - mPreviousTotalAnimationTime);
- mPreviousTotalAnimationTime = mTotalAnimationTime;
- mPreviousContainerPosition = currentPosition.X;
- }
- }
- float currentMovement = currentPosition.X - mContainerStartPosition;
- SetLatestMovement(currentMovement);
-
- // Compute positions of each icon
- ComputeNewPositions((int)mTotalAnimationTime);
-
- // compute mRequiredMovementSize
- if (mRequiredMovementSize == 0 || mNeedUpdateMovementSize)
- {
- mNeedUpdateMovementSize = false;
- mRequiredMovementSize = mViewId.Count * OBJECT_DELAY / mTimeInterval;
- }
-
- // Remove unnecessary movement for memory optimization.
- if (mMovement.Count > mRequiredMovementSize * 2)
- {
- int movementNumberToRemove = mMovement.Count - mRequiredMovementSize;
- mMovement.RemoveRange(0, movementNumberToRemove);
- mTotalAnimationTime -= (float)(mTimeInterval * movementNumberToRemove);
- }
- }
- }
-
- private Window mWindow;
-
- private FrameCallback mFrameCallback; ///< An instance of our implementation of the FrameCallbackInterface.
-
- // Views for launcher
- private View mBaseView;
- private View mControlView;
- private View mLayoutView;
-
- // Variables for animation
- private float mPreviousTouchedPosition;
- private int mTouchedViewIndex;
- private TOUCH_ANIMATION_STATE mAnimationState;
-
- private float mLeftDirectionLimit;
- private float mRightDirectionLimit;
-
-
- // Variables for Finish animation
- // These variables are for deceleration curve.
- // If we want to use another curve like bezier, uses different variables
- private delegate float UserAlphaFunctionDelegate(float progress);
- private UserAlphaFunctionDelegate customScrollAlphaFunction;
- private float mAbsoluteVelocity = 0.0f;
- private float mFinishAnimationDuration = 0.0f;
- private float mFinishAnimationDelta = 0.0f;
- private float mLogDeceleration = 0.0f;
- private float mDecelerationRate = 0.99f;
- private float mEasingThreshold = 0.1f;
-
- private Animation mFinishAnimation;
- private Timer mAnimationOffTimer; // timer to end animation after the easing animation is finished
-
- // Vies of contents
- private View mContentsView;
-
- public void Activate()
- {
- mFrameCallback = new FrameCallback();
- Initialize();
- }
-
- public void Deactivate()
- {
- }
-
- void Initialize()
- {
- // Set the stage background color and connect to the stage's key signal to allow Back and Escape to exit.
- mWindow = Window.Instance;
- mWindow.BackgroundColor = Color.White;
-
- mRightDirectionLimit = INITIAL_POSITION;
-
- // Contents
-
- mContentsView = new View();
- mContentsView.BackgroundColor = new Color(0.921568f, 0.9098039f, 0.890196f, 0.5f);
- mContentsView.ParentOrigin = ParentOrigin.TopLeft;
- mContentsView.PivotPoint = PivotPoint.TopLeft;
- mContentsView.PositionUsesPivotPoint = true;
- mContentsView.WidthResizePolicy = ResizePolicyType.FillToParent;
- mContentsView.HeightResizePolicy = ResizePolicyType.FillToParent;
- mWindow.GetDefaultLayer().Add(mContentsView);
-
- // Launcher
- mBaseView = new View();
- mBaseView.ParentOrigin = ParentOrigin.BottomLeft;
- mBaseView.PivotPoint = PivotPoint.BottomLeft;
- mBaseView.PositionUsesPivotPoint = true;
- mBaseView.Size = new Size(mWindow.Size.Width, 278);
- mBaseView.Position = new Position(0, 0);
- mWindow.GetDefaultLayer().Add(mBaseView);
-
- View iconBackgroundView = new View();
- iconBackgroundView.BackgroundColor = new Color(0.921568f, 0.9098039f, 0.890196f, 0.5f);
- iconBackgroundView.ParentOrigin = ParentOrigin.BottomLeft;
- iconBackgroundView.PivotPoint = PivotPoint.BottomLeft;
- iconBackgroundView.PositionUsesPivotPoint = true;
- iconBackgroundView.Size = new Size(mWindow.Size.Width, 278);
- iconBackgroundView.Position = new Position(0, 0);
- mBaseView.Add(iconBackgroundView);
-
- mControlView = new View();
- mControlView.ParentOrigin = ParentOrigin.CenterLeft;
- mControlView.PivotPoint = PivotPoint.CenterLeft;
- mControlView.PositionUsesPivotPoint = true;
- mControlView.Position = new Position(mRightDirectionLimit, 0);
- mBaseView.Add(mControlView);
- mFrameCallback.SetContainerId(mControlView.ID);
-
- mLayoutView = new View();
- mLayoutView.ParentOrigin = ParentOrigin.CenterLeft;
- mLayoutView.PivotPoint = PivotPoint.CenterLeft;
- mLayoutView.PositionUsesPivotPoint = true;
- mLayoutView.Layout = new LinearLayout()
- {
- LinearOrientation = LinearLayout.Orientation.Horizontal,
- CellPadding = new Size2D(DEFAULT_SPACE, 0),
- };
- mLayoutView.Position = new Position(0, 0);
- mControlView.Add(mLayoutView);
-
- for (int i = 0; i < 4; ++i)
- {
- AddIcon(BACKGROUND_IMAGE_PATH[i], APPS_IMAGE_PATH[i], APPS_ICON_NAME[i], Color.White);
- }
-
- View divideBar = new View();
- divideBar.BackgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.1f);
- divideBar.ParentOrigin = ParentOrigin.CenterLeft;
- divideBar.PivotPoint = PivotPoint.CenterLeft;
- divideBar.PositionUsesPivotPoint = true;
- divideBar.Size = new Size(DEVIDE_BAR_SIZE, OBJECT_SIZE);
- mLayoutView.Add(divideBar);
- mFrameCallback.AddId(divideBar.ID);
-
- int iconNumber = 8;
- for (int i = 0; i < iconNumber; ++i)
- {
- 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));
- }
-
- mFrameCallback.ResetViewPosition();
- mFrameCallback.SetTimeInterval(1000 / FRAME_RATE);
-
- mAnimationState = TOUCH_ANIMATION_STATE.NO_ANIMATION;
-
- mAnimationOffTimer = new Timer(16);
- mAnimationOffTimer.Tick += OffAnimatable;
-
- mBaseView.TouchEvent += OnTouch;
-
- mFinishAnimation = new Animation();
- mFinishAnimation.Finished += EasingAnimationFinishedCallback;
- mLogDeceleration = (float)Math.Log(mDecelerationRate);
- }
-
- // Add icons
-
- void AddIcon(string background, string icon, string text, Color textColor)
- {
- ImageView backgroundView = new ImageView();
- backgroundView.ResourceUrl = background;
- backgroundView.Size = new Size(OBJECT_SIZE, OBJECT_SIZE);
- backgroundView.ParentOrigin = ParentOrigin.CenterLeft;
- backgroundView.PivotPoint = PivotPoint.CenterLeft;
- backgroundView.PositionUsesPivotPoint = true;
- mLayoutView.Add(backgroundView);
- mFrameCallback.AddId(backgroundView.ID);
-
- ImageView iconView = new ImageView();
- iconView.ResourceUrl = icon;
- iconView.Position = new Position(0, -15);
- iconView.ParentOrigin = ParentOrigin.Center;
- iconView.PivotPoint = PivotPoint.Center;
- iconView.PositionUsesPivotPoint = true;
- backgroundView.Add(iconView);
-
- TextLabel label = new TextLabel(text);
- label.Position = new Position(0, 30);
- label.HorizontalAlignment = HorizontalAlignment.Center;
- label.TextColor = textColor;
- label.FontFamily = "SamsungOneUI";
- label.PointSize = 12;
- label.ParentOrigin = ParentOrigin.Center;
- label.PivotPoint = PivotPoint.Center;
- label.PositionUsesPivotPoint = true;
- backgroundView.Add(label);
- }
-
- // Set frame callback to start drag animation.
- private void SetFrameCallback(float position)
- {
- // remove frame callback if it is already added.
- mWindow.RemoveFrameCallback(mFrameCallback);
-
- mFrameCallback.ResetAnimationData();
- mFrameCallback.AddMovement(0.0f); // Add first movement.
-
- // Set container start position and start positions of each icon(and vertical bar)
- // And compute total container size.
- float totalSize = 0.0f;
- mFrameCallback.SetContainerStartPosition(mControlView.Position.X);
- for (int i = 0; i < mLayoutView.ChildCount; ++i)
- {
- mFrameCallback.SetViewPosition(i, mLayoutView.Children[i].Position.X);
- totalSize += (float)(mLayoutView.Children[i].Size.Width + DEFAULT_SPACE);
- }
- totalSize -= (float)DEFAULT_SPACE;
-
- // Find touched icon
- for (int i = (int)mLayoutView.ChildCount - 1; i >= 0; --i)
- {
- if (position >= mLayoutView.Children[i].Position.X + mControlView.Position.X)
- {
- mFrameCallback.SetTouchedViewIndex(i);
- mTouchedViewIndex = i;
- break;
- }
- }
- if (position < mLayoutView.Children[0].Position.X + mControlView.Position.X)
- {
- mFrameCallback.SetTouchedViewIndex(0);
- mTouchedViewIndex = 0;
- }
-
- mPreviousTouchedPosition = position;
-
- // Add frame callback on window.
- // OnUpdate callback of mFrameCallback will be called before every render frame.
- mWindow.AddFrameCallback(mFrameCallback);
-
- // compute limit position the container could go.
- mLeftDirectionLimit = (float)mWindow.Size.Width - (totalSize + (float)(INITIAL_POSITION));
-
- mWindow.RenderingBehavior = RenderingBehaviorType.Continuously; // make rendering be done for upto 60 fps even though there is no update in main thread.
- mAnimationState = TOUCH_ANIMATION_STATE.ON_ANIMATION; // make rendering state on.
- }
-
- private bool OnTouch(object source, View.TouchEventArgs e)
- {
- Vector2 position = e.Touch.GetScreenPosition(0);
-
- PointStateType state = e.Touch.GetState(0);
- if (PointStateType.Down == state)
- {
- if (mAnimationState == TOUCH_ANIMATION_STATE.ON_FINISH_ANIMATION)
- {
- // re-birth current animation
- // in case of touch during finish animation,
- // quit easingAnimation and AnimationOffTimer because animation ownership is returned to the touch event again.
- // AND, DO NOT RESET ALL PROPERTIES OF FRAMECALLBACK.
- // because, for example, if touched icon index is changed, the movement is wrong and the animation can be not continous.
- // This re-birthed animation is just for smooth moving during complex user interaction.
- // during complex and fast interaction, this is not so noticeable.
- // and reset of such properties will be done in the below Motion state
- mFinishAnimation.Stop();
- mAnimationOffTimer.Stop();
-
- // Set Animation State to ON_ANIMATION again
- mAnimationState = TOUCH_ANIMATION_STATE.ON_ANIMATION;
- // Set previousTouchPosition
- mPreviousTouchedPosition = position.X;
- }
- else
- {
- // in case of stable state
- // just set new framecallback for this touched position.
- SetFrameCallback(position.X);
- }
- }
- else if (PointStateType.Motion == state)
- {
- // if framecallback can be reset, quit current frame callback and re-launch new frame callback.
- // because, if current frame callback is re-birthed one, the animation is not totally re-created one.
- // So, some properties like touched icon index can be wrong for the continuous animation.
- // But, some case like that finger is stopped and restart to move, this could make weired feeling.
- // We reset mFrameCallback as soon as possible we can. And the conditions are ...
- // 1. icons in screen is stopped.
- // 2. velocity of frame callback is 0.0 (this frame callback will not move again instantly)
- // 3. frame callback is not dirty (there is no reserved action)
- if (mFrameCallback.IsResetTouchedViewPossible() && mFrameCallback.GetVelocity() == 0.0f && !mFrameCallback.IsDirty())
- {
- SetFrameCallback(position.X);
- }
-
- // Set new controlView(container) position
- // in here, we need to consider the container is not go outside of limits.
- float containerPosition = mControlView.Position.X + (position.X - mPreviousTouchedPosition);
- containerPosition = Math.Min(containerPosition, mRightDirectionLimit);
- containerPosition = Math.Max(containerPosition, mLeftDirectionLimit);
- float adjustedPosition = containerPosition - mControlView.Position.X + mPreviousTouchedPosition;
- mPreviousTouchedPosition = adjustedPosition;
- mControlView.Position.X = containerPosition;
- }
- else if ((PointStateType.Up == state || PointStateType.Leave == state || PointStateType.Interrupted == state) &&
- mAnimationState == TOUCH_ANIMATION_STATE.ON_ANIMATION)
- {
- mAnimationState = TOUCH_ANIMATION_STATE.ON_FINISH_ANIMATION;
-
- // To launch finish animation, we get latest velocty from frame callback
- float velocity = mFrameCallback.GetVelocity();
-
- /* TUNING */
- // This is just for turning of finish animation.
- // change the values if you want.
- velocity = Math.Max(velocity, -3.5f);
- velocity = Math.Min(velocity, 3.5f);
- if (Math.Abs(velocity) < 0.0001f)
- {
- // If velocity is zero. just start animationOfftimer.
- mAnimationOffTimer.Start();
- }
- else
- {
- // If velocity is not zero, make decelerating animation.
- Decelerating(velocity);
- }
- }
- // set currently visible icons for optimization
- SetVisibleLimit();
- // make frame callback dirty.
- mFrameCallback.Dirty();
- return true;
- }
-
- private void SetVisibleLimit()
- {
- int leftViewIndex = mTouchedViewIndex;
- for (; leftViewIndex >= 0; --leftViewIndex)
- {
- float newPosition = mLayoutView.Children[leftViewIndex].Position.X + mControlView.Position.X;
- if (newPosition + (float)mLayoutView.Children[leftViewIndex].Size.Width < 0.0f)
- {
- break;
- }
- }
- leftViewIndex = Math.Max(leftViewIndex, 0);
- int rightViewIndex = mTouchedViewIndex;
- for (; rightViewIndex < mLayoutView.ChildCount; ++rightViewIndex)
- {
- float newPosition = mLayoutView.Children[rightViewIndex].Position.X + mControlView.Position.X;
- if (newPosition > mWindow.Size.Width)
- {
- break;
- }
- }
- rightViewIndex = Math.Min(rightViewIndex, (int)mLayoutView.ChildCount - 1);
-
- mFrameCallback.SetLeftIndex(leftViewIndex);
- mFrameCallback.SetRightIndex(rightViewIndex);
- }
-
- // set decelerating properties
- // in this example, we used decelerate animation in "https://medium.com/@esskeetit/scrolling-mechanics-of-uiscrollview-142adee1142c"
- // But, if this method is problematic or violate some patent of other company, change this other way.
- // We didn't checked anything.
- // Only thing we need to remember when we change this animation is to add "EasingAnimationFinishedCallback" for the new animation.
- private void Decelerating(float velocity)
- {
- mAbsoluteVelocity = Math.Abs(velocity);
- mFinishAnimationDelta = (mAbsoluteVelocity * mDecelerationRate) / (1 - mDecelerationRate);
- float destination = (velocity > 0) ? mControlView.Position.X + mFinishAnimationDelta : mControlView.Position.X - mFinishAnimationDelta;
-
- if (destination < mLeftDirectionLimit || destination > mRightDirectionLimit)
- {
- mFinishAnimationDelta = velocity > 0 ? (mRightDirectionLimit - mControlView.Position.X) : (mControlView.Position.X - mLeftDirectionLimit);
- destination = velocity > 0 ? mRightDirectionLimit : mLeftDirectionLimit;
- if (mFinishAnimationDelta == 0)
- {
- mFinishAnimationDuration = 0.0f;
- }
- else
- {
- mFinishAnimationDuration = (float)Math.Log((mFinishAnimationDelta * mLogDeceleration / mAbsoluteVelocity + 1), mDecelerationRate);
- }
- }
- else
- {
- if (mFinishAnimationDelta == 0)
- {
- mFinishAnimationDuration = 0.0f;
- }
- else
- {
- mFinishAnimationDuration = (float)Math.Log(-mEasingThreshold * mLogDeceleration / mAbsoluteVelocity) / mLogDeceleration;
- }
- }
-
- mFinishAnimation.Clear();
- customScrollAlphaFunction = new UserAlphaFunctionDelegate(CustomScrollAlphaFunction);
- mFinishAnimation.DefaultAlphaFunction = new AlphaFunction(customScrollAlphaFunction);
- GC.KeepAlive(customScrollAlphaFunction);
- mFinishAnimation.Duration = (int)mFinishAnimationDuration;
- mFinishAnimation.AnimateTo(mControlView, "PositionX", destination);
- mFinishAnimation.Play();
- }
-
- private float CustomScrollAlphaFunction(float progress)
- {
- if (mFinishAnimationDelta == 0)
- {
- return 1.0f;
- }
- else
- {
- float realDuration = progress * mFinishAnimationDuration;
- float realDistance = mAbsoluteVelocity * ((float)Math.Pow(mDecelerationRate, realDuration) - 1) / mLogDeceleration;
- float result = Math.Min(realDistance / Math.Abs(mFinishAnimationDelta), 1.0f);
-
- return result;
- }
- }
-
- private void EasingAnimationFinishedCallback(object sender, EventArgs e)
- {
- if (mAnimationState != TOUCH_ANIMATION_STATE.ON_FINISH_ANIMATION)
- {
- return;
- }
-
- // start Animation Off Timer
- mFinishAnimation.Clear();
- SetVisibleLimit();
- mAnimationOffTimer.Start();
- }
-
- // Check each icons in screen is not moving.
- // If it is, finish all animation and make animationstate End_animation(NO_ANIMATION)
- private bool OffAnimatable(object target, Timer.TickEventArgs args)
- {
- if (mFrameCallback.IsResetTouchedViewPossible())
- {
- mWindow.RenderingBehavior = RenderingBehaviorType.IfRequired;
- mWindow.RemoveFrameCallback(mFrameCallback);
- mAnimationOffTimer.Stop();
- mAnimationState = TOUCH_ANIMATION_STATE.END_ANIMATION;
- return false;
- }
- SetVisibleLimit();
- return true;
- }
- }
-}
\ No newline at end of file