Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exponentially increase range #140

Closed
scooterlord opened this issue Nov 11, 2013 · 52 comments
Closed

Exponentially increase range #140

scooterlord opened this issue Nov 11, 2013 · 52 comments
Labels
Feature Feature requests/suggestions
Milestone

Comments

@scooterlord
Copy link

Hello and well done on your excellent work on this plugin!

It would be real nice if we could have exponential increase of the range values, meaning, if there's a huge price range for example 0-2.000.000, the first values would start in steps of 1 and as the range reaches it's end it should be in steps of 10.000.

In other words, variable steps. So, you would define multiple percentage values between which you could set a different step OR define points of range values in percentage width and give the value there.

Much like how ebay price-range works for example!

@leongersen
Copy link
Owner

I'm willing to put some time into this, as it has been requested before. Could you put some thought into the API design for this? How would you define where the jump are? Are they variable? Would you provide a logarithmic formula? It would be -very- helpful if you (or anyone else interested) could provide code-samples of how you'd expect to initialize noUiSlider with these options.

Thanks!

@scooterlord
Copy link
Author

Hello again!

Thanks for your interest. Although I am not a math expert, I think an easy way to do it would be the one I just explained.

You submit pairs of values: First is a percentage and sexond is the range value. I have created an image as an example:

http://i.imgur.com/cygTRW9.jpg

So, in the above case if you have a range from 0-200.000 and you input the 4 sets of values, the following happens:

The range from 0-20% is divided in steps so that the slider changes range values from 0-10.
For the range from 20%-80% is divided in steps so that the slider changes range values from 10-10.000
For the final range 80%-100% is divided in steps so that the slider changes range values from 10.000-200.000

It could also be possible to add a third value so that you define the position of these values on the actual slider. Which means for example that there could be:

val1: 20, 10, 50

Thios would mean that the 20% could be in the 50% of the slider rather than the actual 20%. I hope I get the message across. What do you think?

@leongersen
Copy link
Owner

Ok, but would the movement still be linear?

How about something like this:

// API proposal
slider.noUiSlider({
     range: [0, 200000]
    ,stops: {
// Stepping from 0% to 20% is implied...
        '20%': 10
        '75%': 10000
// ... as is stepping from 75% to 100%
    }
});

@leongersen
Copy link
Owner

Now, in all fairness, this doesn't really answer your question, and it isn't very useful for any practical implementation, but I've set up a small example of using Lagrange polynomial interpolation with noUiSlider.

Stops aren't in the works yet, this is just something fun to share :)

@scooterlord
Copy link
Author

cool! :) Well done! Seems like in the middle of the bar it's 1/4th of the range. I am certain you can do better than this, I hope you find some time to do so :) +1

@gerritvanaaken
Copy link

Would love to see this feature appear in an official release!

@leongersen
Copy link
Owner

To anyone interested in this: I'm looking for feedback as to how the step option should behave when multiple stops have been specified. Could you please share your ideas? Thanks!

@MagSch
Copy link

MagSch commented Dec 11, 2013

A little feedback to help you decide maybe:

What if you could just add values that would automatically decide the number of stops and what value each stop should be assigned to. Like this:

range: [0, 100000]
, steps: {10,50,150,500,1000,5000,10000,50000}

This would automatically create 8 steps inside the total range, dividing the whole range into 9 "pieces". Each piece would be 1/9 of the slider. Linear movement between the stops, based on the stops' individual values.

I used version 3 of your slider, and there was something like that in that one.

Interpolating polynomial would also be cool of course. The ultimate would be a setting to decide between these two alternatives but I might be asking too much. :)

Edit:
If there should be an option to define where the different steps would be placed on the slider, I suggest what you already suggested:

range: [0, 200000]
,stops: {
// Stepping from 0% to 20% is implied...
'20%': 10
'75%': 10000
// ... as is stepping from 75% to 100%
}

Linear movement between stops.

@mgibbs189
Copy link

Great discussion so far. Regarding percentages, we're talking about the slider width, correct?

stops: {
    '20%': 10,
    '75%': 10000
}

If the slider is 200px wide, then the value will be "10" at 40px (and "10000" at 150px).

@MagSch
Copy link

MagSch commented Dec 12, 2013

Yes I think percentage would be best. In my opinion that is more important,
and easier to kind of know where on the slider it is. It's also better for
responsive design.

@leongersen
Copy link
Owner

Yes, the percentages refer to the slide width/height. How would you expect the current step option to behave if this new stops (stepping?) option is used?

@MagSch
Copy link

