[英]Balancing input sliders gives inconsistent results
我有几个范围输入滑块,它们应该平衡自己,总和为 100¹,无论滑块的数量如何。 对于这个例子,我将展示最少两个,但问题出现在任意数量的情况下。 我正在使用 Svelte,但这可能也适用于香草 JS。
¹:全部要求:
<script lang="ts">
const sliders = [
{ id: 1, percentage: 100 },
{ id: 2, percentage: 100 },
// { id: 3, percentage: 100 },
// { id: 4, percentage: 100 },
// { id: 5, percentage: 100 },
];
const handleInput = (event: InputEvent) => {
const element = (event.target as HTMLInputElement);
const newValue = Number(element.value);
const sliderId = Number(element.dataset["slider"]);
sliders.find((slider) => slider.id == poolId).percentage = newValue;
balanceSliders();
}
const balanceSliders = () => {
const total = sliders
.map((slider) => slider.percentage)
.reduce((prev, next) => prev + next, 0);
const normaliser = 100 / total;
sliders.forEach((slider) => {
slider.percentage = slider.percentage * normaliser;
});
};
balanceSliders();
</script>
<form>
{#each sliders as {id, percentage}}
<div>
<input type="range" min="0" max="100" step="0.01" data-slider={id} bind:value={percentage} on:input={handleInput} />
<span>{percentage.toFixed(2)}</span>
</div>
{/each}
</form>
这可能会做得更好,我愿意接受建议。 但主要问题是,当我将 slider 带到接近 100 时,总和会超过 100。此外,如果我慢慢移动 slider,我会得到一些东西:
对于我的目的而言,这显然是不可接受的。
速度影响错误的事实向我表明,问题出在 Svelte/事件处理部分的某个地方,而不是公式本身。
只有在用户停止更改滑块后,我才能让滑块自行平衡,但出于演示目的,我希望它们在输入时也能保持平衡。
有任何想法吗?
这是解决方案,您可以使其动态化并根据需要进行优化。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<div id="app"></div>
<div class="slidecontainer">
<p>Slider 1</p>
<span class="slider1Val">50%</span>
<input
class="slider1"
type="range"
min="1"
max="100"
value="50"
class="slider"
id="myRange"
/>
<p>Slider 2</p>
<span class="slider2Val">50%</span>
<input
class="slider2"
type="range"
min="1"
max="100"
value="50"
class="slider"
id="myRange"
/>
</div>
<script src="src/index.js"></script>
</body>
</html>
CSS
body {
font-family: sans-serif;
}
.slidecontainer {
display: flex;
flex-direction: column;
}
Javascript
import "./styles.css";
const slider1 = document.querySelector(".slider1");
const slider2 = document.querySelector(".slider2");
const balanceSliders = (e) => {
if (e.target.classList.contains("slider1")) {
const slider1Val = document.querySelector(".slider1").value;
slider2.value = 100 - slider1Val;
document.querySelector(".slider1Val").innerHTML = slider1Val + "%";
document.querySelector(".slider2Val").innerHTML = slider2.value + "%";
} else {
const slider2Val = document.querySelector(".slider2").value;
slider1.value = 100 - slider2Val;
document.querySelector(".slider2Val").innerHTML = slider2Val + "%";
document.querySelector(".slider1Val").innerHTML = slider1.value + "%";
}
};
slider1.addEventListener("input", (e) => balanceSliders(e));
slider2.addEventListener("input", (e) => balanceSliders(e));
我将删除值绑定并使用稍微不同的逻辑处理handleInput
中的所有更改。
假设起始值为 50-30-20 并且第一个 slider 移动到 60,则差值为 10。考虑到它们当前的比例,必须从其他滑块中减去此差。 我会根据他们的百分比总和来计算这个比例。
开始50 - 30 - 20
30 + 20 = 50
30: 30/50 = 0.6
→ 30 - 0.6 * 10
20: 20/50 = 0.4
→ 20 - 0.4 * 10
结束60 - 24 - 16
这适用于所有情况,除了当一个 slider 为 100 并且其他的总和为 0 时。在这种情况下,它们的份额是 1/滑块的数量,差异应该被分割到。
REPL
<script>
let sliders = [
{ id: 1, percentage: 100 },
{ id: 2, percentage: 100 },
{ id: 3, percentage: 100 },
// { id: 4, percentage: 100 },
// { id: 5, percentage: 100 },
];
sliders = sliders.map(slider => {
slider.percentage = 100/sliders.length
return slider
})
const handleInput = (event) => {
const changedSlider = event.target;
const sliderId = Number(changedSlider.dataset["slider"]);
const slider = sliders.find((slider) => slider.id == sliderId)
const currentValue = slider.percentage
const newValue = Number(changedSlider.value);
const difference = newValue - currentValue
slider.percentage = newValue;
const otherSliders = sliders.filter(slider => slider.id !== sliderId)
const otherSlidersPercentageSum = otherSliders.reduce((sum, slider) => sum += slider.percentage, 0)
otherSliders.forEach(slider => {
const share = otherSlidersPercentageSum === 0 ? 1 / otherSliders.length : slider.percentage / otherSlidersPercentageSum
slider.percentage = slider.percentage - difference * share
return slider
})
sliders = sliders
}
</script>
<form>
{#each sliders as {id, percentage}}
<div>
<input type="range" min="0" max="100" step="0.01" data-slider={id} value={percentage} on:input={handleInput} />
<span>{Math.max(percentage.toFixed(2), 0)}</span>
<!-- Math.max to prevent -0 -->
</div>
{/each}
</form>
将此与您的方式进行比较
开始50 - 30 - 20
改变60 - 30 - 20
新总和110
标准化100 / 110 (~0.9)
结束54,54 - 27,27 - 18,18
虽然这总和为 100,但故意更改的值被再次直接修改。 从示例中删除bind:value
并在balanceSliders
末尾添加sliders=sliders
以便 Svelte 知道更改后,在将 slider 拖动到 100 REPL时可以注意到这一点
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.