javascript运动系列第三篇——曲线运动
前面的话
上一篇介绍了变速运动,但只实现了直线运动。如果元素的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>
javascript运动系列第三篇——曲线运动的更多相关文章
- javascript面向对象系列第三篇——实现继承的3种形式
× 目录 [1]原型继承 [2]伪类继承 [3]组合继承 前面的话 学习如何创建对象是理解面向对象编程的第一步,第二步是理解继承.本文是javascript面向对象系列第三篇——实现继承的3种形式 [ ...
- 深入理解javascript函数系列第三篇——属性和方法
× 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...
- 深入理解javascript作用域系列第三篇——声明提升(hoisting)
× 目录 [1]变量 [2]函数 [3]优先 前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javasc ...
- 深入理解javascript作用域系列第三篇
前面的话 一般认为,javascript代码在执行时是由上到下一行一行执行的.但实际上这并不完全正确,主要是因为声明提升的存在.本文是深入理解javascript作用域系列第三篇——声明提升(hois ...
- 深入理解javascript函数系列第三篇
前面的话 函数是javascript中特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本文是深入理解javascript函数 ...
- javascript运动系列第四篇——抖动
× 目录 [1]原理介绍 [2]代码实现 [3]实例应用 前面的话 在运动系列中,前面分别介绍了匀速运动.变速运动和曲线运动.下面介绍一种特殊的运动形式——抖动 原理介绍 抖动其实是往复运动的一种特殊 ...
- javascript运动系列第八篇——碰壁运动
× 目录 [1]匀速碰壁 [2]自由落体 [3]投掷碰壁[4]拖拽碰壁 前面的话 碰撞运动可能是运动系列里面比较复杂的运动了.碰撞可以分为碰壁和互碰两种形式,而碰撞前后的运动形式也可以分为变速和匀速两 ...
- javascript动画系列第三篇——碰撞检测
前面的话 前面分别介绍了拖拽模拟和磁性吸附,当可视区域内存在多个可拖拽元素,就出现碰撞检测的问题,这也是javascript动画的一个经典问题.本篇将详细介绍碰撞检测 原理介绍 碰撞检测的方法有很多, ...
- javascript运动系列第六篇——轨迹和投掷
× 目录 [1]运动轨迹 [2]拖拽轨迹 [3]投掷 前面的话 一般地,不同的运动形式会产生不同的轨迹.但仅凭肉眼去识别运动轨迹,其实并不是很直观.因此,在页面中显示运动轨迹,是一个重要的问题.物体初 ...
随机推荐
- 读取hdfs文件之后repartition 避免数据倾斜
场景一: api: textFile("hfds://....").map((key,value)).reduceByKey(...).map(实际的业务计算逻辑) 场景:hdf ...
- 阿里云ECS(Centos)开启X11的步骤
阿里云ECS(Centos)开启X11的步骤 1.修改sshd_config X11Forwarding yes 2.yum -y install xorg-x11-xauth xorg-x11-ut ...
- 基础2.通过Ajax获得servlet数据(最基础)
案列一:从服务器的得到输出的数据 Jsp界面 <script type="text/javascript" src="test.js"></s ...
- 黑马程序员----java基础笔记下(毕向东)
------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 目录--- 21.字符编码 22.javaswig 事件 23.socket 网络通讯 24.网 ...
- “LC.exe”错误
错误“LC.exe”已退出,代码为 -1. 可能的原因是: 这个第三方组件是个商业组件,他在组件的主使用类定义了 LicenseProvider(typeof(LicFileLicenseProvid ...
- *HDU 1392 计算几何
Surround the Trees Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Other ...
- 猥琐的wordpress后门分享
https://www.t00ls.net/thread-37312-1-1.html 一个可以自动调用管理员帐号登录wordpress后台的方法. <?php require('../../. ...
- java学习中的一些疑惑解答
一.java中的枚举类型: 在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的.例如星期一到星期日七个数据元素 ...
- html websocket
from:http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/ websocket 规范升级过,在该链接的文章内未提及,后面 ...
- SQL Server 2016五大优势挖掘企业用户数据价值
SQL Server 2016五大优势挖掘企业用户数据价值 转载自:http://soft.zdnet.com.cn/software_zone/2016/0318/3074442.shtml 3月1 ...