You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DJ黑胶混音器:拖拽定位音频时间与旋转同步问题求助

DJ黑胶混音器拖拽时间计算问题

我正在开发一款DJ黑胶混音器,目标实现拖拽黑胶唱片跳转到指定音频时间点并继续播放的功能。黑胶是宽高325px的圆形,周长1099。目前已实现黑胶拖拽操作,能获取audio元素的currentTime,但不知道怎么通过拖拽操作计算对应的音频播放时间(比如向上拖拽倒带,结束后从对应时间继续播放)。

当前TouchMove事件代码

$(document).on('touchmove click', '#round-play1', function(e){
    detectTap = false; // 排除触摸事件中的滚动事件
    console.log("ddddd");
    detectTapMove = true;

    if (!isStart) return;
    e.preventDefault();
    ang = angXY(e, this) - angStart;
    this.style.transform = `rotateZ(${ang}rad)`;

    console.log("ang1111 " + ang);

    myAudio=document.getElementById('audio2');
    console.log(myAudio.currentTime);

    var x = e.touches[0].clientX;
    var y = e.touches[0].clientY;

    if (y < mY) {
        console.log('Up');

        if(myAudio.currentTime <= 0){
            // myAudio.currentTime = duration - (mY - y);
        }else{

        }

        var ff = 360 / myAudio.duration;
        var dd = (mY - y);

        myAudio.currentTime -= dd;
        console.log("dcurrentTime " + myAudio.currentTime);
        console.log("dduration " + dd);

    } else if (y > mY) {
        console.log('Down');
        myAudio.currentTime += y - mY;
    } else {
        console.log('Moving left-right or the opposite');
    }

    // 测试完成后设置新的mY
    mY = y;

    //r = getCurrentRotation($(this)[0]);
    r = getCurrentRotation($(this)[0]);
});

黑胶自动旋转代码

setInterval(() => {
    if(!detectTapMove){
        if(detectTap == true && rr != undefined){
            r = rr;
            $("#round-play1")[0].style.transform = `rotate(${r}deg)`;
            rr = NaN;
            detectTap = false;
        }else{
            r = r + 1;
            if(r >= 360){
                r = 0;
            }
            console.log("dcurrentTime " + myAudio.currentTime);
            $("#round-play1")[0].style.transform = `rotate(${r}deg)`;
        }
    }
}, 100);

请问针对时长251秒的音频,怎么计算拖拽黑胶到指定位置后的准确播放时间?


更新:拖拽后再次操作黑胶复位问题

已集成解答方案,但仍有问题:拖拽黑胶时操作正常,但停止拖拽后再次拖拽,黑胶会回到初始位置。以下是当前代码,请帮忙排查:

$(document).ready(function(){
    $("body").height($(document).height());
    $("#services").height($(document).height());
    $(".container").height($(document).height());
    $(".row").height($(document).height());

    $("#audioplay1").click(function() {
        myAudio=document.getElementById('audio2');
        duration = myAudio.duration;
        myAudio.currentTime = 0;
        myAudio.loop=true;
        myAudio.play();
        detectTap = false;
        detectTapMove = false;
    })
})

let last_Dot_Product;

$(document).on('touchstart click', '#round-play1', function(e){
    var x = e.touches[0].clientX;
    var y = e.touches[0].clientY;
    
    A_x = x;
    A_y = y;
});

$(document).on('touchmove click', '#round-play1', function(e){
    var x = e.touches[0].clientX;
    var y = e.touches[0].clientY;

    B_x = x;
    B_y = y;

    AB_x = B_x - A_x;
    AB_y = B_y - A_y;

    C_x = 162.5;
    C_y = 162.5;

    AC_x = C_x - A_x;
    AC_y = C_y - A_y;

    var length = Math.sqrt(AC_x * AC_x + AC_y * AC_y);
    AC_x = AC_x/length;
    AC_y = AC_y/length;

    var tmp = AC_x;
    AC_x = AC_y;
    AC_y = -tmp;

    var dot_product = AB_x * AC_x + AB_y * AC_y;

    $("#round-play1")[0].style.transform = `rotate(${dot_product}deg)`;
    if(dot_product > 0){
        console.log(dot_product)
        console.log("UP")
    }else{
        console.log(dot_product)
        console.log("DOWN")
    }
});

$(document).on('touchend click', '#round-play1', function(e){
    var x = e.changedTouches[0].pageX;
    var y = e.changedTouches[0].pageY;
});

解答

一、初始拖拽时间计算问题解决方案

