前面的話
上一篇介紹了變速運動,但只實現了直線運動。如果元素的left和top同時運動,并遵循不同的曲線公式,則會進行不同形式的曲線運動。本文將詳細介紹圓周運動、鐘擺運動、拋物線運動和流體運動這四種曲線運動形式
圓周運動
圓周運動可能是最好理解的曲線運動了

若(x0,y0)為圓心,則圓的公式為(x-x0)*(x-x0) + (y-y0)*(y-y0) = r*r
寫成三角函數的形式為
x = x0 + cosa*r
y = y0 + sina*r
所以,實際上只要知道夾角a和圓心(x0,y0)就可以計算出x和y
圓周運動可以封裝為函數circleMove.js
function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } function circleMove(json){ //要操作的元素 var obj = json.obj; //方向(順時針'+'或逆時針'-') var dir = json.dir; dir = dir || '+'; //最大圈數 var max = json.max; max = Number(max) || 'all'; //半徑 var r = json.r; r = Number(r) || 100; //圓心x軸坐標 var x0 = json.x0 || parseFloat(getCSS(obj,'left')); //圓心y軸坐標 var y0 = json.y0 || parseFloat(getCSS(obj,'top')) - r; //初始夾角,以角度為單位 var a0 = json.a0; a0 = Number(a) || 90; //當前夾角 var a = json.a ||a0; //當前圈數 var num = json.num || 0; //清除定時器 if(obj.timer){return;} //聲明當前值cur var cur = {}; obj.timer = setInterval(function(){ //將這些瞬時值儲存在obj對象中的屬性中 obj.a = a; obj.x0 = x0; obj.y0 = y0; obj.x = x; obj.y = y; obj.num = num; //如果元素運動到指定圈數則停止定時器 if(num == max){ clearInterval(obj.timer); } //順時針 if(dir == '+'){ a++; if(a == a0 + 360){ a = a0; num++; } //逆時針 }else{ a--; if(a == a0 - 360){ a = a0; num++; } } //更新當前值 cur.left = parseFloat(getCSS(obj,'left')); cur.top = parseFloat(getCSS(obj,'top')); //更新left和top值 var x = x0 + r*Math.cos(a*Math.PI/180); var y = y0 + r*Math.sin(a*Math.PI/180) test.style.left = x + 'px'; test.style.top = y + 'px'; },20); }
下面利用封裝的circleMove.js來實現簡單的圓周運動
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button id="btn1">順時針旋轉</button> <button id="btn2">逆時針旋轉</button> <button id="btn3">暫停</button> <button id="reset">還原</button> <div id="result"></div> <div id="backup" style="height: 298px;width:298px;border:1px solid black;border-radius:50%;position:absolute;top:50px;left:50px;"> <div id="test" style="height: 40px;width: 40px;background-color:pink;position:relative;left:130px;top:280px;border-radius:50%"></div> </div> <script src="http://files.cnblogs.com/files/xiaohuochai/circleMove.js"></script> <script> reset.onclick = function(){ history.go(); } btn1.onclick = function(){ circleMove({obj:test,r:150,x0:test.x0,y0:test.y0,a:test.a,num:test.num}); } btn2.onclick = function(){ circleMove({obj:test,r:150,dir:'-',x0:test.x0,y0:test.y0,a:test.a,num:test.num}); } btn3.onclick = function(){ clearInterval(test.timer); test.timer = 0; } </script> </body> </html>
【css3】
css3新增了transform和animation等新的樣式,也可以用來做圓周運動。transform里面有一個變形函數是rotate,這時就需要使用逆向思維。元素本身并不發生運動,而是軌道自身在旋轉,會實現視覺上的圓周運動效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> @keyframes rotate1{ 100%{transform:rotate(360deg);} } @keyframes rotate2{ 100%{transform:rotate(-360deg);} } #backup{ height: 298px;width:298px;border:1px solid black;border-radius:50%;position:absolute;top:50px;left:50px; } #test{ height: 40px;width: 40px;background-color:pink;position:relative;left:130px;top:280px;border-radius:50% } </style> </head> <body> <button id="btn1">順時針旋轉</button> <button id="btn2">逆時針旋轉</button> <button id="btn3">暫停</button> <button id="reset">還原</button> <div id="result"></div> <div id="backup"> <div id="test"></div> </div> <script> reset.onclick = function(){ history.go(); } btn1.onclick = function(){ backup.style.animation= 'rotate1 4s infinite linear'; } btn2.onclick = function(){ backup.style.animation= 'rotate2 4s infinite linear'; } btn3.onclick = function(){ backup.style.animationPlayState = 'paused'; } </script> </body> </html>
三維圓周
前面我們介紹了二維圓周運動,如果是三維圓周運動,則需要考慮x、y、z立體坐標軸

從示意圖中可知,三維圓周運動的模擬實現實際上是元素的寬高發生了變化,元素的x軸變化依然按照三角函數公式進行,元素的y軸一直保存為0
假設圓的寬(或高)在z軸正方向最遠處時為100px,當z軸值為0時,寬(或高)為50px,在z軸負方向最遠處時為0px
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button id="btn1">開始旋轉</button> <button id="btn2">暫停</button> <button id="reset">還原</button> <div id="result"></div> <div id="test" style="height: 100px;width: 100px;background-color:pink;position:relative;left:200px;top:60px;border-radius:50%"></div> <script> reset.onclick = function(){ history.go(); } btn1.onclick = function(){ threeCircleMove({ obj:test,r:200,x0:test.x0,width:test.width,height:test.height,a:test.a,num:test.num }) } btn2.onclick = function(){ clearInterval(test.timer); test.timer = 0; } function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } function threeCircleMove(json){ //要操作的元素 var obj = json.obj; //方向(順時針'+'或逆時針'-') var dir = json.dir; dir = dir || '+'; //最大圈數 var max = json.max; max = Number(max) || 'all'; //半徑 var r = json.r; r = Number(r) || 100; //圓心x軸坐標 var x0 = json.x0 || parseFloat(getCSS(obj,'left')); //元素的初始寬高 var offsetHeight = obj.offsetHeight; var offsetWidth = obj.offsetWidth; //元素的寬高 var height,width; //初始夾角,以角度為單位 var a0 = json.a0; a0 = Number(a) || 90; //當前夾角 var a = json.a ||a0; //當前圈數 var num = json.num || 0; //清除定時器 if(obj.timer){return;} //聲明當前值cur var cur = {}; obj.timer = setInterval(function(){ //將這些瞬時值儲存在obj對象中的屬性中 obj.a = a; obj.x0 = x0; obj.width = width; obj.height = height; obj.x = x; obj.num = num; //如果元素運動到指定圈數則停止定時器 if(num == max){ clearInterval(obj.timer); } //順時針 if(dir == '+'){ a++; if(a == a0 + 360){ a = a0; num++; } //逆時針 }else{ a--; if(a == a0 - 360){ a = a0; num++; } } //更新當前值 cur.left = parseFloat(getCSS(obj,'left')); //更新left值和寬高值 var x = x0 + r*Math.cos((90 + a*Math.PI)/180); width = (offsetWidth/2) + offsetWidth/2*Math.sin((90 + a*Math.PI)/180); height = (offsetHeight/2) + offsetWidth/2*Math.sin((90 + a*Math.PI)/180); test.style.left = x + 'px'; test.style.width = width + 'px'; test.style.height = height + 'px'; },20); } </script> </body> </html>
【css3】
同樣地,使用強大的css3屬性可以實現三維圓周效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> @keyframes rotate1{ 100%{transform:rotateX(60deg) rotate(360deg);} } @keyframes rotate2{ 100%{transform:rotateX(60deg) rotate(-360deg);} } body{ perspective: 700px; } #backup{ height: 298px;width:298px;border:1px solid black;border-radius:50%;position:absolute;top:100px;left:100px;transform:rotateX(60deg) rotate(0); } #test{ height: 40px;width: 40px;background-color:pink;position:relative;left:130px;top:280px;border-radius:50% } </style> </head> <body> <button id="btn1">順時針旋轉</button> <button id="btn2">逆時針旋轉</button> <button id="btn3">暫停</button> <button id="reset">還原</button> <div id="result"></div> <div id="backup"> <div id="test"></div> </div> <script> reset.onclick = function(){ history.go(); } btn1.onclick = function(){ backup.style.animation= 'rotate1 4s infinite linear'; } btn2.onclick = function(){ backup.style.animation= 'rotate2 4s infinite linear'; } btn3.onclick = function(){ backup.style.animationPlayState = 'paused'; } </script> </body> </html>
鐘擺運動
一個鐘擺,一會兒朝左,一會兒朝右,周而復始,來回擺動。鐘擺總是圍繞著一個中心值在一定范圍內作有規律的擺動,這種運動稱為鐘擺運動,可以把鐘擺運動看做圓周運動的一部分,進而比較簡單的實現鐘擺運動

假設,元素初始時處于鐘擺的最底點。當鐘擺與豎直線夾角為60度時,為最高點
若鐘擺運動的圓心為(x0,y0),則圓的公式為(x-x0)*(x-x0) + (y-y0)*(y-y0) = r*r
若夾角a為鐘擺與豎直線夾角,寫成三角函數的形式為
x = x0 + sina*r
y = y0 + cosa*r
當夾角a從0增加到60或減小到-60時,元素開始做反向運動
將鐘擺運動寫成pendulMove.js的形式
function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } function pendulMove(json){ //要操作的元素 var obj = json.obj; //起始方向(順時針'+'或逆時針'-') var dir = json.dir; dir = dir || '+'; //最大次數(再次經過最低點為一次) var max = json.max; max = Number(max) || 'all'; //半徑 var r = json.r; r = Number(r) || 100; //圓心x軸坐標 var x0 = json.x0 || parseFloat(getCSS(obj,'left')); //圓心y軸坐標 var y0 = json.y0 || parseFloat(getCSS(obj,'top')) - r; //初始夾角,以角度為單位 var a0 = json.a0; a0 = Number(a) || 0; //當前夾角 var a = json.a ||0; //當前次數 var num = 0; //清除定時器 if(obj.timer){return;} //聲明當前值cur var cur = {}; obj.timer = setInterval(function(){ //將這些瞬時值儲存在obj對象中的屬性中 obj.a = a; obj.x0 = x0; obj.y0 = y0; obj.x = x; obj.y = y; obj.num = num; //如果元素運動到指定圈數則停止定時器 if(num == max){ clearInterval(obj.timer); } //起始向右運動 if(dir == '+'){ a++; if(a == 60){ //方向變成向左 dir = '-'; } }else{ a--; if(a == -60){ //方向變成向右 dir = '+'; } } //更新當前值 cur.left = parseFloat(getCSS(obj,'left')); cur.top = parseFloat(getCSS(obj,'top')); //更新left和top值 var x = x0 + r*Math.sin(a*Math.PI/180); var y = y0 + r*Math.cos(a*Math.PI/180) test.style.left = x + 'px'; test.style.top = y + 'px'; },20); }
下面利用封裝的pendulMove.js來實現簡單的鐘擺運動
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button id="btn1">起始正向運動</button> <button id="btn2">起始逆向運動</button> <button id="btn3">暫停</button> <button id="reset">還原</button> <div id="result"></div> <div id="backup" style="height: 298px;width:298px;border-bottom:1px solid black;border-radius:50%;position:absolute;top:50px;left:50px;"> <div id="test" style="height: 40px;width: 40px;background-color:pink;position:relative;left:130px;top:280px;border-radius:50%"></div> </div> <script src="http://files.cnblogs.com/files/xiaohuochai/pendulMove.js"></script> <script> reset.onclick = function(){ history.go(); } btn1.onclick = function(){ pendulMove({obj:test,r:150,x0:test.x0,y0:test.y0,a:test.a,num:test.num}); } btn2.onclick = function(){ pendulMove({obj:test,r:150,dir:'-',x0:test.x0,y0:test.y0,a:test.a,num:test.num}); } btn3.onclick = function(){ clearInterval(test.timer); test.timer = 0; } </script> </body> </html>
【彈性運動】
實際情況下,鐘擺運動并不是勻速運動,而是一個重復的加減速運動,正好彈性運動可以輕松的實現類似效果
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button id="btn1">開始運動</button> <button id="btn2">暫停</button> <button id="reset">還原</button> <div id="test" style="height: 100px;width: 100px;background-color: pink;position:absolute;left:0;top: 30px;border-radius:50%"></div> <script> function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } reset.onclick = function(){history.go();} btn2.onclick = function(){ clearInterval(test.timer); } //聲明步長值stepY、stepX var stepY = 30; var stepX = 10; btn1.onclick = function(){ //聲明當前值curY、curX var curY,curX; //聲明目標值 var targetY = parseFloat('400px'); clearInterval(test.timer); test.timer = setInterval(function(){ //更新當前值 curY = parseFloat(getCSS(test,'top')); curX = parseFloat(getCSS(test,'left')); //更新步長值 stepY -= 1; //當元素返回到初始高度時 if(stepY == -30){ stepY = 29; stepX = -stepX; } //更新top、left值 test.style.top = curY + stepY + 'px'; test.style.left = curX + stepX + 'px'; },20); } </script> </body> </html>
拋物線運動
平面內到定點與定直線的距離相等的點的軌跡叫做拋物線。其中定點叫拋物線的焦點,定直線叫拋物線的準線。拋物線實際上就是一段特殊形式的曲線

