2017-06-20 45 views
5

我有一個範圍的輸入場,與改變,當我拖動一個數值:遞歸`setTimeout`不從事預期的方式

enter image description here

當我一路拖到權,輸入的最大值增加。然後,當我釋放(onMouseUp或onTouchEnd),最大值減小,這樣我可以進一步拖累,繼續通過拖動增加最大:

enter image description here

當我一路拖到左邊,最小值的投入減少。然後,當我釋放(onMouseUp或onTouchEnd),min值增加,這樣我可以進一步拖動並繼續通過拖動減小分鐘:

enter image description here

我應該總是具有一定範圍的99例如,如果我增加了最高值530,最小值將是431

問題:

我有兩個遞歸setTimeout S爲minmax值改變設置。

當用戶第一次拖到任何一邊時,數字應該慢慢改變。如果他們持續2秒鐘,這個數字應該更快增加。相關代碼:

// After arbitrary period, increase the rate at which the max value increments 
this.fasterChangeStake = setTimeout(() => { 
    this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT; 
}, 2000); 

這是第一次運作。但隨後的時間,它鎖到快超時第一:

enter image description here

儘管這是我的超時結算拖動結束時:

clearTimeout(this.increaseStakeLimits); 
clearTimeout(this.decreaseStakeLimits); 
clearTimeout(this.timer); 

爲什麼不積極參與第一(慢)超時?

Codepen:https://codepen.io/alanbuchanan/pen/NgjKMa?editors=0010

JS:

const {observable, action} = mobx 
const {observer} = mobxReact 
const {Component} = React 

@observer 
class InputRange extends Component { 
    constructor() { 
    super(); 
    this.INITIAL_STAKE_CHANGE_TIMEOUT = 200; 
    this.FAST_STAKE_CHANGE_TIMEOUT = 20; 
    this.SNAP_PERCENT = 10; 
    this.increaseStakeLimits = this.increaseStakeLimits.bind(this); 
    this.decreaseStakeLimits = this.decreaseStakeLimits.bind(this); 
    } 

    @observable min = 0; 
    @observable max = 99; 
    @observable stakeChangeTimeout = this.INITIAL_STAKE_CHANGE_TIMEOUT; 
    @observable isIncreasing = false; 
    @observable isDecreasing = false; 
    @observable stake = 0; 
    @action updateStake = (amount) => { 
    if (amount > -1) { 
     this.stake = amount 
    } 
    }; 

    increaseStakeLimits() { 
    const { updateStake } = this; 

    this.max = this.max += 1; 
    this.min = this.max - 99 
    updateStake(this.max); 

    // After arbitrary period, increase the rate at which the max value increments 
    this.fasterChangeStake = setTimeout(() => { 
     this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT; 
    }, 2000); 

    // Recursive call, like setInterval 
    this.timer = setTimeout(this.increaseStakeLimits, this.stakeChangeTimeout); 
    this.isIncreasing = true; 
    } 

    decreaseStakeLimits() { 
    console.warn('this.stake:', this.stake) 
    const { stake } = this 
    const { updateStake } = this; 

    this.min = this.min -= 1; 
    this.max = this.min + 99 
    updateStake(this.min); 

    // After arbitrary period, increase the rate at which the max value increments 
    this.fasterChangeStake = setTimeout(() => { 
     this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT; 
    }, 2000); 

    // Recursive call, like setInterval 
    this.timer = setTimeout(this.decreaseStakeLimits, this.stakeChangeTimeout); 
    this.isDecreasing = true; 

    } 

    handleStakeChange = e => { 
    clearTimeout(this.increaseStakeLimits); 
    clearTimeout(this.decreaseStakeLimits); 
    clearTimeout(this.timer); 

    const { updateStake } = this; 
    const { stake } = this; 

    const val = Number(e.target.value) 

    // User has scrolled all the way to the right 
    if (val >= this.max) { 
     console.warn("scrolled to right") 
     this.increaseStakeLimits(); 

    // User has scrolled all the way to the left 
    } else if (val <= this.min) { 
     console.warn("scrolled to left") 
     if (val > -1) { 
     this.decreaseStakeLimits(); 
     } 
    } else { 
     updateStake(val); 
    } 
    }; 

    handleRelease =() => { 
    console.warn("RANGE:", this.max - this.min) 
    console.warn("released"); 
    clearTimeout(this.fasterChangeStake); 
    clearTimeout(this.timer); 
    // Reset the timeout value to the initial one 
    this.stakeChangeTimeout = this.INITIAL_STAKE_CHANGE_TIMEOUT; 
    this.SNAP_PERCENT = 10 
    const snapAmount = this.SNAP_PERCENT 
    if (this.isIncreasing) { 
     this.max += snapAmount 
    } 

    if(this.isDecreasing && this.min > 0) { 
     this.min -= snapAmount 
    } 

    this.isIncreasing = false; 
    this.isDecreasing = false; 
    }; 

    render() { 
    const { stake } = this; 

    const style = { 
     backgroundSize: 
     (stake - this.min) * 100/(this.max - this.min) + "% 100%" 
    }; 

    return (
     <div className="rangeContainer"> 
     <div>{this.stake}</div> 
     <div className="inputContainer"> 
      <input 
      id="betRangeId" 
      type="range" 
      min={this.min} 
      max={this.max} 
      step="1" 
      ref={input => { 
       this.textInput = input; 
      }} 
      value={this.stake} 
      onChange={this.handleStakeChange} 
      onTouchEnd={this.handleRelease} 
      onMouseUp={this.handleRelease} 
      style={style} 
      /> 
     </div> 
     </div> 
    ); 
    } 
} 

ReactDOM.render(<InputRange />, document.getElementById('root')) 

回答

2

這是一個非常聰明的UI!

increaseStakeLimits()大火持續了整個時間在使用者將滑塊移動到最右邊,所以你不斷設置新的下一個定時器,兩秒後改變this.stakeChangeTimeout以較短的時間間隔(和this.fasterChangeStake不斷設置新的值)。這些繼續射擊即使handleRelease()嘗試區間重置爲長值,這迫使它回到快速模式,直到所有的人都解僱(在handleRelease()clearTimeout只抓住其中的一個。)

可以解決這個問題僅通過設置超時說一次:

if (!this.fasterChangeStake) { 
    this.fasterChangeStake = setTimeout(() => { 
    this.stakeChangeTimeout = this.FAST_STAKE_CHANGE_TIMEOUT; 
    this.fasterChangeStake = false; // <-- also do this in handleRelease() after clearing the timeout 
    }, 2000); 
} 

https://codepen.io/anon/pen/OgmMNq?editors=0010