Why does this toggle switch "snap" to the left but not to the right?

Here is a playground https://playground.babylonjs.com/#VVM792 in which I attempt to build a toggle on/off switch out of a slider GUI control by setting the minimum to 0, maximum to 1 and a step of 1.

If I click slightly to the left of the circle, the circle “thumb” snaps to into the “off” position as expected. However if I click to the right of the circle, nothing happens. I expected that clicking to the right of the thumb should toggle the switch into the “on” position. Instead I have to drag the thumb to the right to get the thumb to toggle into the “on” position.

What am I doing wrong? Am I missing something?

@owen Just try this, Whether this is your need

Hey @owen while I’m not exactly sure why the expected behavior isn’t working I came up with a nice workaround that fits your needs.

toggle.isPointerBlocker = true;
    let changed = false;
    toggle.onValueChangedObservable.add(eventData => {
        console.log(eventData)
        changed = true;
    })

    toggle.onPointerClickObservable.add( () => {
        if(changed != true) {
            toggle.value = 1;
        }
        changed = false;
    })

Since we know we’re only working with only 2 values we can assume if we click and we did not go to 0 we can force it to go to 1. Hope this helps. :slight_smile:

2 Likes

:sunglasses: cool! That is a clever fix, I had not thought of that. thanks!

1 Like

After looking into it a bit more I think I found the underlying issue:

https://github.com/BabylonJS/Babylon.js/commit/800885feadfd88d459c3223a98776e995e12b7ec

The slider has an _updateValueFromPointer function that takes the pointer’s x,y coordinates and computes the value for the slider:

value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
        }
var mult = (1 / this._step) | 0;
this.value = this._step ? ((value * mult) | 0) / mult : value;

I have setup a new playground that console logs these intermediate values as you mouse over the toggle switch:

https://playground.babylonjs.com/#VVM792#6

This playground demonstrates that, due to the bitwise OR operation with zero, which is essentially a Math.floor operation on the value, the slider value can never reach the maximum because it is always being round down. And the mouse coordinates that would be required to produce a value of the maximum lie just to the right of the toggle and aren’t reachable.

This isn’t just a problem between toggling between 0 and 1. In my new playground I set the maximum of 3, and if you click around you will see that the same issue happens. The maximum is unreachable by clicking and you the most you can get is 2.

I think perhaps this can be fixed by checking if step is 1 and then using regular Math.round on the value.

@Deltakosh what do you think?

Here is a playground with a “proper” fix?

https://playground.babylonjs.com/#VVM792#7

The toggle now correctly snaps to the maximum. Also works with other values of maximum and other values of step. Please try it out.

In the playground I monkey patched the prototype._updateValueFromPointer to keep everything in the original except the last line where I use Math.round.

Here is a link to my first PR, if this is acceptable: Fix for slider not snapping to maximum value by homanchou · Pull Request #12010 · BabylonJS/Babylon.js · GitHub

1 Like

PR was merged! Original playground of original question now works as expected. :heart_eyes:

1 Like