其实是依托Css3的功劳,先上一个例子

链接: https://pan.baidu.com/s/1cZ-mMI01FHO3u793ZhvF2w 提取码: d3s7
代码地址:链接: https://pan.baidu.com/s/1sldhljJ 密码: i6qh

这动画纵有万般变化,也离不开以下几个属性

  • transform (元素2D 3D转换)

    translate,3d,X,Y,Z (移动距离)
    scale,3d,X,Y,Z (缩放比例)
    rotate,3d,X,Y,Z (旋转角度)
    skew,X,Y (倾斜角度)

  • transform-origin (允许被转换元素位置)

    left center right length %

  • transform-style (被嵌套元素在3D空间中显示)

    flat (2d) presever-3d (3d)

  • perspective (3D元素透视效果 俗称"景深")

    number

  • perspective-origin (设置3D基数位置 x,y)

    top center right length %

  • backface-visibility (元素不面对屏幕是否可见)

    visible hidden


这里写一个变化的例子,帮助理解

以上例子只是单一的变化 如果多个变化一起执行 遵守 “慢写的先执行
比如:
原始图片

"translateX(150px) rotateY(180deg)": 先旋转再移动

"rotateY(180deg) translateX(150px)": 先移动再旋转

为什么两者只是前后顺序不同 结果却是相反的呢?
这就涉及到了 中心点的问题 transform-origin
transform-origin 变换原点 center center;

关键字: top bottom center left right;
具体的长度单位(em,rem,px...)

会受到原点影响的变换有:rotate、skew、scale
translate不受影响

第一个是先根据中心原点旋转180度 再向右移动150pxbr
第二个向右移动150px 中心点未改变 再旋转180deg

还有一点需要注意:

在js中没有办法 通过计算后样式 获取到 transform中的相关操作,只能获取到矩阵

getComputedStyle(XX)['transform'] 得到的是 matrix3d(...)

关于 transform的所有操作,通过封装cssTransform来进行操作,
在 cssTransform 中来记录 对transform的每一步操作,相当于对象赋值。获取的时候,就获取 cssTransform中的记录

function css(element, attr , val){// 通过判断 归纳transform 属性 直接跳到cssTramsform 剩下的直接常规方法处理if(attr == "rotate" || attr == "rotateX" || attr == "rotateY" ||attr == "rotateZ" || attr == "scale" || attr == "scaleX"|| attr == "scaleY" || attr == "skewX"|| attr == "skewY" || attr == "translateX"|| attr == "translateY" || attr == "translateZ" ){return cssTransform(element, attr, val);}if(arguments.length == 2){var val = getComputedStyle(element)[attr];if(attr=='opacity'){val = Math.round(val*100);}return parseFloat(val);} if(attr == "opacity") {element.style.opacity= val/100;} else {element.style[attr]= val + "px";    }
}
function cssTransform(el, attr, val) {if(!el.transform){el.transform = {}}// 如果val为空 为获取值if(typeof val == "undefined"){if(typeof el.transform[attr] == "undefined"){switch(attr) {case "scale":case "scaleX":case "scaleY":el.transform[attr] = 100;break;default:el.transform[attr] = 0;    }}return el.transform[attr];} else {// 设置值 原理就是对象的赋值var transformVal = "";el.transform[attr] = Number(val);for(var s in el.transform){switch(s){case "rotate":case "rotateX":case "rotateY":case "rotateZ":case "skewX":case "skewY":transformVal += " "+s+"("+el.transform[s]+"deg)";break;case "translateX":case "translateY":case "translateZ":transformVal += " "+s+"("+el.transform[s]+"px)";break;case "scale":case "scaleX":case "scaleY":transformVal += " "+s+"("+el.transform[s]/100+")";break;}}el.style.WebkitTransform = el.style.transform = transformVal;}
}

加下来介绍核心库:m.Tween.js运动函数
使用如下:

