使用 javascript 根据旋转角度在 3D 圆柱体/轮子上查找段

我有一个 3D 轮子,我正在使用 javascriptrequestAnimationFrame()函数制作动画。

轮子看起来像:

http://img.mukewang.com/63f864f100019d8e06570495.jpg

有 4 个主要变量需要考虑:

  1. items轮子上的段数。

  2. spinSpeed旋转速度修改器。将每帧的角度增加/减少乘以该值。

  3. spinDuration减速停止前全速旋转动画的长度。

  4. spinDirection轮子应该旋转的方向。接受updown

现在我想使用轮子停止的角度从 DOM 中获取线段(红线相交的地方)。轮段的弧起点和终点角度存储在数据属性中。例如:

<div class="wheel__inner">

    <div class="wheel_segment" ... data-start-angle="0" data-end-angle="12.85">Item 1</div>

    <div class="wheel_segment" ... data-start-angle="12.85" data-end-angle="25.71">Item 2</div>

    <div class="wheel_segment" ... data-start-angle="25.71" data-end-angle="38.58">Item 3</div>

    ...

</div>

我通过在每个刻度上存储修改后的角度来跟踪当前的车轮旋转。例如:


let wheelAngle = 0;


window.requestAnimationFrame( function tick() {


    if ( spinDirection === 'up' ) {

        wheelAngle += speedModifier;

    } else {

        wheelAngle -= speedModifier;

    }


   window.requestAnimationFrame( tick );

} );

当动画停止时,我尝试通过使用开始和结束角度对旋转和过滤片段进行归一化来获取片段。


我将旋转归一化,因为它可以在上方360°和下方移动0°,我使用以下函数执行此操作:


function normaliseAngle( angle ) {

    angle = Math.abs( angle ) % 360;

    angle = 360 - angle; // Invert

    return angle;

}

并像这样使用 jQuery 过滤元素:


const $found = $wheel.find( '.wheel__segment' ).filter( function() {

    const startAngle = parseFloat( $( this ).data( 'start-angle' ) );

    const endAngle = parseFloat( $( this ).data( 'end-angle' ) );

    return angle >= startAngle && angle < endAngle;

} );

然而,尽管我尽了最大的努力,我还是无法让它发挥作用。请在此处查看我的 JSFiddle:https ://jsfiddle.net/thelevicole/ps04fnxm/2/


波斯汪
浏览 70回答 2
2回答

开心每一天1111

有两个问题。下图显示了状态wheelAngle = 0:在您的代码中,项目 0 具有startAngle = 0和endAngle = some positive value。这与您所看到的不符。实际上,项目 0 应该以 0 为中心。所以你需要将你的区域偏移项目角度宽度的一半:var rotateAngle = angle * i;&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;var transform = `rotateX(${ rotateAngle }deg) translateZ(${ radius }px)`;var startAngle = rotateAngle - angle / 2var endAngle = rotateAngle + angle / 2;第二个问题是您的规范化功能。您采用绝对值,因此会丢失任何方向信息。这是该功能的更好版本:function normaliseAngle( angle ) {&nbsp; &nbsp; angle = -angle;&nbsp; &nbsp; return angle - 360 * Math.floor(angle / 360);}

函数式编程

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

相关分类

JavaScript