Logarithmischer Schieberegler

77

Ich habe einen Schieberegler mit Werten zwischen 0 und 100.

Ich möchte sie einem Bereich von 100 bis 10.000.000 zuordnen.

Ich habe einige Funktionen im Internet gesehen, aber sie sind alle in C ++. Ich brauche es in Javascript.

Irgendwelche Ideen?

user103773
quelle

Antworten:

182

Sie können eine Funktion wie diese verwenden:

function logslider(position) {
  // position will be between 0 and 100
  var minp = 0;
  var maxp = 100;

  // The result should be between 100 an 10000000
  var minv = Math.log(100);
  var maxv = Math.log(10000000);

  // calculate adjustment factor
  var scale = (maxv-minv) / (maxp-minp);

  return Math.exp(minv + scale*(position-minp));
}

Die resultierenden Werte stimmen mit einer logarithmischen Skala überein:

js> logslider(0);
100.00000000000004
js> logslider(10);
316.22776601683825
js> logslider(20);
1000.0000000000007
js> logslider(40);
10000.00000000001
js> logslider(60);
100000.0000000002
js> logslider(100);
10000000.000000006

Die umgekehrte Funktion würde, mit den gleichen Definitionen für minp, maxp, minv, maxvund scaleeine Schieberposition von einem Wert wie folgt berechnen:

function logposition(value) {
   // set minv, ... like above
   // ...
   return (Math.log(value)-minv) / scale + minp;
}


Alles in allem würde es in einer Klasse und als Funktionscode-Snippet so aussehen:

// Generic class:

function LogSlider(options) {
   options = options || {};
   this.minpos = options.minpos || 0;
   this.maxpos = options.maxpos || 100;
   this.minlval = Math.log(options.minval || 1);
   this.maxlval = Math.log(options.maxval || 100000);

   this.scale = (this.maxlval - this.minlval) / (this.maxpos - this.minpos);
}

LogSlider.prototype = {
   // Calculate value from a slider position
   value: function(position) {
      return Math.exp((position - this.minpos) * this.scale + this.minlval);
   },
   // Calculate slider position from a value
   position: function(value) {
      return this.minpos + (Math.log(value) - this.minlval) / this.scale;
   }
};


// Usage:

var logsl = new LogSlider({maxpos: 20, minval: 100, maxval: 10000000});

$('#slider').on('change', function() {
   var val = logsl.value(+$(this).val());
   $('#value').val(val.toFixed(0));
});

$('#value').on('keyup', function() {
   var pos = logsl.position(+$(this).val());
   $('#slider').val(pos);
});

$('#value').val("1000").trigger("keyup");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Input value or use slider:
<input id="value" />
<input id="slider" type="range" min="0" max="20" />

etw
quelle
5
Gibt es eine Chance, dass Sie die entgegengesetzte Funktion haben? Geben Sie anhand des Protokollwerts an, an welcher Position sich der Schieberegler befindet.
GeekyMonkey
Brilliant kann nicht genug Danke sagen! Ich habe einen Mathematikabschluss, aber es ist so lange her, dass so etwas zu Frustration führt. Dank SO (und Ihnen) kann das umgangen werden!
Gaz
1
Ist das nicht ein exponentieller Schieberegler? Gibt es eine Möglichkeit, die Kurve logarithmisch umzukehren? Damit der Wert schnell steigt und dann gegen Ende langsam geht?
Nick Hooked
2
@ NickH: Kommt darauf an, wie du es betrachtest. Derzeit steigt der zugehörige Wert immer schneller an, wenn die Position des Schiebereglers steigt. Aus dieser Perspektive ist er also exponentiell. Wenn Sie es umgekehrt brauchen, wechseln Sie valueund position, um den gegenteiligen Effekt zu erzielen.
etw
@sth Danke, ich werde immer mit exponentiell und logaritmisch verwechselt. Gibt es eine Möglichkeit, die Kurve in Ihrer Lösung weniger schnell ansteigen zu lassen (linearer, aber immer noch exponentiell)? Bei einer Eingabe von 50 liegt der Wert irgendwo um 550.
Nick Hooked
9

Ich denke, Sie können diese Formel verwenden, um die gewünschte Verteilung zu erhalten:

var value = Math.floor(-900 + 1000*Math.exp(i/10.857255959));

Hier ist eine in sich geschlossene Seite, auf der die Werte gedruckt werden, die Sie für Ihren Schieberegler 0-100 erhalten, nachdem Sie diese Formel durchlaufen haben:

