/* * opsu! - an open-source osu! client * Copyright (C) 2014, 2015 Jeffrey Han * * opsu! is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * opsu! is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with opsu!. If not, see . */ package itdelatrisu.opsu.ui; /** * Drag to scroll based on: * http://ariya.ofilabs.com/2013/11/javascript-kinetic-scrolling-part-2.html * * @author fluddokt (https://github.com/fluddokt) */ public class KineticScrolling { /** The moving averaging constant. */ private static final float AVG_CONST = 0.2f, ONE_MINUS_AVG_CONST = 1 - AVG_CONST; /** The constant used to determine how fast the target position will be reach. */ private static final int TIME_CONST = 200; /** The constant used to determine how much of the velocity will be used to launch to the target. */ private static final float AMPLITUDE_CONST = 0.25f; /** The current position. */ private float position; /** The offset to scroll to the target position. */ private float amplitude; /** The current target to scroll to. */ private float target; /** The total amount of time since the mouse button was released. */ private float totalDelta; /** The maximum and minimum value the position can reach. */ private float max = Float.MAX_VALUE, min = -Float.MAX_VALUE; /** Whether the mouse is currently pressed or not. */ private boolean pressed = false; /** The change in position since the last update. */ private float deltaPosition; /** The moving average of the velocity. */ private float avgVelocity; /** * Returns the current Position. * @return the position */ public float getPosition() { return position; } /** * Updates the scrolling. * @param delta the elapsed time since the last update */ public void update(float delta) { if (!pressed) { totalDelta += delta; position = target + (float) (-amplitude * Math.exp(-totalDelta / TIME_CONST)); } else { avgVelocity = (ONE_MINUS_AVG_CONST * avgVelocity + AVG_CONST * (deltaPosition * 1000f / delta)); position += deltaPosition; target = position; deltaPosition = 0; } if (position > max) { amplitude = 0; target = position = max; } if (position < min) { amplitude = 0; target = position = min; } } /** * Scrolls to the position. * @param newPosition the position to scroll to */ public void scrollToPosition(float newPosition) { amplitude = newPosition - position; target = newPosition; totalDelta = 0; } /** * Scrolls to an offset from target. * @param offset the offset from the target to scroll to */ public void scrollOffset(float offset) { scrollToPosition(target + offset); } /** * Sets the position. * @param newPosition the position to be set */ public void setPosition(float newPosition) { pressed(); released(); target = newPosition; position = target; } /** * Set the position relative to an offset. * @param offset the offset from the position */ public void addOffset(float offset) { setPosition(position + offset); } /** * Call this when the mouse button has been pressed. */ public void pressed() { if (pressed) return; pressed = true; avgVelocity = 0; } /** * Call this when the mouse button has been released. */ public void released() { if (!pressed) return; pressed = false; amplitude = AMPLITUDE_CONST * avgVelocity; target = Math.round(target + amplitude); totalDelta = 0; } /** * Call this when the mouse has been dragged. * @param distance the amount that the mouse has been dragged */ public void dragged(float distance) { deltaPosition += distance; } /** * Set the minimum and maximum bound. * @param min the minimum bound * @param max the maximum bound */ public void setMinMax(float min, float max) { this.min = min; this.max = max; } }