MTween({el: div, // 目标元素target: { // 期望最后变化的值scale: 200,translateX: 200,translateY: 200,rotate: 360},time: 1000, // 动画执行时间type: "backOut", // 动画特效 贝塞尔曲线callBack: function(){ // 动画执行结束的回调console.log("动画执行完了");},callIn: function(){ // 动画执行过程的回调console.log("动画执行中");}
})

实现的代码也很简单

function MTween(init){var t = 0;var b = {};var c = {};var d = init.time / 20;for(var s in init.target){ b[s] = css(init.el, s); c[s] = init.target[s] - b[s];}clearInterval(init.el.timer); init.el.timer = setInterval(function(){t++;if(t>d){clearInterval(init.el.timer);init.callBack&&init.callBack.call(init.el);} else {init.callIn&&init.callIn.call(init.el);for(var s in b){var val = (Tween[init.type](t,b[s],c[s],d)).toFixed(2);css(init.el, s, val);}}},20);
}

以上只是基础知识,为下面的教程铺垫

正文开始:

1、安踏图标转动,来回变化,消失

2、碎片,云朵不规则圆柱转动

3、主体,浮层 圆柱形滚动入场

4、移动事件,陀螺仪,横竖屏事件

// 整体Html结构
<div id="pageBg"></div>
<div id="view"><div id="logo1"><div class="logoImg"><img src="load/logo.png"></div><p class="logoText">已加载 0%</p></div><div id="main"><div id="tZ"><div id="panoBg"></div><div id="cloud"></div><div id="pano"></div></div></div>
</div>

1、安踏图标转动,来回变化,消失

分析: 安踏图标有三个 分别为 logo1 logo2 logo3 (logo2 logo3 为动态生成,并提前赋值属性,加上360度旋转动画)
logo1 使用css3动画animation 360度转动 1s后透明度为0 并删除
logo2 由 translateZ : -1000 经过300ms 变为0 向前移动;接着经过800ms 变为-1000 向后移动
logo3 在logo2 删除后出现 由远到近 再接着消失

其实代码很简单 就是用下面的模型代码实现

MTween({el: logo1,target: {opacity: 0 // 将要最终变化的值
    },time: 1000,type: 'easeOut',callBack: function() { // 运动结束的执行动作
      view.removeChild(logo1)css(logo2, 'opacity', 100) // 显示logo2// 接下来做logo2动作 以此类推
      MTween({el: logo2,target: {translateZ: 0},time: 300,type: 'easeBoth',callBack: anmt2 })}})

2、碎片,云朵不规则圆柱转动

分析:将9张碎片图片乘3 然后设置随机的 rotateY rotateX translateZ translateY 变成一个随机圆柱排布,然后在碎片的主层加上 rotateY 旋转动画,再用动画控制translateZ 向后移动
祥云入场: 利用 sin cos R 计算translateX translateZ,然后在云层主层加上 rotateY 旋转动画,再用动画控制translateZ 向后移动

碎片代码

