gpt4 book ai didi

jquery - JS中的纺车,如何在随机选择的切片的中心自动停止旋转

转载 作者:行者123 更新时间:2023-12-04 13:10:33 26 4
gpt4 key购买 nike

引用和这个问题的延续:Making an image spin with css and jQuery

我有兴趣进一步采用这种方法。一个由七部分组成的轮子开始旋转,当轮子停止旋转时,会显示来自相关 block 的正确值。我有一个不同的需要,我想实现。单击时,我的七部分轮停在随机区域,但应始终停在该部分的中心。我将如何实现此功能?现在,我的轮子随机停止,有时停在边缘或轮子的一部分没有任何内容。在我看来,要从零点开始第一站,我需要做一些数学运算,例如 ((360/7)/2),它得出第一个 block 的中心。我应该如何将数学结合到脚本中以始终停在其目标 block 的中心?

这是我到目前为止能够构建的...

<html>
<style>
.roulette_center {
position: absolute;
top: 255px;
left: 258px;
cursor: pointer;

}
.roulette_center:hover {
transform: scale(1.01);

}

.roulette_wheel{
touch-action:auto;pointer-events:painted
}
</style>
<body>
<div style="z-index: 0; position: relative">
<span style="position: absolute; top: 0px; left: 360px; font-weight:700;font-size:24px;" id="score">&nbsp;</span>
<img class="roulette_wheel" src="https://www.dropbox.com/s/6kp3fmtp72aj3vy/wellness-wheel.png?raw=1" />
<img class="roulette_center" src="https://www.dropbox.com/s/52x8iyb1nvkjm0w/wheelCenter.png?raw=1" onclick="roulette_spin(this)" onfocus="transform()">
<div style="position: absolute; top: 30px; left: 350px; background-color: red; width: 1px; height: 60px;"></div>

</div>

<script>
var force = 0;
var angle = 0;
var rota = 1;
var inertia = 0.985;
var minForce = 15;
var randForce = 15;
var rouletteElem = document.getElementsByClassName('roulette_wheel')[0];
var scoreElem = document.getElementById('score');

var values = [
"Spititual", "Emotional", "Intellectual", "Physical", "Social", "Environmental", "Financial"
].reverse();

function roulette_spin(btn) {
// set initial force randomly
force = Math.floor(Math.random() * randForce) + minForce;
requestAnimationFrame(doAnimation);
}

function doAnimation() {
// new angle is previous angle + force modulo 360 (so that it stays between 0 and 360)
angle = (angle + force) % 360;
// decay force according to inertia parameter
force *= inertia;
rouletteElem.style.transform = 'rotate(' + angle + 'deg)';
// stop animation if force is too low
if (force < 0.05) {
// score roughly estimated
scoreElem.innerHTML = values[Math.floor(((angle / 360) * values.length) - 0.5)];
return;
}
requestAnimationFrame(doAnimation);
}
</script>
</body>
</html>

最佳答案

这是一个解决方案,用于处理类似 flex 的捕捉到停止切片的中心。

它利用我称之为吸引子的东西,根据目标(最近切片的中心)改变旋转速度。不涉及 CSS 转换,仅涉及 v​​anilla javascript。

你离吸引子越近,将你拉向它的力就越强。它被计算为到吸引子的距离的保护倒数。当距离接近 0 时(当然当它刚好为 0 时),保护意义防止非常大的值

我喜欢这个解决方案,因为它使用简单的人工物理学(车轮惯性和车轮片下的一种磁铁)模拟我们需要的东西,并让物理学只在初始随机插入后解决问题。

您可以尝试使用物理参数和停止条件,但要小心,因为数值不稳定很容易出现。

编辑:

  • 在代码段中添加控件以试验和理解物理设置( slider 的最小/最大值设置为“合理”边界)
  • 修复了多次点击中心按钮同时产生多个动画处理程序的错误(多次点击而不等待滚轮停止),
  • 整个事情现在也在一个自调用函数中,以避免用它的变量污染全局范围(从 html 中删除了 onclick="roulette_spin(this)",添加了 document. getElementsByClassName('roulette_center')[0].addEventListener('click', roulette_spin); 在 js 中将现在私有(private)的(范围内的)处理程序(roulette_spin)绑定(bind)到轮子中心)

编辑 2:

  • 添加了对点击切片的检测

  • 如果轮子旋转并且没有点击切片,它将根据初始速度和惯性停止,将末端捕捉到(随机)切片的中心。

  • 如果轮子旋转并点击一个切片,速度将改变,以便轮子应该停在非常靠近目标切片的地方,迫使吸引器到点击的切片将确保轮子实际上停在这个片。 @mathspeople 在这里:提议的公式远非准确,我很高兴听到修改后的停止距离公式来计算适当的速度修正。但是,嘿......它只是有效......

  • 如果滚轮没有旋转,单击一个切片将使滚轮动画到单击的切片。这是一个很好的特性,具有高摩擦力(低惯性),因为轮子会快速转动直到正确定位并很好地减速。它充当旋转菜单,这是 OP 可能首先想要的。

