如何使用JavaScript拖动thooClock模拟时钟的时针与分针?
实现thooClock时钟时针分针拖动功能的方案
canvas本身是位图结构,无法直接选中内部绘制的指针元素,核心实现逻辑是通过坐标计算判断点击/拖动位置是否落在时针/分针的有效范围内,再绑定拖动事件完成时间更新,具体实现步骤如下:
第一步:获取时钟基础参数
首先从你初始化的thooClock实例中读取核心配置,包括:
- 时钟画布的宽高,计算中心点坐标(画布宽高除以2即可)
- 时针、分针的长度和宽度,要和你初始化时传入的配置完全一致
- 实例当前存储的时间属性
currentTime,用于计算指针当前角度
角度换算公式:
- 分针角度:分钟数 * 6(360度均分60分钟)
- 时针角度:小时数 * 30 + 分钟数 * 0.5(360度均分12小时,叠加分钟带来的偏移量)
- 注意canvas默认0度为正右方向,计算时需要减90度偏移,让0度对应12点方向,转弧度需要乘以
Math.PI/180
第二步:实现指针选中判断逻辑
通过计算点击坐标到指针线段的距离,判断是否选中对应指针,首先封装点到线段的距离计算工具函数:
// 计算点(px,py)到线段(x1,y1)-(x2,y2)的距离 function pointToLineDistance(px, py, x1, y1, x2, y2) { const A = px - x1; const B = py - y1; const C = x2 - x1; const D = y2 - y1; const dot = A * C + B * D; const len_sq = C * C + D * D; const param = len_sq !== 0 ? dot / len_sq : -1; let xx, yy; if (param < 0) { xx = x1; yy = y1; } else if (param > 1) { xx = x2; yy = y2; } else { xx = x1 + param * C; yy = y1 + param * D; } const dx = px - xx; const dy = py - yy; return Math.sqrt(dx * dx + dy * dy); }
在点击/按下事件中,计算当前指针的两个端点坐标,再判断点击位置是否在指针宽度范围内:
const canvas = $('#canvas')[0]; const rect = canvas.getBoundingClientRect(); // 时钟中心点坐标 const centerX = canvas.width / 2; const centerY = canvas.height / 2; // 点击坐标换算为画布内相对坐标 const clickX = e.clientX - rect.left; const clickY = e.clientY - rect.top; // 此处替换为你初始化时配置的时针、分针参数 const hourLength = 30, hourWidth = 8; const minuteLength = 50, minuteWidth = 4; // 读取当前时间 const now = clock.currentTime; const hour = now.getHours() % 12; const minute = now.getMinutes(); // 计算指针弧度 const hourRad = (hour * 30 + minute * 0.5 - 90) * Math.PI / 180; const minuteRad = (minute * 6 - 90) * Math.PI / 180; // 计算指针端点坐标 const hourEndX = centerX + Math.cos(hourRad) * hourLength; const hourEndY = centerY + Math.sin(hourRad) * hourLength; const minuteEndX = centerX + Math.cos(minuteRad) * minuteLength; const minuteEndY = centerY + Math.sin(minuteRad) * minuteLength; // 距离小于指针宽度一半即判定选中 const isHourSelected = pointToLineDistance(clickX, clickY, centerX, centerY, hourEndX, hourEndY) < hourWidth / 2; const isMinuteSelected = pointToLineDistance(clickX, clickY, centerX, centerY, minuteEndX, minuteEndY) < minuteWidth / 2;
第三步:绑定完整拖动逻辑
不要只使用click事件,用mousedown、mousemove、mouseup三个事件组合实现拖动效果:
let selectedPointer = null; // 选中的指针标记,可选值 'hour'/'minute'/null // 初始化thooClock实例,保留实例引用 const clock = $('#canvas').thooClock({ // 你原有的初始化配置 }); const canvas = $('#canvas')[0]; const rect = canvas.getBoundingClientRect(); const centerX = canvas.width / 2, centerY = canvas.height / 2; // 替换为你自己的指针参数 const hourLength = 30, hourWidth = 8; const minuteLength = 50, minuteWidth = 4; // 按下事件判断选中指针 $('#canvas').mousedown(function(e){ const clickX = e.clientX - rect.left; const clickY = e.clientY - rect.top; const now = clock.currentTime; const hour = now.getHours()%12; const minute = now.getMinutes(); const hourRad = (hour*30 + minute*0.5 -90) * Math.PI/180; const minuteRad = (minute*6 -90) * Math.PI/180; const hourEndX = centerX + Math.cos(hourRad)*hourLength; const hourEndY = centerY + Math.sin(hourRad)*hourLength; const minuteEndX = centerX + Math.cos(minuteRad)*minuteLength; const minuteEndY = centerY + Math.sin(minuteRad)*minuteLength; const hourDist = pointToLineDistance(clickX, clickY, centerX, centerY, hourEndX, hourEndY); const minuteDist = pointToLineDistance(clickX, clickY, centerX, centerY, minuteEndX, minuteEndY); if(hourDist < hourWidth/2){ selectedPointer = 'hour'; }else if(minuteDist < minuteWidth/2){ selectedPointer = 'minute'; } }); // 全局移动事件,实时更新时钟 $(document).mousemove(function(e){ if(!selectedPointer) return; const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; // 计算鼠标位置和中心点的夹角 const rad = Math.atan2(mouseY - centerY, mouseX - centerX); // 角度修正12点偏移,负角度转正 const angle = (rad * 180 / Math.PI + 90 + 360) % 360; const now = clock.currentTime; if(selectedPointer === 'minute'){ // 分针每6度对应1分钟,取整 const minute = Math.round(angle /6) %60; now.setMinutes(minute); }else{ // 时针每30度对应1小时,取整 const hour = Math.round(angle /30) %12; now.setHours(hour); // 如果需要时针拖动时分钟联动,可以直接计算总分钟数:const totalMin = Math.round(angle * 2) % 720; 再拆分小时和分钟 } // 调用thooClock内置方法更新时间 clock.setTime(now); }); // 抬起事件清除选中标记 $(document).mouseup(function(){ selectedPointer = null; });
适配说明
- 如果需要支持移动端,把
mousedown/mousemove/mouseup事件替换为对应的touchstart/touchmove/touchend事件,坐标取触摸点对象的clientX/clientY即可 - 所有指针参数必须和初始化thooClock时的参数保持一致,否则会出现选中范围偏移的问题
内容的提问来源于stack exchange,提问作者Durgesh
相关产品推荐
相关产品推荐

