前言我前几天看过《阿丽塔》在感叹炫酷特效的同时,不得不说这部片子热血沸腾!重燃了我对屎桶的热情!!有那么一会儿,我觉得我又回来了……好吧,让我们进入正题。一德医生刚捡到阿丽塔的那段,有没有发现医生家里的设备很有意思~比如人皮缝纫机,非常灵活。层次堪比蜘蛛织网。说到蜘蛛,我就想到了西游记里的蜘蛛精。今年下半年…………………………除了人皮缝纫机,我还注意到他们屏幕上的一个交互很有趣——是的,今天的主角正在做一个类似的旋转和缩放控制和JS一起看看最终效果吧,当当网:好像感觉不错?您可以点此先行体验(此版本仅供PC玩家使用)。本文先开发一个demo试水。暂时用鼠标代替手指,实现思路如下:画一个彩色圆圈,监听鼠标移动事件(保证只作用于圆圈上)计算鼠标当前角度(相对于圆心))实时比较当前角度和上一个角度,确定每一帧的旋转方向和距离,根据变化的角度值旋转圆环,对外部dom1进行缩放和操作。在这里画一个圆环大圆圈套小圆圈形成圆环:大圆圈设置渐变背景色,小圆圈设置纯白色。为什么不直接使用一个圆圈,然后设置边框属性呢?因为不喜欢,因为边框不能设置渐变色,当然还有一个功能:防止事件(详见下文)。万事俱备,目标是把圆环放在左上角绝对定位,内圆要大小适中所以分别设置圆心和大小圆的半径:varCENTER={x:150,y:150},BIG_RADIUS=150,SMALL_RADIUS=70创建一个div来表示两个圆varbigCircle=document.createElement('div')varsmallCircle=document.createElement('div')document.body.appendChild(bigCircle)document.body.appendChild(smallCircle)封装了一个给大小圆添加样式参数的函数:center中心,radius半径,bg背景,isMove是否移动(使用CSS3变化和旋转,每0.16移动一次秒,即60帧)functionsetCircleClass(center,radius,bg,isMove){this.style.position='absolute'this.style.left=center.x-radius+'px'this.style.top=center.y-radius+'px'this.style.width=radius*2+'px'this.style.height=radius*2+'px'this.style.borderRadius='50%'this.style.zIndex=66666this.style.background=bgisMove&&(this.style.transition='transformlinear.016s')}调用添加样式的函数设置圆类。apply(bigCircle,[CENTER,BIG_RADIUS,'linear-gradient(skyblue,darkorange)',true])setCircleClass.apply(smallCircle,[CENTER,SMALL_RADIUS,'#FFF',false])颜色有点瓜:2.monitor鼠标事件监听大圆的mousemove事件,小圆阻止事件传播bigCircle.addEventListener('mousemove',main)smallCircle.addEventListener('mousemove',function(e){e.stopPropagation()})创建大圆圈的监听函数main这里设置为鼠标左键按下时生效,顺便写个打印语句functionmain(e){if(e.buttons===1){console.log('mouseismovinginthering')}}3.实时计算当前角度是下一步。如果想让圆环跟随鼠标旋转,首先想到的就是斜率。当监听事件被触发时,会不断计算连接鼠标位置和圆心的直线的斜率。通过比较这次的斜率和上次的斜率,就可以得到圆环旋转的方向,还得计算出圆环运动的速度。但是坡度的变化不是线性的,所以很难通过坡度的变化来计算速度...所以单靠坡度是没有办法解决问题的,所以也没有别的办法那他的线性变化呢……没错,就是角度~!记得Math对象有一些三角函数的方法,所以赶紧查了一下。正在我看得眼花缭乱的时候,忽然一个黑衣人从天而降,我定睛一看。这就是传说中的atan2函数。就是下一个”环顾四周,发现这家伙不仅长得帅,而且手里还拿着神器:计算角度函数functioncalcAngleDegrees(x,y){returnMath.atan2(y,x)*180/Math.PI}妈的,铁鞋穿破了,赶紧打吧:functionmain(e){if(e.buttons===1){varangle=calcAngleDegrees((e.clientX-CENTER.x),(CENTER.y-e.clientY))console.log('Angle:'+angle)}}从图中可以看出,角度的变化是从9点开始180度,递减360度顺时针方向,回到9点钟的方向,符合我们后续的需求,“师父,您这么厉害,您的好武功是怎么修炼出来的?”atan2笑而不语,转身消失在无尽的夜色中,只留下无尽的疑问……既然如此,作为一个热爱技术的搬砖人,我决定自己一探究竟~本节结束.4.根据角度计算方向和距离。旋转环具有角度值。解决了以下问题。首先创建一个旋转环的函数。参数:deg当前环函数的角度rotate(deg){this.style.webkitTransform='rotate('+deg+'deg)'this.style.mozTransform='rotate('+deg+'deg)'this.style.msTransform='rotate('+deg+'deg)'this.style.oTransform='rotate('+deg+'deg)'this.style.transform='rotate('+deg+'deg)'}创建变量:当前圆(大圆)角度值circleAngle,当前鼠标角度值mouseAngle,上次鼠标角度值lastMouseAnglevarcircleAngle=0,mouseAngle,lastMouseAngle这里需要初始化lastMouseAngle。这个操作看似简单,其实使用正确的姿势可以避免一系列的bug。比较好的方法是将鼠标移入圆圈内,在圆圈内按下。鼠标分配时,有兴趣的童鞋可以自行研究。bigCircle.addEventListener('mouseenter',init)bigCircle.addEventListener('mousedown',init)functioninit(e){lastMouseAngle=calcAngleDegrees((e.clientX-CENTER.x),(CENTER.y-e.clientY))}初始化lastMouseAngle后,mouseAngle-lastMouseAngle为角度正负增量决定方向:正数为逆时针,负数为顺时针增量决定距离:绝对值是旋转角度戒指。由于顺时针旋转时增量为负,而CSS中的transform属性是顺时针旋转增加角度,所以圆环当前角度计算公式为:circleAngle-=(mouseAngle-lastMouseAngle)transformingmain函数:functionmain(e){if(e.buttons===1){mouseAngle=calcAngleDegrees((e.clientX-CENTER.x),(CENTER.y-e.clientY))varchangeMouseAngle=mouseAngle-lastMouseAnglecircleAngle-=changeMouseAngleconsole.log('Currentangle:'+circleAngle)rotate.call(this,circleAngle)lastMouseAngle=mouseAngle}}可以看到原型已经有了,愉快的进入下一步5.操作外部dom想操作外部dom,需要的是可以用脚趾想象的线性变化的值,目前最合适毫无疑问就是当前的圆角circleAngle。如果仔细观察上图,你会发现,鼠标每移动过9点钟方向,圆圈角度就会瞬间改变360度,回到初始值。它不符合当前的需要。这是一波转型。当角度变化值changeMouseAngle超过一定程度时,将不会进行后续操作。考虑到20年单身用户的手速,暂时把这个值设为300varMAX_CHANGE_ANGLE=300functionmain(e){...varchangeMouseAngle=mouseAngle-lastMouseAngleif(Math.abs(changeMouseAngle)>MAX_CHANGE_ANGLE){returnlastMouseAngle=mouseAngle}...}这样,circleAngle就会线性变化。接下来就是找一张阿里塔的美图
