移动端触摸数字转盘无限滚动实现思路

先看下效果,这个转盘的数字是从0-105,可以设置起始数字,那么之前的数字就变灰,不可选取,点击 加减号,会自动转动4个数字间隔,同时也可以手动触摸拖动,拖到终端105,自动回弹不可再拖动下去,同时选中的数字自动变黄···

好啦看下gif实现效果,或者点击查看,请滑动到第三屏

 

难点分析:

1、因为一个圆是360度,数字按照一定间隔排列比较合理的是18度排一个数字,那么一个圆形只能排列20个数字,其他数字如何生成?

2、如何判断当前选中并且加黄?

3、手指触摸拖动和圆盘转动的换算关系?

4、转动如何更加平滑有一定弹性

5、如何控制相应的数字显示隐藏来达到转盘数字一直增加或者减少的效果?

思路分析:

1、对于第一个问题开始的思路是,当转动时,即将有数字要显示出来,到达尖头指向的位置

那么就先获取它的前一个数字,再去+1,同样的相反方向转动就-1,后来发现这样做很烦躁,很多零界点不好控制,而且有时候反复拖动转盘还会失效,根据以往经验,我判断这样做风险很大,路子没找对,其他方案也想了一些,后来最终确定了一个方案,就是再生成dom的时候直接把所有数字生成好,比如要生成1-105个数字,那么1个数字就转动18度的话,那一共要转动105*18度,然后再拖动圆盘转动的时候,可以通过圆盘转动的角度对应来需要显示的数字,其他数字隐藏

来看生成代码

var deg=18;
var _age=0;
var firstAge=_age;
for (var i=0;i<1908/deg;i++){
    if(i>=20){
//数字是从0开始生成,把大于19的数字先隐藏掉,只显示一圈的数字
//并且把每10个数字编成一组,因为圆盘是每次只能看到10个数字,ageGroup1···
        $("#circleBody .con").append('<div class="hide age age'+i+' ageGroup'+parseInt(i/10)+'" style=" transform: rotate('+(deg*i-90)+'deg); -webkit-transform: rotate('+(deg*i-90)+'deg);" data-deg='+(deg*i-90)+'>'+(i+firstAge)+'</div>')
    }else {
        $("#circleBody .con").append('<div class="age age'+i+' ageGroup'+parseInt(i/10)+'" style=" transform: rotate('+(deg*i-90)+'deg); -webkit-transform: rotate('+(deg*i-90)+'deg);" data-deg='+(deg*i-90)+'>'+(i+firstAge)+'</div>')
    }
}

2、第二个问题,如何判断当前选中的数字变黄

这个问题很简单啦,圆盘转动的角度和每一个数字对应转动的角度是有规律的,对应的换算就可以了,注意一下,我们拖动圆盘转动,只控制这一个dom转动,数字是生成在圆盘这个dom里面的,相对于圆盘角度是不变的,也减小了项目的难度

function currentCss(deg){
    var currentDom;
    if(deg<=0){
        currentDom=(Math.abs(Math.ceil(deg))+90)/18;
    }else {
        currentDom=(90-Math.abs(Math.ceil(deg)))/18;
    }
    $(".age"+currentDom).addClass("current").siblings().removeClass("current");
};

3、那么就引出来下一个问题,我手指拖动的轨迹是不定的,我怎么去对应圆盘转动的角度呢?

那就是求触摸的起始点 和触摸结束点和圆心形成的夹角

function rotateAngle (ostart,oend){
    var start={
        x:ostart.x-center.x,
        y:center.y-ostart.y
    };
    var end={
        x:oend.x-center.x,
        y:center.y-oend.y
    };
    //求夹角cos值
    var ab=(start.x*end.x+start.y*end.y)/(Math.sqrt(start.x*start.x+start.y*start.y)*Math.sqrt(end.x*end.x+end.y*end.y));
    if(end.x-start.x>0){
        //当正向拨动 通过反切cos求的角度
        return 360*Math.acos(ab)/(Math.PI*2);
    }else {
        return -360*Math.acos(ab)/(Math.PI*2);
    }
    //返回角度,不是弧度
}

4、对于转动的效果增加弹性,开始是考虑用requestanimationframe ,但是使用后发现在安卓机上兼容性不太好,后来灵机一动,想到了css3的transtion方法,简单,兼容性又好,效果又好,兼职bug一般的存在啊,我只需要定义好需要转动的角度,转动效果完全交给他即可