(我建议在全页模式下查看代码片段。注意:就目前而言,旋转期间不支持多次单击切片并且会失败,单击切片后,您需要等待轮子停止. 修复很简单,但即使它很有趣,我也花了很多时间在这个答案上,禁闭/宵禁有它的好处 :D)

(function() {
var values = [
"Financial", "Environmental", "Social", "Physical", "Intellectual", "Emotional", "Spititual"
];
var speed = 0;
// should init angle so that the cursor is at the center of the desired value,
// this is just to test the offset is right
var angle = getAngleFor("Intellectual");
// how much of the speed is kept at each update step (0 < i < 1, the bigger the longer the wheel spins)
var inertia = 0.988;
var minSpeed = 15;
// how much randomness in initial push speed
var randRange = 15;
// strongest absolute force that an attractor can exerce (is squashed to before scaling is applied)
var maxAttractionForce = 0.5;
// scale attraction force
var attractionForceFactor = 0.02;
var rouletteElem = document.getElementsByClassName('roulette_wheel')[0];
var scoreElem = document.getElementById('score');
// offset the display angle so that angle 0 is at the edge of the first slice
var offset = 26.42;
var currentAnimationFrame = null;
var clickedSliceIndex = -1;
var estimatedSliceIndex = -1;
var totalAngle = 0;

function addParamInput(name, value, min, max, step, handler) {
var id = name + '-input-id';
var wrapper = document.createElement('div');
wrapper.classList.add('single-input-wrapper');
var label = document.createElement('label');
label.setAttribute('for', id);
label.innerHTML = name;
var input = document.createElement('input');
input.setAttribute('type', 'range');
input.setAttribute('min', ''+min);
input.setAttribute('max', ''+max);
input.setAttribute('step', ''+step);
input.setAttribute('value', ''+value);
input.addEventListener('change', handler);
var meter = document.createElement('span');
meter.classList.add('input-meter');
meter.innerHTML = value;
wrapper.appendChild(label);
wrapper.appendChild(input);
wrapper.appendChild(meter);
document.getElementById('all-input-wrappers').appendChild(wrapper);
}

function updateInputMeter(input) {
input.parentElement.getElementsByClassName('input-meter')[0].innerHTML = input.value;
}

function roulette_spin(evt) {
evt.stopPropagation();
clickedSliceIndex = -1;
totalAngle = 0;
// erase score
scoreElem.innerHTML = '';
// set initial speed randomly
speed = Math.floor(Math.random() * randRange) + minSpeed;
// console.log('initial speed', speed);
// probably far from accurate but it kind of works, attractor
// completely ignored because when spinning fast it cost speed as much
// as it adds, and after it won't matter
// since we will be forcing the attractor to where we want to stop
var estimatedSpin = speed / (1 - inertia);
// console.log('estimated total spin approximation', estimatedSpin);
estimatedSliceIndex = getSliceIndex((angle + estimatedSpin) % 360);
// console.log('estimated stopping slice', values[estimatedSliceIndex]);
if (currentAnimationFrame) {
cancelAnimationFrame(currentAnimationFrame);
}
currentAnimationFrame = requestAnimationFrame(doAnimation);
}

function getSliceIndex(angle) {
return Math.floor(((angle / 360) * values.length));
}

function getCircularDist(a, b) {
// assuming both a and b are positive angles 0 <= angle <= 360
// find shortest oriented distance
// example: if a is 350 degrees and b is 10 degrees what's the actuals angular dist from a to b
// naive implementation would be b - a which gives - 340 => wrong, it only takes
// 20 degrees to rotate from 350 to 10 => b - (a - 360)
// conversely if a is 300 and b is 250 the shortest distance is
// the other way around => b - a = -50 degrees
var d1 = b - a;
var d2 = b - (a - 360);
return Math.abs(d1) >= Math.abs(d2) ? d2 : d1;
}

function setRouletteAngle(angle) {
rouletteElem.style.transform = 'rotate(' + (offset + angle) + 'deg)';
}

function getAngleFor(value) {
return getAngleForIndex(values.indexOf(value));
}

function getAngleForIndex(sliceIndex) {
return (sliceIndex + 0.5) * (360 / values.length);
}

function handleRouletteClick(evt) {
// coordinate of the center of the wheel
var centerX = rouletteElem.offsetWidth / 2;
var centerY = rouletteElem.offsetHeight / 2;
// console.log('centerX', centerX, 'centerY',centerY);
// roulette's parent bounding rect
var rect = evt.target.parentElement.getBoundingClientRect();
// coordinates of the click in roulette's parent coordinates
var clickX = evt.clientX - rect.x;
var clickY = evt.clientY - rect.y;
// resulting coordinates relative to the center of the wheel
var x = clickX - centerX;
var y = clickY - centerY;
// console.log('x', x, 'y', y);
var norm = Math.sqrt(x*x+y*y);
// -pi < rad < pi
var rad = Math.atan2(x, y);
var deg = (rad * 180) / Math.PI;
// 0 <= clickedAngle < 360 starting from the first slice
var clickedAngle = ((deg < 0 ? deg + 360 : deg) + angle + 180) % 360;
clickedSliceIndex = Math.floor((clickedAngle / 360) * values.length);
scoreElem.style.color = 'lightgrey';
scoreElem.innerHTML = 'clicked ' + values[clickedSliceIndex];
// outside of an animation started with the center button it will make the wheel spin to target
if (!currentAnimationFrame) {
// assume wheel is not mmoving so the estimated slice is the slice at the current angle
estimatedSliceIndex = getSliceIndex(angle);
currentAnimationFrame = requestAnimationFrame(doAnimation);
}
var estimatedAngle = getAngleForIndex(estimatedSliceIndex);
var targetFinalAngle = getAngleForIndex(clickedSliceIndex);
var spinError = getCircularDist(estimatedAngle, targetFinalAngle);
var speedCorrection = spinError * (1 - inertia);
speed += speedCorrection;

}

function doAnimation() {
// new angle is previous angle + speed modulo 360 (so that it stays between 0 and 360)
totalAngle += speed;
angle = (angle + speed) % 360;
// decay speed according to inertia parameter
speed = speed - (1 - inertia) * speed;
// add attractor force: inverse of circular oriented dist to the closest slice center * some factor
var sliceIndex = clickedSliceIndex !== -1 ? clickedSliceIndex : getSliceIndex(angle);

var target = getAngleForIndex(sliceIndex);
var orientedDist = getCircularDist(angle, target);

// protect against infinity (division by 0 or close to 0)
var inverseOrientedDistAmplitude = orientedDist === 0
? maxAttractionForce
: Math.min(1/Math.abs(orientedDist), maxAttractionForce);
var finalAttractionForce = Math.sign(orientedDist) * inverseOrientedDistAmplitude * attractionForceFactor;

speed = speed + finalAttractionForce;

setRouletteAngle(angle);
// stop animation if speed is low and close to attraction point
if (speed < 0.01 && Math.abs(orientedDist) < 0.05 ) {
// console.log('total spin', totalAngle);
scoreElem.style.color = 'black';
scoreElem.innerHTML = values[getSliceIndex(angle)];
currentAnimationFrame = null;
return;
}
currentAnimationFrame = requestAnimationFrame(doAnimation);
}

setRouletteAngle(angle);
document.getElementsByClassName('roulette_center')[0].addEventListener('click', roulette_spin);
rouletteElem.parentElement.addEventListener('click', handleRouletteClick);
addParamInput('minSpeed', 15, 0, 60, 0.5, function(evt) {
var rangeInput = evt.target;
minSpeed = parseFloat(rangeInput.value);
updateInputMeter(rangeInput)
});

addParamInput('inertia', 0.988, 0.900, 0.999, 0.001, function(evt) {
var rangeInput = evt.target;
inertia = parseFloat(rangeInput.value);
updateInputMeter(rangeInput)
});

addParamInput('attractionForceFactor', 0.02, 0, 0.1, 0.001, function(evt) {
var rangeInput = evt.target;
attractionForceFactor = parseFloat(rangeInput.value);
updateInputMeter(rangeInput)
});

addParamInput('maxAttractionForce', 0.5, 0, 1.5, 0.01, function(evt) {
var rangeInput = evt.target;
maxAttractionForce = parseFloat(rangeInput.value);
updateInputMeter(rangeInput)
});

})();
.roulette_center {
position: absolute;
top: 255px;
left: 258px;
cursor: pointer;
}

