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方式误差极大):
确定旋转角度与时间的映射关系
- 黑胶旋转一圈(360°)对应音频完整时长(251秒),因此每度对应的时间 = 总时长 / 360,即
timePerDegree = 251 / 360 ≈ 0.697秒/度 - 反之,每秒对应的旋转角度 = 360 / 总时长,即
degreePerSecond = 360 / 251 ≈ 1.434度/秒
- 黑胶旋转一圈(360°)对应音频完整时长(251秒),因此每度对应的时间 = 总时长 / 360,即
拖拽时计算角度增量,同步更新音频时间
- 在
touchstart时记录初始角度startAngle和初始音频时间startTime touchmove时计算当前旋转角度currentAngle,角度增量deltaAngle = currentAngle - startAngle- 对应的时间增量
deltaTime = deltaAngle * timePerDegree - 最终音频时间:
myAudio.currentTime = startTime + deltaTime - 注意边界处理:
currentTime不能小于0,不能超过音频总时长duration
- 在
修正原代码的角度计算
原代码中angXY函数计算的是绝对角度,拖拽时应基于初始角度的增量来计算,而非直接赋值。另外,要统一角度单位(用deg或rad,避免混用)。
二、拖拽复位问题排查与修复
当前代码的核心问题是每次touchstart时没有记录黑胶的当前旋转角度,且touchmove时的旋转计算基于触摸起点而非黑胶的当前状态:
问题根源
touchstart只记录了触摸点的坐标A_x/A_y,但没有保存黑胶当前的旋转角度touchmove中计算的dot_product是基于触摸起点的相对值,而非叠加在黑胶已有旋转角度上
修复步骤
- 新增变量存储黑胶的累计旋转角度,比如
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; });
- 额外优化
- 移除
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
相关产品推荐
相关产品推荐