$("#circleBody .con").css({
    "transform":"rotate("+(b+c)+"deg)",
    "webkitTransform":"rotate("+(b+c)+"deg)",
    "webkitTransition":'all .5s ease-out',
    "transition":"all .5s ease-out"
})

使用方法解决了,下一步就要来计算最复杂的一步,就是我要动画的变化量,考虑下,当我触摸从触摸开始到触摸结束,圆盘转动的角度只会根据我这2个点来实时的转动,我手指离开了,那么转动也就结束了,动画会很生硬,而且停止转动的位置也不会准确的停到我选中数字的角度,所以我要计算一下当前停止的角度离要选中的数字的角度哪个角度近,就在我手指离开的时候自动转动到临近的数字,同时使用css3动画效果,这样就会很圆滑了转动效果,那么计算逻辑是:

1)如果正向转动

如果到达了转动的终点,那么手指离开后就让圆盘还自动转回到终点数字的角度

如果转动的是非终点的数字,那么就计算下当前转动的角度离临近的哪个数字角度近,就自动转过去

2)  如果反向转动

如果到达了转动的起点,那么手指离开后就让圆盘还自动转回到起点数字的角度

如果转动的是非终点的数字,那么就计算下当前转动的角度离临近的哪个数字角度近,就自动转过去

看下代码:

c=obj.moveAngle<-currentAgeNum?
    obj.moveAngle>-(maximumAge*18-18-90)?
    (18-Math.abs(obj.moveAngle)%18)*direction:-(maximumAge*18-90)-obj.moveAngle
    :-currentAgeNum-obj.moveAngle
//obj.moveAngle为圆盘转动角度
//currentAgeNum 为当前的选中的角度

那么点击加减号类似,看下代码理解下:

c=obj.endAngle+(18*4)*direction<-currentAgeNum?
    obj.endAngle+(18*4)*direction<-(maximumAge*18-18-90)?-(maximumAge*18-90)-obj.endAngle:(18*4)*direction
    :-currentAgeNum-obj.endAngle

5、最后就是最关键的问题啦,我在触摸转动过程中如何控制数字的显示与隐藏,那么就涉及到很重要的2个变量,一个是转盘当前的角度,一个是转盘要转动到的角度,这一块的逻辑开始想简单了,导致后来坑不断,现在把这个转动的过程原本的来复盘

一、手指触摸转动

先来看下手指触摸转动如何控制,点击加减号与手指触摸控制还不一样,因为手指触摸转动的过程角度的增量是渐进的,这样就可以实时的监测到应该显示哪个一个分组的数字,看下代码

function showAge(deg){
    var abs_moveAngle=parseInt(Math.abs(deg)/180);
    $(".age").hide();//先将所有的隐藏
    if(deg<0){
        $(".ageGroup"+(abs_moveAngle+1)).show();
    }
    $(".ageGroup"+abs_moveAngle).show();
}

二、点击加减号转动,这个情况下不能简单当依靠隐藏数字组来显示隐藏了,因为转动的起始位置不定,不一定是一个数字组内的哪一个位置,所以要控制到具体显示哪些数字

function showAgeClick(deg,direction,c){
    var abs_moveAngle=parseInt(Math.abs(deg-90)/180),//当前的组号
        remainder=(Math.abs(deg-90)%180)/18,//在当前组中的下标
        g1=$(".ageGroup"+(abs_moveAngle-1)),
        g2=$(".ageGroup"+(abs_moveAngle+1));
    $(".age").hide();
    $(".ageGroup"+abs_moveAngle).show();
    if(direction==-1){
        //正向转动
        if(remainder<4){
            for(var i=0;i<8-remainder*2;i++){
                $(g1[9-i]).show();
                $(g2[i]).show()
            }
        }else {
            g2.show();
        }
    }else {
        //反向转动
        if(remainder<6){
            g1.show();
        }else {
            //当当前组下标大于6,大于当前组g2的需要显示remainder-5个,小于当前组的g1需要显示remainder-4,且倒着显示,所以,条件相加为:2*remainder-9
            for(var i=0;i<2*remainder-9;i++){
                $(g2[i]).show();
                $(g1[9-i]).show();
            }
        }
    }
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注