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

如何使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 05:45:03