如何创建具有非线性比例的滑块?

我有一个最小值为0且最大值为500的滑块。

我想当滑块变为100时,拇指位于滑块的中间。

我知道它看起来很奇怪,但有些程序使用缩放滑块来做,我相信它会更好。

这是一个非常有趣的问题,我不能不理会,希望我能得到你正在问的权利:)

您想通过在Thumb位于中间时指定函数的Y值,将SliderValue从线性更改为二次函数。

二次函数写在表单上
式

由于我们有3个点,因此我们有3组X和Y值。

 (X1, Y1) = 0, 0 (X2, Y2) = MiddleX, CenterQuadraticValue (in your case 100) (X3, Y3) = Maximum, Maximum (in your case 500) 

从这里开始,我们可以创建一个二次方程( 例如参见此链接 )
式

不幸的是,此图中的某些值最终低于0,因此它们必须被强制为0(我在答案的底部包含了一个图表)。

我创建了一个控件QuadraticSlider ,它派生自Slider并添加了两个依赖属性: QuadraticValueCenterQuadraticValue 。 它使用上面的公式基于ValueMaximumMinimumCenterQuadraticValue计算QuadraticValue 。 它也反过来:设置QuadraticValue更新Value 。 因此,不是绑定到Value ,而是绑定到QuadraticValue

编辑:最后一个版本是一个小马车。 修正了一些问题

  • 当“a”为0时,从QuadraticValue计算Value不再中断
  • 当推导为负时,使用二次解决方案中的错误根

我上传了一个示例应用程序,其中QuadraticSlider用于缩放图片。 可以指定所有参数,第一张图片使用Value ,另一张QuadraticValue

如果您想尝试一下,请在此处下载 。

看起来像这样
在此处输入图像描述

这就是图形的样子,注意0以下的值

在此处输入图像描述

显示值的良好公式是单调函数,例如幂曲线,采用以下forms:

 DisplayValue = A + B * Math.Exp(C * SliderValue); 

通过反转公式获得内部滑块值(例如从0到1):

 SliderValue = Math.Log((DisplayValue - A) / B) / C; 

现在如何获得A,B和C? 通过使用您给出的三个约束:

 f(0.0) = 0 f(0.5) = 100 f(1.0) = 500 

三个方程式,三个未知数,这是使用基本数学解决的:

 A + B = 0 A + B exp(C * 0.5) = 100 A + B exp(C) = 500 B (exp(C * 0.5) - 1) = 100 B (exp(C) - 1) = 500 exp(C) - 5 exp(C * 0.5) + 4 = 0 // this is a quadratic equation exp(C * 0.5) = 4 C = log(16) B = 100/3 A = -100/3 

产生以下代码:

 double B = 100.0 / 3; double C = Math.Log(16.0); DisplayValue = B * (Math.Exp(C * SliderValue) - 1.0); 

当内部值位于中间时,您可以看到显示值为100:

最终曲线

编辑 :由于要求通用公式,这里是。 鉴于:

 f(0.0) = x f(0.5) = y f(1.0) = z 

A,B和C的值为:

 A = (xz - y²) / (x - 2y + z) B = (y - x)² / (x - 2y + z) C = 2 * log((zy) / (yx)) 

请注意,如果x - 2y + z为零,则没有解决方案,您将得到除零。 那是因为在这种情况下,比例实际上是线性的。 你需要照顾那个特例。

让滑块保持原样,并使用ValueConverter进行绑定。 在ValueConverter中,使用非线性缩放来根据需要缩放值。

作为进一步的参考; 如果您对滑块的确切位置不感兴趣,以对应于比例尺中的特定值,但仍希望滑块对比例开始时的值比对结束时的值更敏感,那么可能使用简单的对数比例可能就够了。

 public class LogScaleConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { double x = (int)value; return Math.Log(x); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { double x = (double)value; return (int)Math.Exp(x); } } 

根据sam hocevar的算法,这里是我放在一起的代码:

 ///  /// Scale a linear range between 0.0-1.0 to an exponential scale using the equation returnValue = A + B * Math.Exp(C * inputValue); ///  /// The value to scale /// The value returned for input value of 0.5 /// The value to be returned for input value of 1.0 ///  private double ExpScale(double inputValue, double midValue, double maxValue) { double returnValue = 0; if (inputValue < 0 || inputValue > 1) throw new ArgumentOutOfRangeException("Input value must be between 0 and 1.0"); if (midValue <= 0 || midValue >= maxValue) throw new ArgumentOutOfRangeException("MidValue must be greater than 0 and less than MaxValue"); // returnValue = A + B * Math.Exp(C * inputValue); double M = maxValue / midValue; double C = Math.Log(Math.Pow(M - 1, 2)); double B = maxValue / (Math.Exp(C) - 1); double A = -1 * B; returnValue = A + B * Math.Exp(C * inputValue); return returnValue; } 

Meleak的post的一些补充。 我略微纠正了QuadraticSlider。 事件处理程序存在问题(QuadraticValueChanged上的事件具有prevoius值;初始化期间的事件超出范围[min,max]值)。

 protected override void OnValueChanged(double oldValue, double newValue) { QuadraticValue = a * Math.Pow(Value, 2) + b * Value + c; base.OnValueChanged(oldValue, newValue); } public double QuadraticValue { get { var qv = (double)GetValue(QuadraticValueProperty); if (double.IsNaN(qv)) qv = 0; qv = Math.Max(qv, base.Minimum); qv = Math.Min(qv, base.Maximum); return qv; } set { SetValue(QuadraticValueProperty, value); } } 

概括Sam Hocevar的优秀答案:

让预期的最大值为M.
让滑块中点的值为m。
(显然,0

  A = - M*m^2 / (M^2 - 2*m*M) B = M*m^2 / (M^2 - 2*m*M) C = Ln((M - m)^2 / m^2) // <- logarithm to the base of e, I always think of 'Log' as base 10 

必须注意单独处理2 * m = M的情况,因为这会导致除以0.但是在这种情况下,无论如何,滑块都会以线性方式运行。

从M / 2和M之间选择m得到一个对数曲线:有效滑块值首先快速上升,然后缓慢上升。 这基本上反转了效果并使用户更好地控制更高的值。

如上所述,接近M / 2的m使得滑块基本上是线性的。
选择接近0或接近M的m可以精确控制极低或极高的值。

我想可以将其与第二个滑块结合使用,将第二个滑块设置为0到M之间的值,以更改真实滑块的... errr ...敏感区域。