.roulette_center:hover {
transform: scale(1.01);
}

.roulette_wheel{
touch-action: auto;
pointer-events:painted
}

.single-input-wrapper {
display: flex;
align-items: center;
}

.single-input-wrapper > label {
width: 150px;
}

.single-input-wrapper > input {
width: 150px;
}

#score {
position: absolute;
top: 0px;
left: 360px;
font-weight: 700;
font-size: 24px;
}

#indicator {
position: absolute;
top: 30px;
left: 350px;
background-color: red;
width: 1px;
height: 60px;
}
#all-input-wrappers {
z-index: 10;
position: absolute;
top: 0px;
left: 0px;
opacity: 0.2;
background-color: white;
display: flex;
flex-direction: column;
transition: opacity 0.2s;
}

#all-input-wrappers:hover {
opacity: 0.8;
}
<html>
<head>
</head>
<body>
<div style="z-index: 0; position: relative">
<div id="all-input-wrappers"></div>
<span id="score">&nbsp;</span>
<img
class="roulette_wheel"
src="https://www.dropbox.com/s/6kp3fmtp72aj3vy/wellness-wheel.png?raw=1"
/>
<img
class="roulette_center"
src="https://www.dropbox.com/s/52x8iyb1nvkjm0w/wheelCenter.png?raw=1"
onfocus="transform()"
>
<div id="indicator"></div>
</div>
</body>
</html>

关于jquery - JS中的纺车,如何在随机选择的切片的中心自动停止旋转,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65959865/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com