2 回答
TA贡献1836条经验 获得超13个赞
有两个问题。
下图显示了状态wheelAngle = 0
:
在您的代码中,项目 0 具有startAngle = 0和endAngle = some positive value。这与您所看到的不符。实际上,项目 0 应该以 0 为中心。所以你需要将你的区域偏移项目角度宽度的一半:
var rotateAngle = angle * i;
var transform = `rotateX(${ rotateAngle }deg) translateZ(${ radius }px)`;
var startAngle = rotateAngle - angle / 2
var endAngle = rotateAngle + angle / 2;
第二个问题是您的规范化功能。您采用绝对值,因此会丢失任何方向信息。这是该功能的更好版本:
function normaliseAngle( angle ) {
angle = -angle;
return angle - 360 * Math.floor(angle / 360);
}
TA贡献1807条经验 获得超9个赞
主要问题是开始/结束角度。我更新了如下逻辑:
$segment.attr('data-start-angle', -startAngle + angle / 2);
$segment.attr('data-end-angle', -endAngle + angle / 2);
并且
function normaliseAngle(angle) {
angle = angle % 360;
if (angle > 0)
angle = angle - 360;
return angle;
}
负旋转将向您显示从第一个元素开始的元素(而不是正旋转)。您还需要考虑偏移量,angle / 2因为 startAngle 会将您置于元素的中间。然后你应该在逻辑上将你的角度归一化为负值。
完整代码
(function($) {
// Settings
const items = 28; // Segments on wheel
const spinSpeed = randNumber(1, 10); // Spin speed multiplier
const spinDuration = randNumber(2, 5); // In seconds
const spinDirection = randNumber(0, 1) ? 'up' : 'down'; // Animate up or down
// Vars
const $wheel = $('.wheel .wheel__inner');
const diameter = $wheel.height();
const radius = diameter / 2;
const angle = 360 / items;
const circumference = Math.PI * diameter;
const height = circumference / items;
// Trackers
let wheelAngle = 0;
const wheelStarted = new Date();
// Add segments to the wheel
for (let i = 0; i < items; i++) {
var startAngle = angle * i;
var endAngle = angle * (i + 1);
var transform = `rotateX(${ startAngle }deg) translateZ(${ radius }px)`;
var $segment = $('<div>', {
class: 'wheel__segment',
html: `<span>Item ${ i }</span>`
}).css({
'transform': transform,
'height': height,
});
// Add start and end angles for this segment
$segment.attr('data-start-angle', -startAngle + angle / 2);
$segment.attr('data-end-angle', -endAngle + angle / 2);
$segment.appendTo($wheel);
}
/**
* Print debug info to DOM
*
* @param {object}
*/
function logInfo(data) {
const $log = $('textarea#log');
let logString = '';
logString += '-----' + "\n";
for (var key in data) {
logString += `${ key }: ${ data[ key ] }` + "\n";
}
logString += "\n";
// Prepend log to last value
logString += $log.val();
// Update field value
$log.val(logString);
}
/**
* Get random number between min & max (inclusive)
*
* @param {number} min
* @param {number} max
* @returns {number}
*/
function randNumber(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
/**
* Limit angles to 0 - 360
*
* @param {number}
* @returns {number}
*/
function normaliseAngle(angle) {
angle = angle % 360;
if (angle > 0)
angle = angle - 360;
return angle;
}
/**
* Get the wheel segment at a specific angle
*
* @param {number} angle
* @returns {jQuery}
*/
function segmentAtAngle(angle) {
angle = normaliseAngle(angle);
const $found = $wheel.find('.wheel__segment').filter(function() {
const startAngle = parseFloat($(this).data('start-angle'));
const endAngle = parseFloat($(this).data('end-angle'));
return angle >= endAngle && angle < startAngle;
});
return $found;
}
/**
* @var {integer} Unique ID of requestAnimationFrame callback
*/
var animationId = window.requestAnimationFrame(function tick() {
// Time passed since wheel started spinning (in seconds)
const timePassed = (new Date() - wheelStarted) / 1000;
// Speed modifier value (can't be zero)
let speedModifier = parseInt(spinSpeed) || 1;
// Decelerate animation if we're over the animation duration
if (timePassed > spinDuration) {
const decelTicks = (spinDuration - 1) * 60;
const deceleration = Math.exp(Math.log(0.0001 / speedModifier) / decelTicks);
const decelRate = (1 - ((timePassed - spinDuration) / 10)) * deceleration;
speedModifier = speedModifier * decelRate;
// Stop animation from going in reverse
if (speedModifier < 0) {
speedModifier = 0;
}
}
// Print debug info
logInfo({
timePassed: timePassed,
speedModifier: speedModifier,
wheelAngle: wheelAngle,
normalisedAngle: normaliseAngle(wheelAngle)
});
// Wheel not moving, animation must have finished
if (speedModifier <= 0) {
window.cancelAnimationFrame(animationId);
const $stopped = segmentAtAngle(wheelAngle);
alert($stopped.text());
return;
}
// Increase wheel angle for animating upwards
if (spinDirection === 'up') {
wheelAngle += speedModifier;
}
// Decrease wheel angle for animating downwards
else {
wheelAngle -= speedModifier;
}
// CSS transform value
const transform = `rotateX(${wheelAngle}deg) scale3d(0.875, 0.875, 0.875)`;
$wheel.css({
'-webkit-transform': transform,
'-moz-transform': transform,
'-ms-transform': transform,
'-o-transform': transform,
'transform': transform,
'transform-origin': `50% calc(50% + ${height/2}px)`,
'margin-top': `-${height}px`
});
// New tick
animationId = window.requestAnimationFrame(tick);
});
})(jQuery);
*,
*:before,
*:after {
box-sizing: border-box;
}
.app {
display: flex;
flex-direction: row;
padding: 15px;
}
textarea#log {
width: 300px;
}
.wheel {
perspective: 1000px;
border: 1px solid #333;
margin: 0 25px;
flex-grow: 1;
}
.wheel:after {
content: '';
display: block;
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 2px;
background-color: red;
transform: translateY(-50%);
}
.wheel .wheel__inner {
position: relative;
width: 200px;
height: 350px;
margin: 0 auto;
transform-style: preserve-3d;
}
.wheel .wheel__inner .wheel__segment {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 40px;
position: absolute;
top: 50%;
background-color: #ccc;
}
.wheel .wheel__inner .wheel__segment:nth-child(even) {
background-color: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="app">
<textarea id="log"></textarea>
<div class="wheel">
<div class="wheel__inner">
</div>
</div>
</div>
添加回答
举报