//基础框架版本 排成一圈
for (var i = 0; i < 27; i++) {var R = 10 + Math.round(Math.random()*240);var deg =  Math.round(Math.random()*360)css(span, 'rotateY', deg)css(span, 'translateZ', R)
}
// 添加上下分布
css(logo4, "translateZ", -2000)
css(logo4, "scale", 0)
for (var i = 0; i < 27; i++) {var xR = 20 + Math.round(Math.random() * 240) // 圆柱碎片的X半径var xDeg = Math.round(Math.random() * 360)var yR = 10 + Math.round(Math.random() * 240) // 圆柱碎片的Y半径var yDeg = Math.round(Math.random() * 360)css(span, "rotateY", xDeg);css(span, "translateZ", xR);css(span, "rotateX", yDeg);css(span, "translateY", yR)
}
// 从远到近的移动
MTween({el: logo4,target: {translateZ: 0,scale: 100},time: 500,type: "easeOutStrong",callBack: function() {setTimeout(function() { //从近到远
        MTween({el: logo4,target: {translateZ: -1000,scale: 20},...})

祥云代码
这里需要每一片云朵都面对我们自己

这里知道每一个R deg,便能求得x, z
x = Math.sin(deg * Math.PI / 180) * R
z = Math.cos(deg * Math.PI / 180) * R

  var span = document.createElement("span");span.style.backgroundImage = 'url(' + imgData.cloud[i % 3] + ')';var R = 200 + (Math.random() * 150) // 设置随机半径var deg = (360 / 9) * i // 圆柱各个角度var x = Math.sin(deg * Math.PI / 180) * R // sin求得Xvar z = Math.cos(deg * Math.PI / 180) * R // cos求得Zvar y = (Math.random() - .5) * 200 // 上下分布css(span, "translateX", x)css(span, "translateZ", z)css(span, "translateY", y)...// 设置动画
    MTween({el: cloud,target: {rotateY: 540},time: 3500,type: "easeIn",callIn: function() { // 这里需要用到运动过程的回调 将祥云外层的角度赋予内层祥云的每个角度var deg = -css(cloud, "rotateY");for (var i = 0; i < cloud.children.length; i++) {css(cloud.children[i], "rotateY", deg);}}})

3、主体,浮层 圆柱形滚动入场


这里的图片是由20张分割好的宽129px的图片组成

每张图片的角度deg为360/20,这样就能得到中心点距离每张图片的距离,利用数学的tan公式 R = (width / 2) / Math.tan((deg/ 2 )* Math.PI / 180)

var panoBg = document.querySelector('#panoBg')
var width = 129 // 一张图片宽度
var deg = 360 / imgData.bg.length // 圆柱图片角度
var R = parseInt((width / 2) / Math.tan((deg/ 2 )* Math.PI / 180) - 1) // tan@ = 对边(R) / 临边(W/2)
var startDeg = 180; // 开始角度
for (var i = 0; i < imgData.bg.length; i++) {var span = document.createElement("span");css(span, 'rotateY', startDeg)css(span, 'translateZ', -R)span.style.backgroundImage = "url(" + imgData.bg[i] + ")";panoBg.appendChild(span);startDeg -= deg // 每张图片角度递减
}

设置主体从远到近 类似画轴显示出来,在span初始化时候都设置display="none",然后设置定时器依次打开

var timer = setInterval(function() {panoBg.children[num].style.display = "block";num++if (num >= panoBg.children.length) {clearInterval(timer)}
}, 3600 / 2 / 20)

设置漂浮层
漂浮层相对简单一些,动态创建漂浮层,设置初始translateX translateZ,遍历对应的浮层,设置上面求得的半径距离,角度

  var pano = document.querySelector('#pano'); // 浮层容器var deg = 18; // 差值角度var R = 406; // 上图计算的Rvar nub = 0; // 计数var startDeg = 180; // 初始角度 css(pano, "rotateX", 0);css(pano, "rotateY", -180);css(pano, "scale", 0);var pano1 = document.createElement("div");pano1.className = "pano";css(pano1, "translateX", 1.564);css(pano1, "translateZ", -9.877);for (var i = 0; i < 2; i++) {var span = document.createElement("span");span.style.cssText = "height:344px;margin-top:-172px;";span.style.background = "url(" + imgData["pano"][nub] + ")";css(span, "translateY", -163); // 设定固定的值css(span, "rotateY", startDeg); // 角度逐级递减css(span, "translateZ", -R);nub++;startDeg -= deg;pano1.appendChild(span)}var pano2 = document.createElement("div");pano2.className = "pano";css(pano2, "translateX", 20.225);css(pano2, "translateZ", -14.695);for (var i = 0; i < 3; i++) {var span = document.createElement("span");span.style.cssText = "height:326px;margin-top:-163px;";span.style.background = "url(" + imgData["pano"][nub] + ")";css(span, "translateY", 278);css(span, "rotateY", startDeg);css(span, "translateZ", -R);nub++;startDeg -= deg;pano2.appendChild(span)}

4、移动事件,陀螺仪,横竖屏事件


移动事件需要监听三个事件touchstart touchmove touchend
初始化 按下的点startPoint, 主层角度panoBgDeg, 移动一度变化多少px的系数scale,主层深度startZ,最后角度lastDeg,最后差距lastDis

手指按下 touchstart

 document.addEventListener('touchstart', function(e) {startPoint.x = e.changedTouches[0].pageX //手指初始位置startPoint.y = e.changedTouches[0].pageY //
    panoBgDeg.x = css(panoBg, 'rotateY') //主体容器左右移动 rotateY便是X轴panoBgDeg.y = css(panoBg, 'rotateX')})

手指移动 touchmove

document.addEventListener('touchmove', function(e) {var nowDeg = {}var nowDeg2 = {} // 悬浮层也需要移动var nowPoint = {}nowPoint.x = e.changedTouches[0].pageX; //变化的位置nowPoint.y = e.changedTouches[0].pageY;var dis = {}dis.x = nowPoint.x - startPoint.x // 移动的距离Xdis.y = nowPoint.y - startPoint.yvar disDeg = {}disDeg.x = -(dis.x / scale.x) // 距离转度数 disDeg.y = dis.y / scale.ynowDeg.y = panoBgDeg.y + disDeg.y // 开始角度 + 移动角度nowDeg.x = panoBgDeg.x + disDeg.xnowDeg2.x = panoBgDeg.x + (disDeg.x) * 0.95 // 浮层的稍微偏动nowDeg2.y = panoBgDeg.y + (disDeg.y) * 0.95if (nowDeg.y > 45) {nowDeg.y = 45} else if (nowDeg.y < -45) {nowDeg.y = -45}if (nowDeg2.y > 45) {nowDeg2.y = 45} else if (nowDeg2.y < -45) {nowDeg2.y = -45}lastDis.x = nowDeg.x - lastDeg.x //进行差距计算lastDeg.x = nowDeg.xlastDis.y = nowDeg.y - lastDeg.ylastDeg.y = nowDeg.ycss(panoBg, "rotateX", nowDeg.y); // 进行主体角度赋值css(panoBg, "rotateY", nowDeg.x);css(pano, "rotateX", nowDeg2.y); // 悬浮层角度css(pano, "rotateY", nowDeg2.x);var disZ = Math.max(Math.abs(dis.x), Math.abs(dis.y))if (disZ > 300) {disZ = 300}css(tZ, 'translateZ', startZ - disZ) // 控制拖拉远近距离})

手指抬起 touchend

document.addEventListener('touchend', function(e) {var nowDeg = {x: css(panoBg, "rotateY"), // 获取结束角度y: css(panoBg, "rotateX")};var disDeg = {x: lastDis.x * 10, // y: lastDis.y * 10}MTween({el: tZ,target: {translateZ: startZ // 移动后回来 变近
      },time: 700,type: "easeOut"})MTween({el: panoBg,target: {rotateY: nowDeg.x + disDeg.x // 主体缓冲
      },time: 800,type: "easeOut"})MTween({el: pano,target: {rotateY: nowDeg.x + disDeg.x // 悬浮层缓冲
      },time: 800,type: "easeOut",callBack: function() {window.isTouch = falsewindow.isStart = false}})})
}

设置景深随不同屏幕适配进行调整

function setPerc() {resteview()window.onresize = resteviewfunction resteview() {var view = document.querySelector('#view') // 最外层var main = document.querySelector('#main')var deg = 52.5var height = document.documentElement.clientHeight;var R = Math.round(Math.tan(deg / 180 * Math.PI) * height * .5);view.style.WebkitPerspective = view.style.perspective = R + "px"; // 设置景深css(main, 'translateZ', R)}
}

陀螺仪 横竖屏事件

陀螺仪基础

 window.addEventListener('deviceorientation', function(e) {e.beta // 左右e.gamma // 上下
})

横竖屏基础

 window.addEventListener('orientationchange', function(e) {window.orientation // 0 90 -90 180 代表四个方向
})

这里需要解决触摸事件的冲突,需要定义一个全局的isTouch判断,遇到触摸就终止陀螺仪事件引起的变化。
同时需要注意横竖屏会把陀螺仪的beta gamma 改变

 dir = window.orientationswitch (dir) {case 0:x = e.beta;y = e.gamma;break;case 90:x = e.gamma;y = e.beta;break;case -90:x = -e.gamma;y = -e.beta;break;case 180:x = -e.beta;y = -e.gamma;break;}

开始倾斜时,记录开始的陀螺仪位置,主体层的位置。
移动时候和触摸一样进行距离差值计算,并进行相加赋予主体层的变化。然后进行远近动画,主体移动动画,悬浮层动画。

 var nowTime = Date.now()// 检测陀螺仪 转动时间 与插件的20ms 兼容if (nowTime - lastTime < 30) {return}lastTime = nowTime// 角度倾斜if (!isStart) {//startisStart = true;start.x = xstart.y = ystartEl.x = css(pano, 'rotateX')startEl.y = css(pano, 'rotateY')} else {// movenow.x = xnow.y = yvar dis = {}dis.x = now.x - start.xdis.y = now.y - start.yvar deg = {}deg.x = startEl.x + dis.xdeg.y = startEl.y + dis.yif (deg.x > 45) {deg.x = 45;} else if (deg.x < -45) {deg.x = -45;}var disXZ = Math.abs(Math.round((deg.x - css(pano, 'rotateX')) * scale))var disYZ = Math.abs(Math.round((deg.y - css(pano, "rotateY")) * scale))var disZ = Math.max(disXZ, disYZ)if (disZ > 300) {disZ = 300}MTween({el: tZ,target: {translateZ: startZ - disZ},time: 300,type: 'easeOut',callBack: function(){MTween({el:tZ,target:{translateZ: startZ // 进行缓冲动画
            },time: 400,type: "easeOut"})}})MTween({el: pano,target: {rotateX: deg.x,rotateY: deg.y},time: 800,type: 'easeOut'})MTween({el: panoBg,target: {rotateX: deg.x,rotateY: deg.y},time: 800,type: 'easeOut'})

以上便是主要代码,最好自己运行调试下,运用好动画函数,理解每一个步骤。
前端实现3D VR 还有更牛的Three.js, A-Frame。继续深究
该课程是由[妙味课堂]提供的,可以从基础开始学习。

转载于:https://www.cnblogs.com/QRL909109/p/7041898.html

前端的3D(css3版本)--淘宝造物节3D创景的制作相关推荐

  1. 前端:移动端“淘宝造物节” 3D绚酷空间 VR 场景

    前端:移动端"淘宝造物节" 3D绚酷空间 VR 场景 资源分享,第六节之后的视频资源没找到,下面附效果图.资源.源码地址. 链接:https://pan.baidu.com/s/1 ...

  2. 解读:为何在今年的淘宝造物节上!AR直播火到如此程度?

    2017年淘宝造物节来了,作为淘宝最好玩.最盛大的线下狂欢节日,3000平米的场馆里不仅空气里都是有趣的因子,随时随地与各种脑洞大开的创意相遇,更依靠技术上的新突破和内容创作上新模式,鼓励年轻人创造出 ...

  3. 看完它你就造了!为什么AR直播是淘宝造物节最火的黑科技

    2017年淘宝造物节来了,作为淘宝最好玩.最盛大的线下狂欢节日,3000平米的场馆里不仅空气里都是有趣的因子,随时随地与各种脑洞大开的创意相遇,更依靠技术上的新突破和内容创作上新模式,鼓励年轻人创造出 ...

  4. 再遇淘宝造物节,一个90后的“奇市江湖”

    天生万物,淘宝造节.因为如双11一般有着塑造品牌的使命,新一届的淘宝造物节如期而至.当然,跟双11的全民狂欢不同,淘宝造物节的受众目标更加明确--9095一代 ,这群人的人生与"造" ...

  5. 偶遇“奇市江湖”,淘宝造物节的freestyle你有吗?

    天生万物,淘宝造节.因为如双11一般有着塑造品牌的使命,新一届的淘宝造物节如期而至.当然,跟双11的全民狂欢不同,淘宝造物节的受众目标更加明确--9095一代 ,这群人的人生与"造" ...

  6. 中秋快乐!旷视喊你来淘宝造物节凹最炫的AI造型

    9月13日,中秋来临,一年一度的淘宝造物节也终于来啦!在充满硬核工业风的杭州锅炉厂,不仅有人造肉."淘宝打手".火箭餐厅等超嗨玩法,更有最 IN.最炫的 AI 黑科技登场! 准备好 ...

  7. 【欢迎反馈建议】淘宝造物节意犹未尽的你,快来看看阿里四位专家畅聊背后的VR技术!...

    VR是云栖社区视频品牌<云栖说>首期内容,已经观看"阿里GM Lab.优酷VR .阿里云,畅谈VR造物神"的朋友们,希望能得到大家的反馈. 观看报名链接:8月10日, ...

  8. 淘宝造物节2017十大神店:一言不合自己造!

    买不到心仪球鞋,一言不合自己造,上海男生罗汉干脆在淘宝上做起手绘球鞋: 做正经的书法家太枯燥,一言不合自己造,朱敬一用"段子书法"杀出一片天地: 如今木匠们重款式而轻工艺,一言不合 ...

  9. 人造物,淘宝造节,造物节造江湖

    天猫有双11,淘宝有什么?除了双12,还有造物节. 前些天,阿里巴巴集团董事局主席马云突然出现在淘宝造物节的团队项目室,他表示造物节是淘宝品牌重塑的重要一环,作为一个重要的IP,造物节是一个让你&qu ...

  10. css3 3d关键点 淘宝购物节的3D效果 如何实现的 h5

    规定被嵌套元素如何在 3D 空间中显示 transform-style: preserve-3d; 视距:(通过视距可以实现近大远小的视觉效果) 这个属性也是规定 3D 元素的透视效果 -webkit ...

最新文章

  1. ORM查询语言(OQL)简介--概念篇
  2. 谈中型项目下的编码技巧二
  3. Warning: Permanently added the RSA host key for IP address '13.229.188.59' to the list of known host
  4. 100万并发连接服务器笔记之处理端口数量受限问题
  5. C#LeetCode刷题之#83-删除排序链表中的重复元素(Remove Duplicates from Sorted List)
  6. c 初始化char**_C/C++内存模型
  7. python抽签代码_Python(抽签问题),python实例,
  8. Java 泛型中的PECS原则
  9. hdoj--5621--KK's Point(简单数学)
  10. qt geomery的单位是什么_生命吗哪QT灵修11月28日
  11. Nature封面:基因突变才是衰老的罪魁祸首?体细胞突变越快,寿命越短
  12. 上海市提取公积金攻略
  13. [NOI2008] 糖果雨
  14. 世界读书日送你畅销好书!前所未有4折购书福利
  15. 一个完整的NES模拟器
  16. mysql全文索引与停止词
  17. STM32 HAL WS2812B PWM+DMA控制
  18. 计算机品牌企业关系图,AJ71C24-S3 画出PLC的I/O点与输入/输出设备的连接图或对应关系表, 三菱AJ71C24-S3 - 广州凌控...
  19. google官方图标 Material icons 全图标一览
  20. idea下maven下载依赖ssl错误

热门文章

  1. ReactNative仿支付宝付款密码输入框
  2. ubuntu18打开网页慢的问题
  3. w10运行游戏计算机中丢失xinput1-3.dll,Win10系统提示丢失xinput13dll的解决方法
  4. Python爬取王一博高清图片,竟然如此简单!
  5. LSF的使用方法总结
  6. 渗透测试技巧总结更新篇2
  7. 自制 12306 抢票工具 5秒内完成订票
  8. AngularJs $http.post 数据后台获取不到数据问题 的解决过程
  9. 长春公办计算机学校有哪些条件,长春小学初中哪些学校民办?哪些公办?最新一览表来也!...
  10. 系统检测,是否引证:否