【webGl】threejs实现一个简单的动画-弹跳的小球
在这里,我们将动态画面简称为动画(animation)。正如动画片的原理一样,动画的本质是利用了人眼的视觉暂留特性,快速地变换画面,从而产生物体在运动的假象。而对于Three.js程序而言,动画的实现也是通过在每秒中多次重绘画面实现的。
为了衡量画面切换速度,引入了每秒帧数FPS(Frames Per Second)的概念,是指每秒画面重绘的次数。FPS越大,则动画效果越平滑,当FPS小于20时,一般就能明显感受到画面的卡滞现象。
那么FPS是不是越大越好呢?其实也未必。当FPS足够大(比如达到60),再增加帧数人眼也不会感受到明显的变化,反而相应地就要消耗更多资源(比如电影的胶片就需要更长了,或是电脑刷新画面需要消耗计算资源等等)。因此,选择一个适中的FPS即可。
NTSC标准的电视FPS是30,PAL标准的电视FPS是25,电影的FPS标准为24。而对于Three.js动画而言,一般FPS在30到60之间都是可取的。
setInterval方法
如果要设置特定的FPS(虽然严格来说,即使使用这种方法,JavaScript也不能保证帧数精确性),可以使用JavaScript DOM定义的方法:
setInterval(func, msec)
其中,func
是每过msec
毫秒执行的函数,如果将func
定义为重绘画面的函数,就能实现动画效果。setInterval
函数返回一个id
,如果需要停止重绘,需要使用clearInterval
方法,并传入该id
,具体的做法为:
requestAnimationFrame方法
大多数时候,我们并不在意多久重绘一次,这时候就适合用requestAnimationFrame方法了。它告诉浏览器在合适的时候调用指定函数,通常可能达到60FPS。
如何取舍
setInterval
方法与requestAnimationFrame
方法的区别较为微妙。一方面,最明显的差别表现在setInterval
可以手动设定FPS,而requestAnimationFrame
则会自动设定FPS;但另一方面,即使是setInterval
也不能保证按照给定的FPS执行,在浏览器处理繁忙时,很可能低于设定值。当浏览器达不到设定的调用周期时,requestAnimationFrame
采用跳过某些帧的方式来表现动画,虽然会有卡滞的效果但是整体速度不会拖慢,而setInterval
会因此使整个程序放慢运行,但是每一帧都会绘制出来;
总而言之,requestAnimationFrame
适用于对于时间较为敏感的环境(但是动画逻辑更加复杂),而setInterval
则可在保证程序的运算不至于导致延迟的情况下提供更加简洁的逻辑(无需自行处理时间)。
开始工作
完成init函数
var requestAnimationFrame = window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame; var scene = null;
var camera = null;
var renderer = null; var id = null; var stat = null; function init() {
stat = new Stats();
stat.domElement.style.position = 'absolute';
stat.domElement.style.right = '0px';
stat.domElement.style.top = '0px';
document.body.appendChild(stat.domElement); renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('mainCanvas')
});
scene = new THREE.Scene(); id = requestAnimationFrame(draw);
} function draw() {
stat.begin(); renderer.render(scene, camera); id = requestAnimationFrame(draw); stat.end();
} function stop() {
if (id !== null) {
cancelAnimationFrame(id);
id = null;
}
}
然后,为了实现弹球弹动的效果,我们创建一个球体作为弹球模型,创建一个平面作为弹球反弹的平面。为了在draw
函数中改变弹球的位置,我们可以声明一个全局变量ballMesh
,以及弹球半径ballRadius
。
var ballMesh = null;
var ballRadius = 0.5;
在init
函数中添加球体和平面,使弹球位于平面上,平面采用棋盘格图像作材质:
// ball
ballMesh = new THREE.Mesh(new THREE.SphereGeometry(ballRadius, , ),
new THREE.MeshLambertMaterial({
color: 0xffff00
}));
ballMesh.position.y = ballRadius;
scene.add(ballMesh); // plane
var texture = THREE.ImageUtils.loadTexture('../img/chess.png', {}, function() {
renderer.render(scene, camera);
});
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(, );
var plane = new THREE.Mesh(new THREE.PlaneGeometry(, ),
new THREE.MeshLambertMaterial({map: texture}));
plane.rotation.x = -Math.PI / ;
scene.add(plane);
现在,每帧绘制的都是相同的效果:
为了记录弹球的状态,我们至少需要位置、速度、加速度三个矢量,为了简单起见,这里弹球只做竖直方向上的自由落体运动,因此位置、速度、加速度只要各用一个变量表示。其中,位置就是ballMesh.position.y
,不需要额外的变量,因此我们在全局声明速度v
和加速度a
:
var v = ;
var a = -0.1;
这里,a = -0.1
代表每帧小球向y方向负方向移动0.1
个单位。
一开始,弹球从高度为maxHeight
处自由下落,掉落到平面上时会反弹,并且速度有损耗。当速度很小的时候,弹球会在平面上作振幅微小的抖动,所以,当速度足够小时,我们需要让弹球停止跳动。因此,定义一个全局变量表示是否在运动,初始值为false
:
var isMoving = false;
在HTML中定义一个按钮,点击按钮时,弹球从最高处下落:
function drop() {
isMoving = true;
ballMesh.position.y = maxHeight;
v = ;
}
下面就是最关键的函数了,在draw
函数中,需要判断当前的isMoving
值,并且更新小球的速度和位置:
function draw() {
stat.begin(); if (isMoving) {
ballMesh.position.y += v;
v += a; if (ballMesh.position.y <= ballRadius) {
// hit plane
v = -v * 0.9;
} if (Math.abs(v) < 0.001) {
// stop moving
isMoving = false;
ballMesh.position.y = ballRadius;
}
} renderer.render(scene, camera); id = requestAnimationFrame(draw); stat.end();
}
这样就实现小球的弹动效果了。最终的代码为:
var requestAnimationFrame = window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame; var scene = null;
var camera = null;
var renderer = null; var id = null; var stat = null; var ballMesh = null;
var ballRadius = 0.5;
var isMoving = false;
var maxHeight = ; var v = ;
var a = -0.01; function init() {
stat = new Stats();
stat.domElement.style.position = 'absolute';
stat.domElement.style.right = '0px';
stat.domElement.style.top = '0px';
document.body.appendChild(stat.domElement); renderer = new THREE.WebGLRenderer({
canvas: document.getElementById('mainCanvas')
});
scene = new THREE.Scene(); camera = new THREE.OrthographicCamera(-, , 3.75, -3.75, 0.1, );
camera.position.set(, , );
camera.lookAt(new THREE.Vector3(, , ));
scene.add(camera); // ball
ballMesh = new THREE.Mesh(new THREE.SphereGeometry(ballRadius, , ),
new THREE.MeshLambertMaterial({
color: 0xffff00
}));
ballMesh.position.y = ballRadius;
scene.add(ballMesh); // plane
var texture = THREE.ImageUtils.loadTexture('../img/chess.png', {}, function() {
renderer.render(scene, camera);
});
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(, );
var plane = new THREE.Mesh(new THREE.PlaneGeometry(, ),
new THREE.MeshLambertMaterial({map: texture}));
plane.rotation.x = -Math.PI / ;
scene.add(plane); var light = new THREE.DirectionalLight(0xffffff);
light.position.set(, , );
scene.add(light); id = requestAnimationFrame(draw);
} function draw() {
stat.begin(); if (isMoving) {
ballMesh.position.y += v;
v += a; if (ballMesh.position.y <= ballRadius) {
// hit plane
v = -v * 0.9;
} if (Math.abs(v) < 0.001) {
// stop moving
isMoving = false;
ballMesh.position.y = ballRadius;
}
} renderer.render(scene, camera); id = requestAnimationFrame(draw); stat.end();
} function stop() {
if (id !== null) {
cancelAnimationFrame(id);
id = null;
}
} function drop() {
isMoving = true;
ballMesh.position.y = maxHeight;
v = ;
}
链接:http://runjs.cn/code/qqpikkwt
链接:http://runjs.cn/detail/ecll36ex
要好好复习一下物理和数学了
【webGl】threejs实现一个简单的动画-弹跳的小球的更多相关文章
- js实现一个简单钟表动画(javascript+html5 canvas)
第一次在博客园注册发博.有一次去人家单位开标,看到开标网站上有个钟表动画,一时兴起,就写了个简单的钟表动画. 用js和html5 canvas对象实现一个简单钟表程序 主要用到的就是h5的canvas ...
- QT 中如何实现一个简单的动画
QT可以实现一下简单的动画,比如 运动的时钟 闪烁的按钮. 动画的实现: (1)创建一个定时器 (2)调用QWidget::update()通知界面重绘 实现一个按钮闪烁的例子: circlewidg ...
- pyqt一个简单的动画
import sys from PyQt4.QtGui import QApplication , QGraphicsEllipseItem , QGraphicsItemAnimationfrom ...
- unity使用Animator做一个简单的动画
1.在unity的物体上添加Animator组件 2.在Project下的Assets下添加Animator Controller 3.在Animator Controller添加动作 4.在动作之间 ...
- anime.js 实战:实现一个带有描边动画效果的复选框
在网页或者是APP的开发中,动画运用得当可以起到锦上添花的作用.正确使用动画,不但可以有助于用户理解交互的作用,还可以大大提高网页应用的魅力和使用体验.并且在现在的网页开发中,动画已经成为了一个设计的 ...
- [安卓] 18、一个简单的例子做自定义动画按钮和自定义Actionbar
在做安卓UI的时候有时候需自定义具有动画效果的按钮或需要自定义一下actionbar~ 本节用一个简单的demo讲如何自定义具有动画效果的按钮,以及个性化的actionbar 下面是效果: 其中: △ ...
- 分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”
这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业 ...
- Android 属性动画实现一个简单的PopupWindow
1.今天看到一个PopupWindow的效果如图: 2.其实就是属性动画的一个简单实用就ObjectAnimator就可以的,想实现更多,更灵活的可以用ValueAnimator 3.直接上代码: p ...
- Three.js构造一个简单的房间
主要研究three.js在3D场景中基本使用:画一个简单的房子.房子上画门和玻璃.房间内放一个床.定义鼠标事件可以移动场景.动画的使用等. 1.Three.js画的一个简单的房子,模拟地板以及四堵墙 ...
随机推荐
- 对于多个数据库表对应一个Model问题的思考
最近做项目遇到一个场景,就是客户要求为其下属的每一个分支机构建一个表存储相关数据,而这些表的结构都是一样的,只是分属于不同的机构.这个问题抽象一下就是多个数据库表对应一个Model(或者叫实体类).有 ...
- SQL语句-创建索引
语法:CREATE [索引类型] INDEX 索引名称ON 表名(列名)WITH FILLFACTOR = 填充因子值0~100 GO USE 库名GO IF EXISTS (SELECT * FRO ...
- sql 触发器删除操作
create trigger CheckDelete on 表 for delete as ) select @state=isnull(字段,'') from deleted if (@state& ...
- IE兼容方法
其实我也觉得非常麻烦,开始的时候都用 _XXX:XXX; /* IE6支持 */ *XXX:XXX; /* IE6.IE7支持 */ *+XXX:XXX; /* IE7支持 */ XXX:XXX\9; ...
- 把url参数转化成一个对象返回
var readUrlToParams = function () { var url = location.href; var nameValue; var paraString = url.sub ...
- leetcode
Coding on LeetCode Online Judge leetcode(leetcode website) Problems algorithms 13. Roman to Integer ...
- HTTP Request Method共计15种
更多信息可参考http://tools.jb51.net/table/http_request_method
- bzoj2330: [SCOI2011]糖果
2330: [SCOI2011]糖果 Time Limit: 10 Sec Memory Limit: 128 MB Description 幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友 ...
- javaScript timer控制
<script type="text/javascript"> ; //间隔一秒循环执行 var id = setInterval(function () { num ...
- php验证身份证号码的正确性
/********************php验证身份证号码是否正确函数*********************/function is_idcard( $id ) { $id = strto ...