javascript运动系列第六篇——轨迹和投掷
前面的话
一般地,不同的运动形式会产生不同的轨迹。但仅凭肉眼去识别运动轨迹,其实并不是很直观。因此,在页面中显示运动轨迹,是一个重要的问题。物体初始态时,受到外力大小不同,则初速度不同。如何在网页中模拟投掷效果,也需要解决。接下来,将详细介绍轨迹和投掷
运动轨迹
元素在运动过程中,不同的运动形式会产生不同的轨迹。如果不把轨迹表示出来,我们只能通过肉眼来区分运动形式。表示轨迹通常有两种方式:创建小元素和使用canvas
创建小元素
创建小元素原理上比较简单,但是性能较差。在元素移动时,创建一个2px*2px的小元素,并添加到页面上。以最简单的匀速运动为例
<!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>
<button id="btn1">开始运动</button>
<button id="btn2">删除轨迹</button>
<button id="reset">还原</button>
<div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div>
<script>
function getCSS(obj,style){
if(window.getComputedStyle){
return getComputedStyle(obj)[style];
}
return obj.currentStyle[style];
}
function move(obj,attr,target,step,fn){
//如果没有建立定时器对象,则在obj下建立定时器对象
if(!obj.timers){obj.timers = {};}
//清除定时器
clearInterval(obj.timers[attr]);
//声明当前值变量cur
var cur;
//判断步长step的正负值
step = parseInt(getCSS(obj,attr)) < target ? step : -step;
//开启定时器
obj.timers[attr] = setInterval(function(){
//获取样式当前值并赋值给cur
cur = parseFloat(getCSS(obj,attr));
////若步长设置值使得元素超过目标点时,将步长设置值更改为目标点值 - 当前值
if((cur + step - target)*step > 0){
step = target - cur;
}
//将合适的步长值赋值给元素的样式
obj.style[attr] = cur + step + 'px';
//设置轨迹
createTracks(obj.offsetLeft,obj.offsetTop)
//当元素到达目标点后,停止定时器
if(step == target - cur){
clearInterval(obj.timers[attr]);
obj.timers[attr] = 0;
fn && fn.call(obj);
}
},20);
}
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--;
}
}
}
btn1.onclick = function(){
move(test,'left',300,10)
}
btn2.onclick = function(){
deleteTracks()
}
reset.onclick = function(){
history.go();
}
</script>
</body>
</html>
使用canvas
使用canvas也可以实现运动轨迹,且性能较好,只不过需要掌握canvas的一些基础知识
<!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: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div>
<canvas id="drawing" style="position:absolute;left:0;top:0;z-index:-1"></canvas>
<script>
var context;
backupCanvas();
function backupCanvas(){
var drawing = document.getElementById('drawing');
drawing.setAttribute('width',document.documentElement.clientWidth);
drawing.setAttribute('height',document.documentElement.clientHeight);
if(drawing.getContext){
context = drawing.getContext('2d');
context.beginPath();
context.moveTo(test.offsetLeft,test.offsetTop);
}
}
function getCSS(obj,style){
if(window.getComputedStyle){
return getComputedStyle(obj)[style];
}
return obj.currentStyle[style];
}
function move(obj,attr,target,step,fn){
//如果没有建立定时器对象,则在obj下建立定时器对象
if(!obj.timers){obj.timers = {};}
//清除定时器
clearInterval(obj.timers[attr]);
//声明当前值变量cur
var cur;
//判断步长step的正负值
step = parseInt(getCSS(obj,attr)) < target ? step : -step;
//开启定时器
obj.timers[attr] = setInterval(function(){
//获取样式当前值并赋值给cur
cur = parseFloat(getCSS(obj,attr));
////若步长设置值使得元素超过目标点时,将步长设置值更改为目标点值 - 当前值
if((cur + step - target)*step > 0){
step = target - cur;
}
//将合适的步长值赋值给元素的样式
obj.style[attr] = cur + step + 'px';
createCanvasTracks(obj.offsetLeft,obj.offsetTop);
//当元素到达目标点后,停止定时器
if(step == target - cur){
clearInterval(obj.timers[attr]);
obj.timers[attr] = 0;
fn && fn.call(obj);
}
},20);
}
function createCanvasTracks(x,y){
context.lineTo(x,y);
context.stroke();
}
function deleteCanvasTracks(){
drawing.height = drawing.height;
}
btn1.onclick = function(){
move(test,'left',300,10)
}
btn2.onclick = function(){
deleteCanvasTracks()
}
reset.onclick = function(){
history.go();
}
</script>
</body>
</html>
拖拽轨迹
物体在拖拽的时候,同样也存在着拖拽轨迹。由于拖拽的运动形式不固定,因此轨迹也不固定
同样地,拖拽轨迹也有创建小元素和使用canvas两种方法
【创建小元素】
<!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>
<button id="btn">删除轨迹</button>
<button id="reset">还原</button>
<div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div> <script>
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--;
}
}
}
btn.onclick = function(){
deleteTracks()
}
reset.onclick = function(){
history.go();
}
test.onmousedown = function(e){
e = e || event;
//获取元素距离定位父级的x轴及y轴距离
var x0 = this.offsetLeft;
var y0 = this.offsetTop;
//获取此时鼠标距离视口左上角的x轴及y轴距离
var x1 = e.clientX;
var y1 = e.clientY;
document.onmousemove = function(e){
e = e || event;
//获取此时鼠标距离视口左上角的x轴及y轴距离
x2 = e.clientX;
y2 = e.clientY;
//计算此时元素应该距离视口左上角的x轴及y轴距离
var X = x0 + (x2 - x1);
var Y = y0 + (y2 - y1);
//将X和Y的值赋给left和top,使元素移动到相应位置
test.style.left = X + 'px';
test.style.top = Y + 'px';
//创建轨迹
createTracks(X,Y);
} document.onmouseup = function(e){
//当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
document.onmousemove = null;
//释放全局捕获
if(test.releaseCapture){
test.releaseCapture();
}
}
//阻止默认行为
return false;
//IE8-浏览器阻止默认行为
if(test.setCapture){
test.setCapture();
}
}
</script>
</body>
</html>
【使用canvas】
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="btn">删除轨迹</button>
<button id="reset">还原</button>
<div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div>
<canvas id="drawing" style="position:absolute;left:0;top:0;z-index:-1"></canvas>
<script>
var context;
backupCanvas();
function backupCanvas(){
var drawing = document.getElementById('drawing');
drawing.setAttribute('width',document.documentElement.clientWidth);
drawing.setAttribute('height',document.documentElement.clientHeight);
if(drawing.getContext){
context = drawing.getContext('2d');
context.beginPath();
context.moveTo(test.offsetLeft,test.offsetTop);
}
}
function createCanvasTracks(x,y){
context.lineTo(x,y);
context.stroke();
}
function deleteCanvasTracks(){
drawing.height = drawing.height;
}
btn.onclick = function(){
deleteCanvasTracks()
}
reset.onclick = function(){
history.go();
}
test.onmousedown = function(e){
e = e || event;
//获取元素距离定位父级的x轴及y轴距离
var x0 = this.offsetLeft;
var y0 = this.offsetTop;
//获取此时鼠标距离视口左上角的x轴及y轴距离
var x1 = e.clientX;
var y1 = e.clientY;
document.onmousemove = function(e){
e = e || event;
//获取此时鼠标距离视口左上角的x轴及y轴距离
x2 = e.clientX;
y2 = e.clientY;
//计算此时元素应该距离视口左上角的x轴及y轴距离
var X = x0 + (x2 - x1);
var Y = y0 + (y2 - y1);
//将X和Y的值赋给left和top,使元素移动到相应位置
test.style.left = X + 'px';
test.style.top = Y + 'px';
//创建轨迹
createCanvasTracks(X,Y);
} document.onmouseup = function(e){
//当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
document.onmousemove = null;
//释放全局捕获
if(test.releaseCapture){
test.releaseCapture();
}
}
//阻止默认行为
return false;
//IE8-浏览器阻止默认行为
if(test.setCapture){
test.setCapture();
}
}
</script>
</body>
</html>
投掷
如何使用javascript模拟出投掷的效果呢?javascript里面并没有力的概念。我们可以使用投掷速度为基准,当投掷速度快时,元素的速度也快;投掷速度慢时,元素速度同样也慢
问题来了,投掷速度如何确定。在javascript中模拟运动通常是使用一定频率的定时器来实现,投掷速度也同样如此。速度就相当于是一定时间的位移(或称为步长)。在定时器频率确定的情况下同,位移的确定其实就是找起始点和结束点这两个点的坐标位置
拖拽共涉及到三个鼠标事件:mousedown、mousemove和mouseup。结束点的位置是mouseup事件的鼠标位置(注意:mouseup事件的鼠标位置和最后一次mousemove事件的鼠标位置是相同的),而开始点的位置可以是mousemove事件倒数第二次的鼠标位置。这两个位置是拖拽运动的最后两个运动位置,通过确定他们就可以确定投掷步长了
下面以匀速运动为例,来进行实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="reset">还原</button>
<div id="test" style="height: 50px;width: 50px;background:pink;position:absolute;top:40px;left:0;"></div>
<script>
function getCSS(obj,style){
if(window.getComputedStyle){
return getComputedStyle(obj)[style];
}
return obj.currentStyle[style];
}
function move(obj,attr,target,step,fn){
//如果没有建立定时器对象,则在obj下建立定时器对象
if(!obj.timers){obj.timers = {};}
//清除定时器
clearInterval(obj.timers[attr]);
//声明当前值变量cur
var cur;
//判断步长step的正负值
step = parseInt(getCSS(obj,attr)) < target ? step : -step;
//开启定时器
obj.timers[attr] = setInterval(function(){
//获取样式当前值并赋值给cur
cur = parseFloat(getCSS(obj,attr));
//若步长设置值使得元素超过目标点时,将步长设置值更改为目标点值 - 当前值
if((cur + step - target)*step > 0){
step = target - cur;
}
//将合适的步长值赋值给元素的样式
obj.style[attr] = cur + step + 'px';
//当元素到达目标点后,停止定时器
if(step == target - cur){
clearInterval(obj.timers[attr]);
obj.timers[attr] = 0;
fn && fn.call(obj);
}
},30);
}
reset.onclick = function(){
history.go();
}
test.onmousedown = function(e){
e = e || event;
//声明投掷步长值
var stepX,stepY;
//声明上一次mousemove事件的坐标位置
var lastX2 = e.clientX;
var lastY2 = e.clientY;
//获取元素距离定位父级的x轴及y轴距离
var x0 = this.offsetLeft;
var y0 = this.offsetTop;
//获取此时鼠标距离视口左上角的x轴及y轴距离
var x1 = e.clientX;
var y1 = e.clientY;
document.onmousemove = function(e){
e = e || event;
//获取此时鼠标距离视口左上角的x轴及y轴距离
var x2 = e.clientX;
var y2 = e.clientY;
stepX = x2 - lastX2;
stepY = y2 - lastY2;
lastX2 = e.clientX;
lastY2 = e.clientY;
//计算此时元素应该距离视口左上角的x轴及y轴距离
var X = x0 + (x2 - x1);
var Y = y0 + (y2 - y1);
//将X和Y的值赋给left和top,使元素移动到相应位置
test.style.left = X + 'px';
test.style.top = Y + 'px';
}
document.onmouseup = function(e){
e = e || event;
var maxHeight = document.documentElement.clientHeight - test.offsetHeight;
var maxWidth = document.documentElement.clientWidth - test.offsetWidth;
//以设置的投掷速度来进行匀速运动
//向右投掷
if(stepX > 0){
move(test,'left',maxWidth,stepX);
//向左投掷
}else{
move(test,'left',0,stepX);
}
//向下投掷
if(stepY > 0){
move(test,'top',maxHeight,stepY);
//向上投掷
}else{
move(test,'top',0,stepY);
}
//当鼠标抬起时,拖拽结束,则将onmousemove赋值为null即可
document.onmousemove = null;
//释放全局捕获
if(test.releaseCapture){
test.releaseCapture();
}
}
//阻止默认行为
return false;
//IE8-浏览器阻止默认行为
if(test.setCapture){
test.setCapture();
}
}
</script>
</body>
</html>
javascript运动系列第六篇——轨迹和投掷的更多相关文章
- javascript运动系列第八篇——碰壁运动
× 目录 [1]匀速碰壁 [2]自由落体 [3]投掷碰壁[4]拖拽碰壁 前面的话 碰撞运动可能是运动系列里面比较复杂的运动了.碰撞可以分为碰壁和互碰两种形式,而碰撞前后的运动形式也可以分为变速和匀速两 ...
- javascript运动系列第四篇——抖动
× 目录 [1]原理介绍 [2]代码实现 [3]实例应用 前面的话 在运动系列中,前面分别介绍了匀速运动.变速运动和曲线运动.下面介绍一种特殊的运动形式——抖动 原理介绍 抖动其实是往复运动的一种特殊 ...
- javascript运动系列第三篇——曲线运动
× 目录 [1]圆周运动[2]三维圆周 [3]钟摆运动 [4]抛物线[5]流体运动 前面的话 上一篇介绍了变速运动,但只实现了直线运动.如果元素的left和top同时运动,并遵循不同的曲线公式,则会进 ...
- javascript运动系列第七篇——鼠标跟随运动
× 目录 [1]眼球转动 [2]苹果菜单[3]方向跟随 前面的话 运动除了直线运动和曲线运动两种运动形式外,还有一种运动形式是鼠标跟随运动,而这种跟随运动需要用到三角函数的相关内容或者需要进行比例运算 ...
- javascript运动系列第五篇——缓冲运动和弹性运动
× 目录 [1]缓冲运动 [2]弹性运动 [3]距离分析[4]步长分析[5]弹性过界[6]弹性菜单[7]弹性拖拽 前面的话 缓冲运动指的是减速运动,减速到0的时候,元素正好停在目标点.而弹性运动同样是 ...
- JavaScript 专题系列第六篇,讲解深浅拷贝的技巧和以及实现深浅拷贝的思路
拷贝也是面试经典呐! 数组的浅拷贝 如果是数组,我们可以利用数组的一些方法比如:slice.concat 返回一个新数组的特性来实现拷贝. 比如: var arr = ['old', 1, tru ...
- javascript面向对象系列第三篇——实现继承的3种形式
× 目录 [1]原型继承 [2]伪类继承 [3]组合继承 前面的话 学习如何创建对象是理解面向对象编程的第一步,第二步是理解继承.本文是javascript面向对象系列第三篇——实现继承的3种形式 [ ...
- 深入理解javascript函数系列第三篇——属性和方法
× 目录 [1]属性 [2]方法 前面的话 函数是javascript中的特殊的对象,可以拥有属性和方法,就像普通的对象拥有属性和方法一样.甚至可以用Function()构造函数来创建新的函数对象.本 ...
- 深入理解javascript作用域系列第四篇——块作用域
× 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...
随机推荐
- JavaScript-Function基础知识
function 1. 定义:一段预先设置的代码块,可以反复调用,根据输入参数的不同,返回不同的值: 2. 函数的声明方法: (1)function 命令声明函数 functio ...
- label 多行显示自适应高度
//项目中显示 地址:XXXXXXX换行 UILabel *numLable = [[UILabel alloc] initWithFrame:CGRectMake(80, 50, 40, 20)] ...
- 自定义委托类型 - .Net自带委托类型
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递. 与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用. 一.自定义委托类型 1.语法结构:访问修 ...
- 最长下降子序列O(n^2)及O(n*log(n))解法
求最长下降子序列和LIS基本思路是完全一样的,都是很经典的DP题目. 问题大都类似于 有一个序列 a1,a2,a3...ak..an,求其最长下降子序列(或者求其最长不下降子序列)的长度. 以最长下降 ...
- 【hihoCoder】1148:2月29日
问题:http://hihocoder.com/problemset/problem/1148 给定两个日期,计算这两个日期之间有多少个2月29日(包括起始日期). 思路: 1. 将问题转换成求两个日 ...
- iOS 添加中文支持的操作
1.选择工程菜单,这里要选中Project,而不是Targets 2.点击Info菜单, 下拉到最后,看到Localizations. 点击+号. 3.选择中文 chinese-simplif ...
- DoTween 应用设置
一.下载 官方下载地址:http://dotween.demigiant.com/download.php 二.安装 1.把下载到压缩包中的DOTween文件夹拷贝到项目文件中 2.安装DOTween ...
- 提取bmp图片的颜色信息,可直接framebuffer显示(c版本与python版本)
稍微了解了下linux的framebuffer,这是一种很简单的显示接口,直接写入像素信息即可 配置好的内核,会有/dev/fbn 的接口,于是想能否提前生成一个文件,比如logo.fb,里面仅包含像 ...
- OUTLOOK 发生错误0x8004010D
问题: outlook 2003 在接收邮件时报错: “正在接收”报告了错误(0x8004010D):“在包含您的数据文件的驱动器上,磁盘空间不足.请清空“已删除邮件”文件夹或删除某些文件以释放 ...
- 浅谈Js原型的理解
一.js中的原型毫无疑问一个难点,学习如果不深入很容易就晕了! 在参考了多方面的资料后,发现解释都太过专业,对于很多还没有接触过面向对象 语言的小白来说,有理解不了里面的专有名词!如果你没 ...