又想起了良久之前参加校招口试的时候,口试官问了我一个问题“能不能不借助库实现小球在浏览器中做圆周运动?”,于是就整理了一下让小球圆周运动的方法(纯属无聊不喜勿喷)。
由于之前的题目过于大略,作为一个需求来说短缺了一些必要条件,于是,我们给它增加一些条件,让他看起来更靠谱些。
在浏览器中间600px600px的指定区域内,不借助任何第三方插件,利用原生JavaScript或者CSS让一个半径为25px的小球环绕指定区域的中央做圆周运行,你有几种方法?
在让小球进行圆周运动之前,让我们先来实现一下题目中提到的根本样式,剧中的容器、半径25px的小球。
.container{position:absolute;left:50%;top:50%;margin:-300px00-300px;width:600px;height:600px;background:lightgray;}.ball{position:relative;width:50px;height:50px;border-radius:50%;background-color:coral;}
根本样式很随意马虎就完成了,我们再来思考一下实现圆周运动的详细方法。我们可以将办理方法分为CSS和JavaScript两大类。
CSS方法方法1: 双Dom元素,父元素利用animation属性<divclass="container"><divclass="inner-container"><divclass="ball"></div></div></div>
.inner-container{width:600px;height:600px;animation:rotate8slinearinfinite;}.ball{margin:0auto;}@keyframesrotate{to{transform:rotate(1turn);}}
上面的方法属于很随意马虎想到的一种方法,这里我们利用了animation属性,一贯循环播放定义好的keyframe动画rotate即可。
仅仅通过animation和transform属性让外层元素旋转起来,视觉效果上看起来便是小圆球在旋转。为了让效果明显,我们把外层元素的背景色弄的明显一些。
方法2: 单Dom元素,修正transform-origin
如果是单个Dom元素,我们怎么才能让小球做圆周运动呢。如下图,A点环绕O点移动一定的角度到达B点,我们不断扩大迁徙改变的角度即可实现圆周运动。
我们只须要让我们的圆环绕着某一个中央点旋转就可以了。而CSS中刚好供应了这样一个属性transform-origin,让我们能够修正DOM节点的中央点。
在我们的根本样式中,在.ball上增加transform-origin: 300px center;属性,就可以帮我们把旋转的圆心向右移动300px(图中赤色区域)。
再通过上面实现的旋转动画,即可实现圆周运动。详细实当代码如下:
<divclass="container"><divclass="ball"></div></div>
.ball{top:50%;margin-top:-25px;transform-origin:300pxcenter;animation:rotate8slinearinfinite;}@keyframesrotate{to{transform:rotate(1turn);}}
前面两个方法相比拟较常见,我们的第三种方法将会用到前面提到css属性motion path。
我们的题目中是在圆周运动,因此,还可以纯挚通过animation和transform属性来完成效果,如果我们的题目变更成了要去实现一条繁芜的曲线就会很僵硬,比如下面的路径。
为理解决上面的问题,2015年W3C提出了CSS路径动画干系的草案,也便是我们现在准备要利用的motion path。个中包括了5个属性:
offset-path:运动路径offset-anchor:运动元素的锚点位置offset-position:定了路径本身的初始位置offset-distance:运动元素在路径上的位置offset-rotate:工具的旋转角度或是如何自动旋转个中,offset-path吸收path()、url()、ray()、none等值。这里的path()是利用SVG坐标语法定义的路径字符串。那我们的问题就变得大略了,只须要找到在svg中如何实现圆环路径即可。
下面是我们找到的SVG中实现一个圆环路径的代码,个中cx、cy代表圆形的坐标, r代表圆的半径,填入对应的值即可天生我们想要的圆环路径M 300, 300 m -275, 0 a 275,275 0 1,0 550,0 a 275,275 0 1,0 -550,0。
<path d=" M cx cy m -r, 0 a r,r 0 1,0 (r 2),0 a r,r 0 1,0 -(r 2),0"/>
在实现完轨迹之后,我们还须要通过掌握offset-distance的值让我们的圆在我们画出来的圆环路径上动起来。
详细实当代码如下:
<divclass="container"><divclass="ball"></div></div>
.ball{offset-path:path('M300,300m-275,0a275,27501,0550,0a275,27501,0-550,0');animation:move3000mslinearinfinite;}@keyframesmove{0%{offset-distance:0%;}100%{offset-distance:100%;}}
我们来到了JavaScript环节,通过万能的JS我们只须要节制一点点数学知识和定时器知识即可实现我们的小球运动啦~
这里实现时有2个须要把稳的点:
实现动画播放时,为什么用requestAnimationFrame不用setInterval?改变圆的位置时,为什么不直接修正top和left,而是要修正transform?问题1: 实现动画播放时,为什么用requestAnimationFrame不用setInterval?为了担保动画运行流畅性,我们须要只管即便保持动画的播放帧率大于即是60Hz,即1000 / 60 = 16.67ms播放一次动画,requestAnimationFrame和setInterval都可以掌握动画渲染的间隔。
但是,setInterval本身是基于韶光来设置,它仅仅是将须要实行的事宜定期插入到须要实行的行列步队中,不管,前面的事宜是否已经实行了。因此,可能存在多次定时器之间的间隔可能小于我们设定的值,导致动画实行过快或者直接跳帧。
而requestAnimationFrame则是奉告浏览器在刷新下一帧时须要实行那些函数。确保我们的动画的播放帧率和屏幕的刷新帧率同等。
问题2: 改变圆的位置时,为什么不直接修正top和left,而是要修正transform?直接修正top和left会触发浏览器的重排和重绘,而修正tranform仅仅会触发重绘。而频繁的重排对动画性能影响极大,详细缘故原由大家可以百度一下浏览器 重排 重绘,干系的文章非常多。
其余想要查看自己的页面是否触发了重排、重绘,大家可以借助chrome的rendering面板来查看
说了这么多,我们来看一下利用requestAnimationFrame怎么实现圆周运动的动画,详细实当代码如下:
<divclass="container"><divid="ball"class="ball"></div></div>
let$ball=document.querySelector('#ball');letangle=0;letgetPosition=((centerX,centerY,radius)=>{return(angle)=>{return{x:+centerX+Math.cos(angle)radius,y:+centerY+Math.sin(angle)radius,};};})(275,275,275);run();functionrun(){//角度加1angle+=1;letposition=getPosition((angleMath.PI)/180);$ball.style.transform=`translate(${position.x}px,${position.y}px)`;window.requestAnimationFrame(run);}
除了通过Dom来掌握元素之外,我们还可以通过canvas直接在页面上画圆。canvas做动画的事理便是快速的重复“擦除->移动位置画圆”的这个步骤。这里就不阐明为啥了,直接上代码
详细实当代码如下:
<divclass="container"><canvasid="canvas"width="600px"height="600px"></canvas></div>
letangle=0;letgetPosition=((centerX,centerY,radius)=>{return(angle)=>{return{x:+centerX+Math.cos(angle)radius,y:+centerY+Math.sin(angle)radius,};};})(300,300,275);let$canvas=document.querySelector('#canvas');letctx=$canvas.getContext('2d');//设置添补色ctx.fillStyle='coral';run();functionrun(){angle+=1;letposition=getPosition((angleMath.PI)/180);//清空画布ctx.clearRect(0,0,600,600);ctx.beginPath();//画圆ctx.arc(position.x,position.y,25,0,2Math.PI,true);//添补ctx.fill();ctx.closePath();window.requestAnimationFrame(run);}
好啦,打完收工,这里便是我能想到的让小球做圆周运动的5种方法。
如果还有别的方法可以留言互换一下,一起涨涨姿势。
后记我是一个莫得感情的代码搬运工,最近搞了一个"大众号,每周会定期更新1、2篇前端文章,大家有兴趣的话关注一下,我们一起互换前端知识~