写在最前:

 前两天我们这边接到公司做教育那块的一个活儿,要做一个比较炫酷的3D旋转特效,拿到网页一看,别人是用Three.js去实现的,确实比较狂拽。但是我对这个只是了解一点点,还不到能用它做出成熟DEMO的地步(学习的道路任重而道远啊...)。所以老大建议用css transform特性加上perspective试试。嗯,活儿都来了,上呗。所以有了这边分享的诞生,但是我的实现肯定比不了Three那么炫酷,将就着看吧,里面有很多数学方面的推导是我请教部门清华的同事完成的,再次感谢他,下面我们来看看吧。

 对了,惯例,我的博客点赞哈~

成果展示:

Git地址:

github.com/ry928330/tr…

功能说明:

  1. 以屏幕中心为球心,屏幕横向为X轴,纵向为Y轴,垂直屏幕为Z轴,横向滑动屏幕,页面中的图标会绕着Y轴旋转。纵向滑动,图标会绕着X轴旋转,斜着滑,图标就会斜着转。
  2. 点击页面中任意一个图标,被点中的图标会旋转到页面的中间(受perspective属性的影响,这个中间是一个近似的中间的位置)。

实现细节:

 按照功能我们从两个方面讲解DEMO的实现,一个是滑动屏幕,图标相应的滚动,另一个是点击图标,旋转到某一正确的位置。

滑屏转动图标:

 因为画图比较麻烦,所以我借助草稿一起来分析下图标的转动,如下图所示:

从图中我们可以清楚的得到绕X、Y轴分别旋转后所在位置的X、Y、Z坐标,但是我要想要斜着滑动,的话就存在空间中的点同时绕X和Y轴的旋转,此时我们只需要要讲两个旋转矩阵相乘即可,即AB或者BA,如果是前者,表示先绕X轴旋转,然后再绕Y轴旋转,如果是后者则表示先绕Y轴旋转,再绕X轴旋转。因为每转动一个很小的角度我们都计算一下空间点的位置,所以你看不出先后顺序,给人的感觉是同时进行的。这里给出代码如下:

/*** 旋转函数* @param xAng  绕x轴旋转角度,轴向顺时针* @param yAng  绕y轴旋转角度,轴向顺时针* @param vector  待旋转向量*/
function rotate(xAng, yAng, vector) {xRotMat = [[1, 0, 0], [0, Math.cos(xAng), Math.sin(xAng)], [0, -Math.sin(xAng), Math.cos(xAng)]];yRotMat = [[Math.cos(yAng), 0, -Math.sin(yAng)],[0, 1, 0],[Math.sin(yAng), 0, Math.cos(yAng)]];vector = matmul(yRotMat, vector);vector = matmul(xRotMat, vector);return vector;
}
/*** 简单矩阵乘法,不做任何合法性判断* A为3*3矩阵  B为1*3 返回1*3矩阵* */
function matmul(matA, matB) {var result = [];for (var i = 0; i < 3; i++) {result[i] = 0;for (var j = 0; j < 3; j++) {result[i] += matA[i][j] * matB[j];}}return result;
}复制代码

 得到各个坐标点的值后,通过transform的3d设置,即可完成图标在页面的绘制:

    /*** 绘制所有图标*/function printBalls() {for (var i = 0; i < objNum; i++) {index = i + 1;x = objPos[i][0];y = objPos[i][1];z = objPos[i][2];$('.num' + index).css({'transform': 'translate3d(' + x + 'px,' + y + 'px,' + z + 'px)'});}}复制代码

 这里我们得提一下关于页面结构,如下所示,在css里面我们得把图标的父元素container的perspective熟悉设置一个比图标运动半径大的值,不能太接近。因为perspective 属性用于定义 3D 元素距视图的距离,以像素计。该属性允许你改变 3D 元素查看 3D 元素的视图。当为元素定义 perspective 属性时,其子元素会获得透视效果,而不是元素本身。而且,当子元素的Z值越接近父元素的perspective值时,图标呈现的大小越大,因为此时就相当于图标就在你的眼前,当超出perspective值后,图标就看不见了,这时就相当于是图标成的像落在了你的视网膜后面,你没法儿看见图像了。关于perspective的详解,建议你看看张鑫旭的文章。

 <div id="stage"><div id="container"><div class="num1 word"></div><div class="num2 word"></div><div class="num3 word"></div><div class="num4 word"></div></div>