MagSch commented Dec 12, 2013

That's a tricky one. :)

Using the current step option like it works today would not be suitable if you use the stops(stepping) option to create a simulated interpolating polynomia. The steps would be too big in the beginning of the slider and too small at the end. My first thought was that the step option then specifies the percentage for each stop within the stop itself. A little hard to explain... If you have 0 as start and 100 as first stop and 10 as step, each step in that section would be 10 (units). The second part of the slider might have the values between 100 and 500, making each step 40.

Another option would be to let step be 1 if you use stops(stepping), ie ignore what step is.

Hard to say which would be/work the best. It probably depends on the implemantation and your personal liking I guess.

@mgibbs189
Copy link

My vote is to stick with linear movements (but make steps flexible). How about something like this:

step: 1,
steps: {
    '25%': {
        value: 10,
        step: 5
    },
    '50%': {
        value: 1000,
        step: 1
    },
    '75%': {
        value: 10000,
        step: 500
    }
}

Each step could be either a scalar value (e.g. '50%': 1000,) or an object.

Also, since 0% is omitted, it's step is also 1 (the default).

@fjorgemota
Copy link

I suggest that step can receive a function in the context of the slider.

With it, the application can decide which to do, even when the value of step is fully dynamic and irregular.

That's my suggestion, and it's much more simpler. =)

@mgibbs189
Copy link

@fjorgemota that sounds interesting. Could you put together a code example?

I'm curious on how were you thinking of getting access to the properties (value, active handle, % dragged, etc) -- whether via $(this).data or passed directly into the function...

@fjorgemota
Copy link

@mgibbs189 I think that is possible to $.extend these status variables with the one found in the options passed to the constructor, using its as the context to the function.

In the case that originated this issue, some thing like:

    $("#price").noUiSlider({
      "range": [0, 2000000],
      // Other options..
      "step": function(){
        // This function does not receive any arguments. All the data can be acessed using `this`
        var max_value = this.range[1]; // We can get passed options too (this variable will not be used, its here only to demonstrate)
        var values = this.val();
        var handle_value = values[this.active_handle_index];
        if(handle_value < 1000){
          return 1;
        }
        else if(handle_value <10000){
          return 10;
        }
        else if(handle_value < 100000){
          return 100;
        }
        else if(handle_value < 1000000){
          return 1000;
        }
        else{
          return 10000;
        }
      }
  });

Okay, maybe thats not very beautiful, but it shows the idea. This way, things like formulas can be implemented too, what can be great. =)

Thinks like .data() calls is not very good (in my opinion) as it's not directly accessible and involves functions calls. But passing these type of data can be used too, as the effect is approximately the same.

But that is only a suggestion. (and can be not very good)

@mgibbs189
Copy link

@fjorgemota This looks great. step could continue accepting a scalar value too. Available options (add more if needed):

this.val // ['123'] or ['123', '456']
this.active_handle_index // 0 or 1
this.position // each handle's position from 0-100 ['25.2'] or ['25.2', '76.4']
this.range // same as options
this.start // same as options

@leongersen Any thoughts?

@MagSch
Copy link

MagSch commented Dec 13, 2013

Matt, I really like your suggestion:

step: 1,
steps: {
'25%': {
value: 10,
step: 5
},
'50%': {
value: 1000,
step: 1
}
'75%': {
value: 10000,
step: 500
}

@leongersen
Copy link
Owner

Wow, thanks for the input everyone! Implementing step on each individual range is one thing, but what happens when the next step is in a new sub-range? Does it step to the start? Make the previous step? Any ideas there?

@fjorgemota
Copy link

@leongersen Maybe the function that i suggested can have a context (a simple object that are passed and maintained along function calls). The library select2 uses that to allow the function to know what it doing at the last call. That allow to define correctly the step in the sub-range. =)

@leongersen
Copy link
Owner

@fjorgemota Could you elaborate? I get the idea of passing a function for step, and I like it, but I'm not sure what you mean in your last comment.

@fjorgemota
Copy link

@leongersen I think that what you say of sub-ranges is something like...in some interval, the step adruptally changes.

Can be a mistake of me, anyway, can you please send a example to i elaborate correctly my comment?

@leongersen
Copy link
Owner

Well:

 range: [0, 100]
,step: 7
,stepping: {
    '20%': 10
    '40%': 40
    '80%': 90
}

If the sub-range from 10 to 40, the steps are 10, 17, 24, 32, 38, ...oops. Where does the slider now step to? With non-linear ranges, I can no longer just convert step to a percentage relative to the range, if you get what I'm saying here.