<html><body><script>
for (var i = 0; i <= 100; i++) {
    var value = Math.floor(-900 + 1000*Math.exp(i/10.857255959));
    document.write(value + "<br>");
}
</script></body></html>

Die Zahlen reichen von 100 bis 10.000.000, was für mein mathematisch verrostetes Auge die gewünschte Verteilung ist. 8-)

RichieHindle
quelle
4
Begonnen mit Math.exp (i) und von Hand angepasst, bis die Zahlen passen. Mein Mathe-Fu ist schwach, aber ich kann für England binär hacken. 8-)
RichieHindle
Diese beiden Antworten stellen die Kraft des Wissens in der Mathematik dar. Dies sollte jedem Programmierer die Anforderung des Lernens von Mathematik zum Ausdruck bringen.
Asyncwait
9

Nicht ganz die Frage zu beantworten, aber für Interessenten ist die umgekehrte Zuordnung der letzten Zeile

return (Math.log(value)-minv)/scale + min;

nur um zu dokumentieren.

HINWEIS Der Wert muss> 0 sein.

siliconeagle
quelle
5

Das Problem mit einem echten logarithmischen Schieberegler liegt am unteren Ende. Mehrere Punkte auf dem Schieberegler führen wahrscheinlich zu doppelten Werten.

Aus rein UI-Sicht bietet es auch keine sehr intuitive Ausgabe für die Benutzereingaben.

Ich denke, eine bessere Option ist die Verwendung einer "gestuften" Transformation mit gleichmäßiger Verteilung.

Geben Sie hier die Bildbeschreibung ein

Mit anderen Worten, wir geben eine Reihe von Inkrementen an, die wir verwenden möchten (z. B. 1, 10, 100, 1000). Dann teilen wir den Schieberegler basierend auf der Anzahl der von uns definierten Inkremente in gleiche Teile. Wenn wir durch unsere verschiedenen Abschnitte gleiten, wird die Schiebereglerausgabe um das jeweilige Inkrement erhöht.

ARBEITSDEMO

REAKTIEREN SIE DEN CODE

Im obigen Beispiel definieren wir unseren min, maxund intervalsArray.

<ExpoStepSlider
  intervals={[1, 2, 5, 10, 100, 1000]}
  min={1}
  max={50000}
/>

Wir müssen dann die Anzahl der diskreten Werte ermitteln, die unser Schieberegler haben muss, damit er basierend auf unseren definierten Intervallverteilungen ordnungsgemäß von min nach max wechselt.

let sliderPoints = Math.ceil(
  (max - min) / 
  intervals.reduce((total, interval) => total + interval / intervals.length, 0)
);

In diesem Fall 535.

Hinweis : Ihre Schiebereglerpunkte sollten die Anzahl der Pixel im Schieberegler nicht überschreiten

Schließlich transformieren wir unsere Ausgabe einfach mit dem oben beschriebenen Algorithmus. Das Codebeispiel erledigt auch einige Arbeiten, sodass die Ausgabe für das aktuelle Schrittintervall immer rund ist.

NSjonas
quelle
2
Das ist großartig, danke, dass du den Code geteilt hast! Ich war mir nicht einmal bewusst, dass der Schieberegler so reagieren sollte
Chuck Bergeron
Sehr beeindruckend! Ihr Kommentar war wahr, diese Methode hat mein Ziel viel besser erreicht als eine logarithmische Funktion
Alex
1

Ich habe nach dem logarithmischen Schieberegler für Winkel gesucht , kann aber keinen finden und bin dann auf diese Antwort gestoßen.

Und ich habe das für Angular 2+ erstellt (Demo ist in Angular 6): WORKING DEMO

Vielen Dank an @sth für den Ausschnitt:

function LogSlider(options) {
   options = options || {};
   this.minpos = options.minpos || 0;
   this.maxpos = options.maxpos || 100;
   this.minlval = Math.log(options.minval || 1);
   this.maxlval = Math.log(options.maxval || 100000);

   this.scale = (this.maxlval - this.minlval) / (this.maxpos - this.minpos);
}

LogSlider.prototype = {
   // Calculate value from a slider position
   value: function(position) {
      return Math.exp((position - this.minpos) * this.scale + this.minlval);
   },
   // Calculate slider position from a value
   position: function(value) {
      return this.minpos + (Math.log(value) - this.minlval) / this.scale;
   }
};
Vivek Doshi
quelle