核心思路是将黑胶的旋转角度与音频时长绑定,而非用拖拽的像素差直接加减时间(之前的y-mY方式误差极大):

  1. 确定旋转角度与时间的映射关系

    • 黑胶旋转一圈(360°)对应音频完整时长(251秒),因此每度对应的时间 = 总时长 / 360,即 timePerDegree = 251 / 360 ≈ 0.697秒/度
    • 反之,每秒对应的旋转角度 = 360 / 总时长,即 degreePerSecond = 360 / 251 ≈ 1.434度/秒
  2. 拖拽时计算角度增量,同步更新音频时间

    • 在touchstart时记录初始角度startAngle和初始音频时间startTime
    • touchmove时计算当前旋转角度currentAngle,角度增量deltaAngle = currentAngle - startAngle
    • 对应的时间增量deltaTime = deltaAngle * timePerDegree
    • 最终音频时间:myAudio.currentTime = startTime + deltaTime
    • 注意边界处理:currentTime不能小于0,不能超过音频总时长duration
  3. 修正原代码的角度计算
    原代码中angXY函数计算的是绝对角度,拖拽时应基于初始角度的增量来计算,而非直接赋值。另外,要统一角度单位(用deg或rad,避免混用)。

二、拖拽复位问题排查与修复

当前代码的核心问题是每次touchstart时没有记录黑胶的当前旋转角度,且touchmove时的旋转计算基于触摸起点而非黑胶的当前状态:

  1. 问题根源

    • touchstart只记录了触摸点的坐标A_x/A_y,但没有保存黑胶当前的旋转角度
    • touchmove中计算的dot_product是基于触摸起点的相对值,而非叠加在黑胶已有旋转角度上
  2. 修复步骤

    • 新增变量存储黑胶的累计旋转角度,比如totalRotation = 0
    • touchstart时:
      • 记录触摸点坐标startX/startY
      • 计算触摸点相对于黑胶中心(162.5,162.5)的初始角度startTouchAngle,用于后续计算拖拽的角度增量
    • touchmove时:
      • 计算当前触摸点相对于中心的角度currentTouchAngle
      • 角度增量deltaRotation = currentTouchAngle - startTouchAngle(处理跨0度的边界情况)
      • 更新累计旋转角度:totalRotation += deltaRotation
      • 应用旋转:$("#round-play1")[0].style.transform = rotate(${totalRotation}deg)``
      • 同步更新音频时间:基于totalRotation取模360后映射到0~duration区间
    • touchend时更新startTouchAngle为当前触摸角度,确保下次拖拽基于当前状态继续

修正后的关键代码片段:

let totalRotation = 0;
let startTouchAngle = 0;
let myAudio;

// 计算点相对于中心的角度(返回0~360度)
function getAngleFromCenter(x, y, centerX, centerY) {
    const dx = x - centerX;
    const dy = y - centerY;
    let angle = Math.atan2(dy, dx) * (180 / Math.PI);
    return angle < 0 ? angle + 360 : angle;
}

$(document).ready(function(){
    // ...原有初始化代码...
    myAudio = document.getElementById('audio2');
})

$(document).on('touchstart click', '#round-play1', function(e){
    const centerX = 162.5;
    const centerY = 162.5;
    const x = e.touches[0].clientX;
    const y = e.touches[0].clientY;
    // 记录当前触摸点的初始角度
    startTouchAngle = getAngleFromCenter(x, y, centerX, centerY);
});

$(document).on('touchmove click', '#round-play1', function(e){
    e.preventDefault();
    const centerX = 162.5;
    const centerY = 162.5;
    const x = e.touches[0].clientX;
    const y = e.touches[0].clientY;
    
    const currentTouchAngle = getAngleFromCenter(x, y, centerX, centerY);
    // 处理跨0度的角度增量计算
    let deltaRotation = currentTouchAngle - startTouchAngle;
    if (deltaRotation > 180) deltaRotation -= 360;
    if (deltaRotation < -180) deltaRotation += 360;
    
    // 更新累计旋转角度
    totalRotation += deltaRotation;
    // 应用旋转
    $("#round-play1")[0].style.transform = `rotate(${totalRotation}deg)`;
    
    // 同步音频时间
    if (!myAudio.duration) return;
    const timePerDegree = myAudio.duration / 360;
    // 取模360,确保循环对应音频时长
    let normalizedRotation = totalRotation % 360;
    if (normalizedRotation < 0) normalizedRotation += 360;
    myAudio.currentTime = normalizedRotation * timePerDegree;
    
    // 更新startTouchAngle,用于下一次move计算
    startTouchAngle = currentTouchAngle;
});
  1. 额外优化
    • 移除setInterval中的硬编码旋转增量,改为基于音频currentTime实时计算黑胶旋转角度,确保音频与黑胶状态完全同步:
      // 替换原setInterval代码
      function syncVinylRotation() {
          if (!detectTapMove && myAudio && myAudio.duration) {
              const degreePerSecond = 360 / myAudio.duration;
              totalRotation = myAudio.currentTime * degreePerSecond;
              $("#round-play1")[0].style.transform = `rotate(${totalRotation}deg)`;
          }
          requestAnimationFrame(syncVinylRotation);
      }
      // 在音频播放时启动同步
      $("#audioplay1").click(function() {
          // ...原有代码...
          syncVinylRotation();
      })
      

内容的提问来源于stack exchange,提问作者redoc01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 03:07:03