@fjorgemota
Copy link

Yes, i get.

Because these type of problem - and the exponential number of possibilities (ok, thats just a joke) - i just suggested something like a function to allow the application to decide what the step to do (i dont see these type of problem involving sub-ranges with callback functions, or these problems exists in your opinion?)

Thanks for the explication 👍

@mgibbs189
Copy link

@leongersen @fjorgemota I think the function approach would require a lookahead. E.g. for every slider movement, the function would need to run twice. Once for the current movement, and one for the next (or previous if sliding left).

Assume the following:

  • Step = 7 when value >= 20
  • Step = 10 when value >= 40
  • Resolution = 1
At 20. Next value (27) has different range? No... add 7 to current value.
At 27. Next value (34) has different range? No... add 7 to current value.
At 34. Next value (41) has different range? Yes... set value to beginning of new range (40).
At 40. Next value (10) has different range? No... add 10 to current value.
At 50...

It's similar when sliding left (see resolution variable)...

At 55. Next value (45) has different range? No... subtract 10 from current value.
At 45. Next value (35) has different range? Yes... set value to (start_of_current_range - resolution)
At 39. Next value (32) has different range? No... subtract 7 from current value.
At 32. Next value (25) has different range? No... subtract 7 from current value.
At 25...

@leongersen
Copy link
Owner

And that is assuming the function returns the same value every time.

@mgibbs189
Copy link

The example above accounts for varying return values (see lines 3-4 of the first example).

@leongersen
Copy link
Owner

I wasn't clear enough: the same value for the same input. For fun: 100 * Math.random().

@mgibbs189
Copy link

@leongersen Can we assume that range is fixed, and the only things changing (besides step) are the Handle position(s) and Handle value(s)? EDIT: changed this.start to range

@leongersen
Copy link
Owner

range is the fixed, yes. The stepping option would also be fixed.

@leongersen
Copy link
Owner

I've settled on an API. Feedback welcome (I'd even say required. I want to get this right!).

// When 'spread' is set, the 'step' value can only be 'true'.
// If set, the slider will step between the VALUES
// provided in spread.
 step: true
// I'm looking for a better name for this setting
,spread: {
    // Set a value and a 'step' for the 
    // sub-range that STARTS here ( so from 10% => 40% )
     '10%': [ 500, 100 ]
    // Omit 'step' for unstepped behaviour 
    // from here to the next step. ( from 50% to 100% );
    ,'50%': [ 4000 ]
}

Additionally:

 spread: {
// The 'step' can be set for sub-range 0% => 10%. 
// 'value' for 0% will be ignored.
     '0%': [ 0, 30 ]
     '10%': [ 500, 100 ]
    ,'50%': [ 4000 ]
// Any 'value' or 'step' for 100% will be ignored.
    ,'100%': [ 3, 3 ]
}

@vdboor
Copy link

vdboor commented Dec 23, 2013

Curious:

  • what do the two numbers mean? Seeing [ 0, 30 ] and [ 500, 100 ] doesn't make sense to me.
  • why use a new variable, instead of using step for this?

Thanks a lot for the work on this code. I'm looking forward to both the serialization + variable step features too (in fact, already using the github version right now in my project).

@leongersen
Copy link
Owner

The values allow for setting 'subranges', and custom step values for each of those.

I'll explain by example:

If you want to have a 'subrange' of 0 - 100 on the first 10% of a slider, you might want to set a step of, say, 10 there. If the next subrange is 100 - 10000, the step could be 500. You'd need to set both values.

Be careful using the work-in-progress code in live projects. It isn't stable yet. That said: any feedback?

@leongersen
Copy link
Owner

To everyone: please have a look at http://refreshless.com/feedback/, and let me know your thoughts and ideas. Thanks!

@fjorgemota
Copy link

@leongersen If it helps, something like UserVoice - even to allow best suggestions - will be in someway best for the users of the library. :p

GitLab is an example of project that uses it for suggestions, and it is great =) - See: http://feedback.gitlab.com/forums/176466-general

By the way, i send my opinion soon.

Thanks.

@mgibbs189
Copy link

@leongersen it still needs work. I agree with @vdboor - the numbers are confusing and it's clunky to require a separate variable.

// Single step value
spread: 5
// multiple step values
spread: {
    '0%': { step: 5 },
    '20%': { step: 20, maxValue: 500 },
    '60%': { step: 60, maxValue: 2500 },
    '80%': { step: 200 }
}

@leongersen
Copy link
Owner