拋物線方程為y=a*x*x+b*x+c
其中a、b、c為參數,以x為參照的話,當x以固定值遞增的方式進行變化時,y也會有相應變化
若a>0時,拋物線的開口向下;否則,開口向上
拋物線的準線的x軸坐標為(-2*a/b)。如果target目標設置為100,則(-2*a/b)盡量設置為50
若a = 0.01,則b=-1
將拋物線運動寫成parabolMove.js的形式
function getCSS(obj,style){ if(window.getComputedStyle){ return getComputedStyle(obj)[style]; } return obj.currentStyle[style]; } function parabolMove(json){ //設置要操作的元素 var obj = json.obj; //設置x軸上的目標值 var target = json.target; target = Number(target) || 300; //設置x軸的步長值 var stepValue = json.step || 2; //設置x軸的步長 var step = 0; //設置回調函數 var fn = json.fn; //參數a、b、c var a = json.a; a = Number(a) || 0.01; var b = json.b; b = Number(b) || -1*target/100; var c = json.c; c = Number(c) || 0; //初始值 var left = parseFloat(getCSS(obj,'left')); if(left >= target){return;} var top = parseFloat(getCSS(obj,'top')); //清除定時器 if(obj.timer){return;} //聲明當前值cur var cur = {}; obj.timer = setInterval(function(){ //更新步長值 step += stepValue; //更新left和top值 var x = left + step; var y = top + a*step*step + b*step + c; if(x > target){ x = target; } test.style.left = x + 'px'; test.style.top = y + 'px'; //如果到達目標點,清除定時器 if(x == target){ clearInterval(obj.timer); obj.timer = 0; fn && fn.call(obj); } },20); }
下面利用封裝的parabolMove.js來實現簡單的拋物線運動
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <button id="btn1">開始運動</button> <button id="reset">還原</button> <div id="test" style="height: 40px;width: 40px;background-color:pink;position:absolute;left:0px;top:100px;"></div> <script src="http://files.cnblogs.com/files/xiaohuochai/parabolMove.js"></script> <script> reset.onclick = function(){ history.go(); } btn1.onclick = function(){ parabolMove({obj:test,target:200}); } </script> </body> </html>
流體運動
流體運動實際上就是三角函數曲線運動,以sin為例,y = asin(b*x),當a和b取不同的值時,就可以得到不同的曲線形式
在這里要注意的是,sin里面的參數一定要寫成弧度的形式
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> .track{width: 2px;height: 2px;background-color:#000;position:absolute;} </style> </head> <body> <label for="a" id="labelA">參數a:100</label> <input id="a" type="range" min="50" max="100" step="10" value="100" /> <label for="b" id="labelB">參數b:1</label> <input id="b" type="range" min="1" max="5" step="1" value="1" /> <button id="reset">還原</button> <span>三角函數的公式為: y = a*sin(b*x)</span> <span id="result">所以,實際公式為:y = 100*sin(1*x)</span> <div id="test" style="height: 50px;width: 50px;background-color: pink;border-radius:50%;position: absolute;left: 30px;top:50px;"></div> <script> reset.onclick = function(){ history.go(); } function createTracks(x,y){ var ele = document.createElement('div'); ele.className = 'track'; ele.style.left = x + 'px'; ele.style.top = y + 'px'; document.body.appendChild(ele); } function deleteTracks(){ var eles = document.getElementsByTagName('div'); for(var i = 0 ;i < eles.length; i++){ if(eles[i].className == 'track'){ document.body.removeChild(eles[i]); i--; } } } function getResult(){ result.innerHTML = '所以,實際公式為: y=' + a.value + '*sin(' + b.value + '*x)'; } show(); function show(){ clearInterval(test.timer); //重置left、top值 test.style.left = 30 + 'px'; test.style.top = 50 + 'px'; //聲明定時器運行次數 var n = 0; //聲明拓展倍數 var value = 100; //清除軌跡 deleteTracks(); test.timer = setInterval(function(){ var A = Number(a.value); var B = Number(b.value); n++; var x = (B*n)*Math.PI/180; var y = A*Math.sin(x); test.style.left = x*value + 'px'; test.style.top = 2*A+y + 'px'; createTracks(x*value,2*A+y); if(x*value >= document.documentElement.clientWidth - 2*test.offsetWidth){ clearInterval(test.timer) } },20) } a.oninput = function(){ labelA.innerHTML = '參數a:' + this.value; getResult(); show(); } b.oninput = function(){ labelB.innerHTML = '參數b:' + this.value; getResult(); show(); } </script> </body> </html>
文章列表