</div>复制代码

点击图标,旋转到特定位置:

 鉴于之前建立的空间坐标,你可能说只要给theta(空间中和Y轴的夹角)和phi(空间中和Z轴的夹角)两个角度设置一个固定的值,就能很容易的转到你想要的位置。但是现在有一个问题,绕Y轴旋转可以得到phi角的变化,但是绕哪个轴的旋转可以得到theta夹角的变化呢。这时请教了完美的清华同事给出了解决方案。以空间所在位置的向量和Y轴的向量构成的平面,做一个法向量,经验证可以得出该法向量的坐标为([z, 0, -x])然后绕着该轴旋转,即可得到theta的变化。但是,新的问题又来了,空间中怎么得到绕某一向量旋转一定角度该如何去计算呢,查阅相关资料,得到了最后的旋转矩阵,代码如下:

/*** 绕指定转轴进行旋转,要求转轴必须过原点* @param vector 转轴对应的向量* @param ang  旋转的角度*/
function rotateByArbitraryVec(vector, ang) {var cos = Math.cos(ang);var sin = Math.sin(ang);var a = vector[0];var b = vector[1];var c = vector[2];var r = Math.sqrt(a*a+b*b+c*c);a = a/r;b = b/r;c = c/r;var rotMat = [[a * a + (1 - a * a) * cos, a * b * (1 - cos) + c * sin, a * c * (1 - cos) - b * sin],[a * b * (1 - cos) - c * sin, b * b + (1 - b * b) * cos, b * c * (1 - cos) + a * sin],[a * c * (1 - cos) + b * sin, b * c * (1 - cos) - a * sin, c * c + (1 - c * c) * cos]];//对所有物体一次操作for (var i = 0, len = objNum; i < len; i++) {var result = matmul(rotMat, objPos[i]);objPos[i][0] = result[0];objPos[i][1] = result[1];objPos[i][2] = result[2];}
}复制代码

 这里我们得注意必须对空间旋转向量做归一化,这样旋转后的向量其模值才不会发生改变。有了旋转方程,我们就可以得出最后的计算结果,代码如下:

function moveToCenter(index) {// moveToTargetAngAdvance(index, Math.PI / 2, Math.PI / 2, Math.PI / 30);var fastMoveId = setInterval((function(index){//目标移动角度var theta = Math.PI/4; var phi = Math.PI/2;//每步需要旋转的度数var delta = Math.PI/30;var oriTheta, oriPhi;var tempArr = calPosAng(index);oriTheta = tempArr[0];oriPhi = tempArr[1];//计算需要旋转的方位角差值var theta2Rot = theta - oriTheta;var phi2Rot = phi - oriPhi;//计算需要的步数var step = Math.floor(Math.max(Math.abs(theta2Rot / delta), Math.abs(phi2Rot / delta)));if (step==0) {clearInterval(fastMoveId);return;}//每步需要旋转的方位角var dTheta = theta2Rot / step;var dPhi = phi2Rot / step;// console.log(dTheta - delta, dPhi - delta);var x, z;var k = 0;return function() {k++;// console.log(k, step);x = objPos[index][0];z = objPos[index][2];rotateByArbitraryVec([z, 0, -x], -dTheta);var tempAng = calPosAng(index);var degTheta = tempAng[0]/Math.PI*180;var degPhi = tempAng[1]/Math.PI*180;var alterPhi = tempAng[1] - oriPhi;//绕y轴旋转角度var yAng = (dPhi-alterPhi)/Math.PI*180;rotateByArbitraryVec([0, 1, 0], dPhi-alterPhi);tempAng = calPosAng(index);degTheta = tempAng[0]/Math.PI*180;degPhi = tempAng[1]/Math.PI*180;oriPhi = tempAng[1];// console.info("方位角[", oriTheta, ",", oriPhi, "]");printBalls();if (Math.abs(k - step) <= 0.001 ) {console.log('here');clearInterval(fastMoveId);}}})(index), 30)}复制代码

 注意,因为theta的变化会导致phi的变化,所以theta变化过程中导致phi变化的部分,我们再计算phi时得提前减出来。

写在最后:

 其实说起来这篇文章涉及的前端知识并不是很多,主要就是transform搭配perspective能够实现3D变化的效果,说白了就是加上了一个景深的效果,使你的图标看起来有种近大远小的感觉而是数学上的一些空间变化,深感自己高数没有学好啊,是时候复习一波高数的知识了。

简单的立体旋转特效DEMO实现相关推荐

  1. jquery环形3D立体旋转特效

    jquery环形3D立体旋转特效 作者/代码整理:站长素材  (转载请附加本文地址,带有"懒人原生"字样的谢绝转载)发布日期:2013-07-20 立体效果比较强的jquery特效 ...

  2. Android中实现简单的立体旋转

    新建RotateAnimationZ import android.graphics.Camera; import android.graphics.Matrix; import android.vi ...

  3. Windows Phone 7 立体旋转动画的实现

    Storyboard.TargetProperty表示获取或设置应进行动画处理的属性的名称.通过对Storyboard.TargetProperty属性的设置可以很简单地实现X轴.Y轴.Z轴的立体旋转 ...

  4. GDI+简单现实文字旋转

    原文 http://www.cnblogs.com/kaixiangbb/p/3301272.html 题记 入职新公司已快有两月了,试用期已快结束,项目却迟迟还未正式启动.安排给我的多是些琐事,一直 ...

  5. 从零开始打造一个Android 3D立体旋转容器

    本文地址,转载请注明 http://blog.csdn.net/mr_immortalz/article/details/51918560  github 代码下载地址 :https://github ...

  6. css3 3D旋转特效

    最近自己的网站正在制作,由于是个人网站,所以我傲娇的抛弃了IE,痛快的用起了css3和html5的诸多特效,然而问题亦随之而来.这篇文章的主要讲解在使用css3的3D旋转的时候遇到的文章,错误之处,还 ...

  7. 基于animation.css实现动画旋转特效

    分享一款基于animation.css实现动画旋转特效.这是一款基于CSS3实现的酷炫的动画旋转特效代码.效果图如下: 在线预览    源码下载 实现的代码. html代码: <div clas ...

  8. 2.CCGridAction(3D效果),3D反转特效,凸透镜特效,液体特效,3D翻页特效,水波纹特效,3D晃动的特效,扭曲旋转特效,波动特效,3D波动特效

     1 类图组织 2 实例 CCSprite * spr = CCSprite::create("HelloWorld.png"); spr->setPosition(cc ...

  9. HTML5火焰文字特效DEMO演示---转载

    只有google支持 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> &l ...

最新文章

  1. 一堂拯救千万股民的公开课,不能错过!
  2. TFS 无法签入或自动签出 解决方法 【强制撤销签出无效】
  3. nodejs实现webservice问题总结
  4. 斥资2亿加码新消费,“瓜子之王”洽洽要圆“坚果梦”?
  5. 初识 ASP.NET 3.5 MVC 开发
  6. URAL 1091. Tmutarakan Exams
  7. 用c语言读取和写入文件数据
  8. 使用Eclipse创建Web工程后未生成web.xml文件
  9. 诺基亚S40手机联系人导入安卓手机
  10. linux加密框架 crypto 算法管理 - 创建哈希算法实例
  11. python怎样创建桌面快捷方式_python创建桌面快捷方式的代码
  12. 今晚博文视点大咖直播伴你读No.2:人工智能学习路线
  13. Openstack的ping不通实例的解决办法
  14. 在Silverlight 5 项目中创建单元测试项目
  15. c语言 blue的大写l,C语言代码训练(一)
  16. 4.郝斌C语言笔记——基本的输入和输出函数的用法
  17. 学习能力篇:“拼图式”学习法
  18. 教资报名照片怎么弄成200k?照片大小怎么改到200k?
  19. Spark Streaming概述_大数据培训
  20. 奥哲低代码助力西子联合自主搭建航空QMS

热门文章

  1. 计算机写给未来自己的一段话,写给以后的自己一段话 致未来自己的唯美句子...
  2. 新手站长容易犯的3个WordPress安全错误
  3. OSG飞机姿态控制笔记
  4. Iphone键盘收齐页面空白问题
  5. fcpx插件:CrumplePop VideoDenoise(消除视频噪音插件)
  6. Socket通信(TCP协议)
  7. 湖北省大学程序设计竞赛(网络同步赛) D. Who killed Cock Robin
  8. java毕业设计师资管理系统源码+lw文档+mybatis+系统+mysql数据库+调试
  9. Git客户端 安装 和 使用 教程
  10. 西安邮电大学计算机课程表,西安邮电大学2013-2014-01学期课程表(45页)-原创力文档...