Skip to content

Commit eec689a

Browse files
committed
feat: new on hold get acceleration interval method
Refs: 20
1 parent 4b3cce4 commit eec689a

File tree

1 file changed

+100
-26
lines changed

1 file changed

+100
-26
lines changed

src/InputSpinner.js

Lines changed: 100 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import {debounce, isNumeric, isEmpty} from "./Utils";
77
/**
88
* Default constants
99
*/
10+
const defaultSpeed = 2.5;
11+
const defaultAccelerationDelay = 1000;
1012
const defaultColor = "#3E525F";
11-
const defaultLongPressDelay = 750;
12-
const defaultLongPressSpeed = 5;
1313
const defaultTypingTime = 500;
1414

1515
/**
@@ -27,6 +27,7 @@ class InputSpinner extends Component {
2727
// Timers
2828
this.increaseTimer = null;
2929
this.decreaseTimer = null;
30+
this.holdTime = null;
3031

3132
let spinnerStep = this.parseNum(this.props.step);
3233
if (!this.typeDecimal() && spinnerStep < 1) {
@@ -151,23 +152,31 @@ class InputSpinner extends Component {
151152
this.decreaseTimer = null;
152153
}
153154

155+
/**
156+
* Clear on change timers
157+
* @private
158+
*/
159+
_clearOnChangeTimers() {
160+
this._clearMaxTimer();
161+
this._clearMinTimer();
162+
}
163+
154164
/**
155165
* Clear all timers
156166
* @private
157167
*/
158-
clearTimers() {
168+
_clearTimers() {
169+
this._clearOnChangeTimers();
159170
this._clearIncreaseTimer();
160171
this._clearDecreaseTimer();
161-
this._clearMaxTimer();
162-
this._clearMinTimer();
163172
}
164173

165174
/**
166175
* On value change
167176
* @param value
168177
*/
169178
async onChange(value) {
170-
this.clearTimers();
179+
this._clearOnChangeTimers();
171180

172181
let num = value;
173182
let parsedNum = value;
@@ -188,23 +197,34 @@ class InputSpinner extends Component {
188197
num = parsedNum = this.parseNum(String(num).replace(/^0+/, "")) || 0;
189198
if (!this.minReached(num)) {
190199
if (this.maxReached(num)) {
191-
parsedNum = this.state.max;
192-
if (!isEmpty(value)) {
193-
this.maxTimer = this._debounceSetMax();
194-
}
195-
if (this.props.onMax) {
196-
this.props.onMax(this.state.max);
200+
if (this.maxReached(num)) {
201+
parsedNum = this.state.max;
202+
if (!isEmpty(value)) {
203+
this.maxTimer = this._debounceSetMax();
204+
}
205+
if (this.props.onMax) {
206+
this.props.onMax(this.state.max);
207+
}
197208
}
198209
}
199210
} else {
200-
parsedNum = this.state.min;
201-
if (!isEmpty(value)) {
211+
if (!isEmpty(value) && !this.isEmptied()) {
212+
parsedNum = this.state.min;
202213
this.minTimer = this._debounceSetMin();
214+
} else if (this.isEmptied()) {
215+
parsedNum = null;
216+
} else {
217+
parsedNum = this.state.min;
203218
}
204219
if (this.props.onMin) {
205-
this.props.onMin(this.state.min);
220+
this.props.onMin(parsedNum);
206221
}
207222
}
223+
224+
if (isEmpty(value) && this.isEmptied()) {
225+
parsedNum = value;
226+
}
227+
208228
if (current_value !== num && this.props.onChange) {
209229
const res = await this.props.onChange(parsedNum);
210230
if (!isEmpty(value)) {
@@ -222,6 +242,15 @@ class InputSpinner extends Component {
222242
}
223243
}
224244

245+
/**
246+
* On buttons press out
247+
* @param e
248+
*/
249+
onPressOut(e) {
250+
this._clearTimers();
251+
this._resetHoldTime();
252+
}
253+
225254
/**
226255
* On Button Press
227256
* @param buttonDirection
@@ -342,8 +371,6 @@ class InputSpinner extends Component {
342371
return "";
343372
} else {
344373
return String(this.state.min);
345-
} else {
346-
return this.props.placeholder;
347374
}
348375
}
349376

@@ -374,11 +401,56 @@ class InputSpinner extends Component {
374401
}
375402

376403
/**
377-
* Get time to wait before increase/decrease on long press
404+
* Update holding time
405+
* @private
406+
*/
407+
_startHoldTime() {
408+
this.holdTime = new Date().getTime();
409+
}
410+
411+
/**
412+
* Get the holding time
413+
* @private
414+
*/
415+
_getHoldTime() {
416+
if (isEmpty(this.holdTime)) {
417+
return 0;
418+
}
419+
let now = new Date().getTime();
420+
return now - this.holdTime;
421+
}
422+
423+
/**
424+
* Reset holding time
425+
* @private
426+
*/
427+
_resetHoldTime() {
428+
this.holdTime = null;
429+
}
430+
431+
/**
432+
* Find the interval between changing values after a button has been held for a certain amount of time
378433
* @returns {number}
434+
* @author Tom Hardern <https://gist.github.com/taeh98/f709451457400818094d802cd33694d5>
435+
* @private
379436
*/
380-
getLongPressWaitingTime() {
381-
return 1000 / (this.withinRange(this.props.onLongPressSpeed, 1, 10) * 2);
437+
_getHoldChangeInterval() {
438+
const MIN_HOLD_CHANGE_INTERVAL = 10; // the minimum time interval between increases or decreases in value when holding a button
439+
const HOLD_CHANGE_ACCELERATION_FACTOR = this.props.acceleration; // a factor that controls how fast the speed of the change in value will accelerate while a button is held
440+
const HOLD_CHANGE_ACCELERATION_DELAY = this.props.accelerationDelay; // how long to wait after a button is being held before the acceleration of it speed going through values starts
441+
const TIME_HOLDING_BUTTON_FOR_MIN_CHANGE_INTERVAL =
442+
(Math.exp(MIN_HOLD_CHANGE_INTERVAL / -1) +
443+
HOLD_CHANGE_ACCELERATION_DELAY) /
444+
HOLD_CHANGE_ACCELERATION_FACTOR;
445+
const HOLD_TIME = this._getHoldTime(); // time on hold in ms
446+
447+
return HOLD_TIME >= TIME_HOLDING_BUTTON_FOR_MIN_CHANGE_INTERVAL
448+
? MIN_HOLD_CHANGE_INTERVAL // if the button has been held for long enough, return the minimum interval
449+
: -1 *
450+
Math.log(
451+
HOLD_CHANGE_ACCELERATION_FACTOR * HOLD_TIME -
452+
HOLD_CHANGE_ACCELERATION_DELAY,
453+
); // otherwise calculate with a logarithm => an exponential increase in speed
382454
}
383455

384456
/**
@@ -401,9 +473,10 @@ class InputSpinner extends Component {
401473
}
402474
}
403475

404-
let wait = this.getLongPressWaitingTime();
476+
let wait = this._getHoldChangeInterval();
405477
if (this.increaseTimer === null) {
406-
wait = this.props.onLongPressDelay;
478+
this._startHoldTime();
479+
wait = this.props.accelerationDelay;
407480
} else {
408481
if (this.props.onLongPress) {
409482
await this.props.onLongPress(num);
@@ -434,9 +507,10 @@ class InputSpinner extends Component {
434507
}
435508
}
436509

437-
let wait = this.getLongPressWaitingTime();
510+
let wait = this._getHoldChangeInterval();
438511
if (this.decreaseTimer === null) {
439-
wait = this.props.onLongPressDelay;
512+
this._startHoldTime();
513+
wait = this.props.accelerationDelay;
440514
} else {
441515
if (this.props.onLongPress) {
442516
await this.props.onLongPress(num);
@@ -892,7 +966,7 @@ class InputSpinner extends Component {
892966
disabled={this._isDisabledButtonLeft()}
893967
style={buttonStyle}
894968
onPressIn={this.decrease.bind(this)}
895-
onPressOut={this.clearTimers.bind(this)}
969+
onPressOut={this.onPressOut.bind(this)}
896970
{...this.props.leftButtonProps}>
897971
{this._renderLeftButtonElement()}
898972
</TouchableHighlight>
@@ -928,7 +1002,7 @@ class InputSpinner extends Component {
9281002
disabled={this._isDisabledButtonRight()}
9291003
style={buttonStyle}
9301004
onPressIn={this.increase.bind(this)}
931-
onPressOut={this.clearTimers.bind(this)}
1005+
onPressOut={this.onPressOut.bind(this)}
9321006
{...this.props.rightButtonProps}>
9331007
{this._renderRightButtonElement()}
9341008
</TouchableHighlight>

0 commit comments

Comments
 (0)