kitschpatrol / svelte-tweakpane-ui

A Svelte component library wrapping UI elements from Tweakpane, plus some additional functionality for convenience and flexibility.
https:///kitschpatrol.com/svelte-tweakpane-ui
MIT License
105 stars 3 forks source link

Logarithmic scale for generic input #5

Closed ecstrema closed 4 months ago

ecstrema commented 4 months ago

Hey! Awesome library!

I was looking into how to use a logarithmic scale for a slider, and realized it was more involved than listening to on:change and updating the value accordingly.

Do you have any pointers on how to do it?

kitschpatrol commented 4 months ago

Hey thanks and good question!

There's some discussion of this in the Tweakpane repo here and here... the author suggests writing a Tweakpane plugin as the only way to accomplish this "properly".

I'd be happy to add a Tweakpane Plugin that accomplishes this to the Svelte TweakpaneUI library, but I don't think anyone's implemented it yet.

You can get kind of close in Svelte Tweakpane UI be combining a Svelte reactive value with the Slider component's format prop to keep the slider's label in sync with the log value, something like this:

<script lang="ts">
  import { Slider } from 'svelte-tweakpane-ui';

  function logScale(x: number) {
    return Math.log10(1 + 99 * x) / Math.log10(100);
  }

  let linearValue = 1;
  $: logValue = logScale(linearValue);
</script>

<Slider
  bind:value={linearValue}
  min={0}
  max={1}
  format={(v) => logScale(v).toFixed(2)}
/>
<pre>
{logValue}
</pre>

Example: https://stackblitz.com/edit/vitejs-vite-x6nku5?file=src%2FApp.svelte

But the problem here is that format only affects the view of the display text input field, not the underlying value, so a number entered directly in the text field will be remapped from a linear to log scale, and appear to change, which feels weird.

The Wheel and Ring components have a wide prop that lets you turn off the text input field, so there's nothing (visible) to keep in sync inside the Tweakpane control itself... but this might only really make sense if you want an exponential scale or something.

(This makes me think that a similar wide option for the Slider and IntervalSlider components might be nice...)

So because of how the slider handle and text input field are coupled inside Tweakpane itself, there's not a great way to completely change the value scale that I can think of, sorry. Let me know if you end up finding an approach that works.

ecstrema commented 4 months ago

Thanks for the answer! Your solution perfectly fits my case, so I just modified it a bit and ended up with:

<script lang="ts">
  let frequencyExponent = 6;

  $: frequency = Math.pow(10, frequencyExponent);
</script>

<Slider bind:value={frequencyExponent} label="Frequency" min={0} max={7} format={(value) => Math.pow(10, value).toExponential(1)}/>

And I'm using frequency elsewhere in the app. It works very well, and it's small enough not to require a change in tweakpane or in this repo I believe.