@mgibbs189, fair enough. The problem here is that spread provides values for both range and step. You could set a spread without providing any step values at all.

Merging this option would overcomplicate range, I feel. It would also be confusing with step being a separate option. If the step values for the subranges would be provided in step, all percentages would have to be duplicated. We'll need some way to merge all this, so that it makes sense, don't you agree?

@leongersen
Copy link
Owner

@fjorgemota Uservoice seems like a good fit. I'll consider adding such a feedback option to the documentation. Thanks!

@leongersen
Copy link
Owner

I've taken your comments into account; here's an idea:

// Range changes a ton! It now takes a 
// 'min' and 'max' property, instead of an array.
// You can set subranges, but these are not required.
range: {
    'min': {
        value: 2000
        step: 500
    }
/* <Optional> */
    '10%': {
        value: 3000,
        step: 100
    },
    '40%': {
        value: 6000,
        step: 400
    },
    '80%': {
        value: 8000,
        step: 800
    },
/* </Optional> */
    'max': {
        value: 10000
    }
}
// Enforce snapping between range values. Defaults to false.
snap: true,

The minimal setup is then pretty, and it can easily be extended without introducing new options.

range: {
    'min': {
        value: 2000
    }
    'max': {
        value: 10000
    }
}

@mgibbs189
Copy link

@leongersen I like that a lot. Given a range of 1 - 100 with a step of 5, will any of the following examples be supported in 6.0?

// Example 1 (backwards compatibility)
$('.test').noUiSlider({
    range: [1, 100],
    step: 5
});
// Example 2 (accept an object OR value)
$('.test').noUiSlider({
    range: {
        'min': { value: 1, step: 5 },
        'max': 100
    }
});

@leongersen
Copy link
Owner

I'm not sure about backwards compatibility. I'm replacing the entire Serialization API anyway, so adding backwards features here would risk bloating the option validation code. Example 2 makes more sense, as it writes up a lot easier. There is also still the option to replace the option/step object with an array.

@mgibbs189
Copy link

@leongersen Sounds good.

@leongersen
Copy link
Owner

While writing documentation, the syntax felt overly verbose, so I changed it again:

Minimal setup:

 range: { // Value
    'min': [  2000 ]
   ,'max': [ 10000 ]
}

Minimal setup with step. An alternative (not in range) syntax for step is available for simplicity. It is overwritten when specified within 'range'.

 step: 1000
,range: { // Value
    'min': [  2000 ]
   ,'max': [ 10000 ]
}

Additional subranges can be added in between:

 range: { // Value
    'min': [  2000 ]
   ,'10%': [  6000 ]
   ,'max': [ 10000 ]
}

Adding stepping on subranges:

 range: { // Value  Step
    'min': [  2000,  500 ]
   ,'10%': [  6000, 1000 ]
   ,'max': [ 10000 ]
}

I'm more happy with this, as it requires far less keywords.

@mgibbs189
Copy link

Just checking in - how's this coming along?

@leongersen
Copy link
Owner

Fair enough. I haven't had an time to properly finish this and other features (documentation, testing, etc.). I'm currently insanely busy (pfff). I hope to get everything up somewhere in February, when things settle down. Sorry for the delay.

@mgibbs189
Copy link

No rush - thanks for all your hard work thus far. noUiSlider is wonderful!

@a-sabaa
Copy link

a-sabaa commented Feb 21, 2014

Hello, I'm totally in love with your plugin but I was wondering if I can actually "hack it" for such behavior using the slide function? Maybe at least till you're done with this

@leongersen
Copy link
Owner

I know it took forever, but this option is new in noUiSlider 6. See the documentation on slider values for implementation details.

@sandstrom
Copy link

Awesome ⛵

noUiSlider is great!

@nmindab
Copy link

nmindab commented Jul 16, 2015

Fantastic! I have been looking for a range slider with good mobile support. A non-linear slider with pips was more than I even hoped to find!

@Revadike
Copy link

I developed my own dynamic idea that you may find useful. Here max is the range you want the slider to me (from 0 to max).

            const digits = max.toString().split(``).length;
            const range = {
                "min": 0,
                "max": max
            };

            let percentage = 0;
            for (let i = 1; i <= digits - 1; i++) {
                percentage += 100 / (digits - 1);
                range[`${percentage}%`] = Math.pow(10, i);
            }

            noUiSlider.create(slider, {
                "start": [0, max],
                "connect": true,
                "step": 1,
                "range": range
            });

@github-actions
Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 27, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Feature Feature requests/suggestions
Projects
None yet
Development

No branches or pull requests