我有一个最小值为0且最大值为500的滑块。
我想当滑块变为100时,拇指位于滑块的中间。
我知道它看起来很奇怪,但有些程序使用缩放滑块来做,我相信它会更好。
答案 0 :(得分:36)
显示值的一个好的公式是单调函数,例如幂曲线,如下所示:
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((z-y) / (y-x))
请注意,如果x - 2y + z
为零,则没有解决方案,您将得到除零。那是因为在这种情况下,比例实际上是线性的。你需要照顾那个特例。
答案 1 :(得分:11)
让滑块保持原样并使用ValueConverter进行绑定。在ValueConverter中,使用非线性缩放来根据需要缩放值。
答案 2 :(得分:8)
作为进一步的参考;如果您对滑块的确切位置不感兴趣,以对应于比例尺中的特定值,但仍希望滑块对比例开始时的值比对结束时的值更敏感,那么可能使用简单的对数比例可能就够了。
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);
}
}
答案 3 :(得分:4)
这是一个非常有趣的问题,我不能不理会,希望我能得到你正确的问题:)
您想通过在Thumb位于中间时指定函数的Y值,将Value
的{{1}}从线性更改为二次函数。
二次函数写在形式
上
由于我们有3个点,因此我们有3组X和Y值。
Slider
从这里开始,我们可以创建一个二次方程(see this link for example)
不幸的是,此图表中的某些值最终低于0,因此必须将它们强制为0(我在答案的底部包含了一个图表)。
我创建了一个控件(X1, Y1) = 0, 0
(X2, Y2) = MiddleX, CenterQuadraticValue (in your case 100)
(X3, Y3) = Maximum, Maximum (in your case 500)
,它派生自QuadraticSlider
并添加了两个依赖属性:Slider
和QuadraticValue
。它使用上面的公式根据CenterQuadraticValue
,QuadraticValue
,Value
和Maximum
计算Minimum
。它也是相反的:设置CenterQuadraticValue
更新QuadraticValue
。因此,不要绑定到Value
,而是绑定到Value
。
编辑:最后一个版本是一个小小的车。修正了一些事情
QuadraticValue
计算Value
不再中断
我上传了一个示例应用程序,其中QuadraticValue
用于缩放图片。可以指定所有参数,第一张图片使用QuadraticSlider
,另一张Value
。
Download it here如果您想尝试一下。
看起来像这样
这就是图表的样子,请注意0
以下的值
答案 4 :(得分:2)
根据sam hocevar的算法,这里是我放在一起的代码:
/// <summary>
/// Scale a linear range between 0.0-1.0 to an exponential scale using the equation returnValue = A + B * Math.Exp(C * inputValue);
/// </summary>
/// <param name="inoutValue">The value to scale</param>
/// <param name="midValue">The value returned for input value of 0.5</param>
/// <param name="maxValue">The value to be returned for input value of 1.0</param>
/// <returns></returns>
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;
}
答案 5 :(得分:1)
Meleak的帖子的一些补充。 我略微纠正了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);
}
}
答案 6 :(得分:0)
概括Sam Hocevar的优秀答案:
让预期的最大值为M. 必须注意单独处理2 * m = M的情况,因为这会导致除以0.但在这种情况下,无论如何,你都会让滑块以线性方式运行。 从M / 2和M之间选择m得到一个对数曲线:有效滑块值首先快速上升,然后慢慢上升。这基本上反转了效果,并使用户能够更好地控制更高的值。 如上所述,接近M / 2的m使得滑块基本上是线性的
选择接近0或接近M的m可以很好地控制非常低或非常高的值。 我想可以将其与第二个滑块结合使用,将第二个滑块设置为0到M之间的值,以更改真实滑块的...... errr ...敏感区域。
让滑块中点的值为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
答案 7 :(得分:0)
不像这里的其他答案那么优雅,但是除了平滑的曲线外,您还可以使用两条直线。将y作为滑块的位置,将x作为实际值,您将使用(y1 - y2)/(x1 - x2)
获取斜率,然后使用y - y1 = slope * (x - x1)
获取y截距。为此,请对滑块的每半部分执行此操作,然后使用条件语句根据值是大于还是小于50来区别对待值。您所用的基本数学方法:
(0 - 50)/(0 - 100)
-50/-100
1/2
y = x/2
...因为我们知道y的截距为0,然后...
(50-100)/(100-500)
-50/-400
1/8
y - 50 = 1/8 * (x - 100)
y - 50 = x/8 - 25/2
y = x/8 + 75/2
从用户体验的角度来看,使用曲线绝对会更好(我特别喜欢Cesar's log idea),因为它避免了尖锐的“弯头”,在该弯头处滑块开始以完全不同的速率突然增加值。但是这种方法易于实现且灵活(您可以轻松添加更多具有不同比率的区域),因此我认为还是值得一提。
答案 8 :(得分:0)
这里是推导指数方程的代码:https://play.golang.org/p/JlWlwZjoebE