Dickzeng 2019-07-01
我使用了jquery编写交互的旋转,因为初学所以不太承受,还请见谅。样式我是写stylus编码,自动生成css。
先放上一张效果图
我将其分成三部分。第一部分是正方体部分(SVG
),第二部分是SVG
中的路径动画了(animateMotion
+ jQuery
),第三部分则是交互旋转(jQuery
)。
做一个正方体
我的思路是用六块SVG
正方形画板通过css
属性旋转和平移来构成正方体。
html
代码:
<div class="page"> <div class="state"> <!-- 定位--> <div class="container"> <!--旋转--> <!-- 前 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="front"> <rect class="rect"></rect> </svg> <!-- 后 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="behind"> <rect class="rect "></rect> </svg> <!-- 左 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="left"> <rect class="rect "></rect> </svg> <!-- 右 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="right"> <rect class="rect "></rect> </svg> <!-- 上 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="top"> <rect class="rect "></rect> </svg> <!-- 下 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="bottom"> <rect class="rect "></rect> </svg> </div> </div> </div>
stylus
代码
html margin 0 padding 0 height 100% width 100% body height 100% width 100% .page .state height 300px width 300px position absolute top 50% left 50% transform translate(-50%,-50%)//修正位置 .container transform-style preserve-3d position relative height 300px width 300px transform rotateX(0deg) rotateY(0deg) svg position absolute height 300px width 300px stroke-width 5px stroke brown fill transparent //填充用透明 .rect height 300px width 300px .top transform rotateX(90deg) translateZ(150px) .bottom transform rotateX(-90deg) translateZ(150px) .left transform rotateY(-90deg) translateZ(150px) .right transform rotateY(90deg) translateZ(150px) .front transform rotateY(0deg) translateZ(150px) .behind transform rotateY(180deg) translateZ(150px)
通常有两种方式来构建一个立方体
第一种:先平移再旋转。优点是不许太强的空间构造能力,写起来比较简单。缺点就是:代码数会多一些,需要在平移后设置旋转基点。替换一下样式就行。
.top fill blue transform-origin: bottom transform translateY(-200px) rotateX(90deg) .bottom fill red transform-origin:top transform translateY(200px) rotateX(-90deg) .left fill green transform-origin: right transform translateX(-200px) rotateY(-90deg) .right fill black transform-origin: left transform translateX(200px) rotateY(90deg) .front fill grey transform translateZ() .behind fill pink transform translateZ(-200px)
第二种:先旋转再平移。这个的特点就是与上面的相反了。(我使用的是这种)
两种生成立方体的原理看下图
第一种
第二种
以上就是两种构建立方体的方法大致原理了
路径动画我是通过在相邻两个面上一个动画结束的位置和下一个动画起始位置重合,
下一个动画起始的延时设置为之前所有动画的动画时间,做到视觉认为路线是连接起来的。
先上代码:
<div class="state"> <!-- 定位--> <div class="container"> <!--旋转--> <!-- 前 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="front"> <rect class="rect"></rect> <circle x="0" y="0" r="5" stroke="none" fill="red"> <animateMOtion dur="3s" begin="infinite"> <mpath xlink:href="#frontY"></mpath> </animateMOtion> </circle> <circle x="0" y="0" r="5" stroke="none" fill="blue"> <animateMOtion dur="3s" begin="infinite"> <mpath xlink:href="#frontX"></mpath> </animateMOtion> </circle> <path fill="#fff" stroke="#000" stroke-width="1.5" stroke-opacity="null" fill-opacity="null" opacity="0.5" d="m150.75,0.75l0,300" id="frontY" /> <path fill="#fff" stroke="#000" stroke-width="1.5" stroke-opacity="null" fill-opacity="null" opacity="0.5" d="m300,150c-300,0 -300,0 -300,0" id="frontX" /> </svg> <!-- 后 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="behind"> <rect class="rect "></rect> <circle x="0" y="0" r="5" stroke="none" fill="red"> <animateMOtion dur="3s" begin="infinite"> <mpath xlink:href="#behindY"></mpath> </animateMOtion> </circle> <circle x="0" y="0" r="5" stroke="none" fill="blue"> <animateMOtion dur="3s" begin="infinite"> <mpath xlink:href="#behindX"></mpath> </animateMOtion> </circle> <path fill="#fff" stroke="#000" stroke-width="1.5" stroke-opacity="null" fill-opacity="null" opacity="0.5" d="m150,300c0,0 1,-300 0,-300" id="behindY" /> <path fill="#fff" stroke="#000" stroke-width="1.5" stroke-opacity="null" fill-opacity="null" opacity="0.5" d="m300,150c-300,0 -300,0 -300,0" id="behindX" /> </svg> <!-- 左 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="left"> <rect class="rect "></rect> <circle x="0" y="0" r="5" stroke="none" fill="blue"> <animateMOtion dur="3s" begin="infinite"> <mpath xlink:href="#leftX"></mpath> </animateMOtion> </circle> <path fill="#fff" stroke="#000" stroke-width="1.5" stroke-opacity="null" fill-opacity="null" opacity="0.5" d="m300,150c-300,0 -300,0 -300,0" id="leftX" /> </svg> <!-- 右 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="right"> <rect class="rect "></rect> <circle x="0" y="0" r="5" stroke="none" fill="blue"> <animateMOtion dur="3s" begin="infinite"> <mpath xlink:href="#rightX"></mpath> </animateMOtion> </circle> <path fill="#fff" stroke="#000" stroke-width="1.5" stroke-opacity="null" fill-opacity="null" opacity="0.5" d="m300,150c-300,0 -300,0 -300,0" id="rightX" /> </svg> <!-- 上 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="top"> <rect class="rect "></rect> <circle x="0" y="0" r="5" stroke="none" fill="red"> <animateMOtion dur="3s" begin="infinite"> <mpath xlink:href="#topY"></mpath> </animateMOtion> </circle> <path fill="#fff" stroke="#000" stroke-width="1.5" stroke-opacity="null" fill-opacity="null" opacity="0.5" d="m150.75,0.75l0,300" id="topY" /> </svg> <!-- 下 --> <svg xmlns="http://www.w3/org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="bottom"> <rect class="rect "></rect> <circle x="0" y="0" r="5" stroke="none" fill="red"> <animateMOtion dur="3s" begin="infinite"> <mpath xlink:href="#bottomY"></mpath> </animateMOtion> </circle> <path fill="#fff" stroke="#000" stroke-width="1.5" stroke-opacity="null" fill-opacity="null" opacity="0.5" d="m150.75,0.75l0,300" id="bottomY" /> </svg> </div> </div>
路径动画是有animateMotion
元素做的所以可以不是直线,只是为了方便设计,我吧path
路径设计成了直线
有兴趣的可以自己设计一下。
路径是我用 Method Draw 画的 editor.method.ac/
接下来就是重点的路径控制了
$(document).ready(function () { const animate = document.getElementsByTagName('animateMotion'); // 0 frontY 1 frontX 2 behindY 3 behindX 4 leftX 5 rightX 6 topY 7 bottomY var frontY = animate[0], frontX = animate[1], behindY = animate[2], behindX = animate[3], leftX = animate[4], rightX = animate[5], topY = animate[6], bottomY = animate[7]; // Y面球体运动 (() => { //先执行一次 frontY.beginElement(); setTimeout(() => { bottomY.beginElement(); }, 3000); setTimeout(() => { behindY.beginElement(); }, 6000); setTimeout(() => { topY.beginElement(); }, 9000); // 循环执行动画 var time = setInterval(() => { frontY.beginElement(); setTimeout(() => { bottomY.beginElement(); }, 3000); setTimeout(() => { behindY.beginElement(); }, 6000); setTimeout(() => { topY.beginElement(); }, 9000); }, 12000); })(); // X面球体运动 (() => { //先执行一次 frontX.beginElement(); setTimeout(() => { leftX.beginElement(); }, 3000); setTimeout(() => { behindX.beginElement(); }, 6000); setTimeout(() => { rightX.beginElement(); }, 9000); // 循环执行动画 var time = setInterval(() => { frontX.beginElement(); setTimeout(() => { leftX.beginElement(); }, 3000); setTimeout(() => { behindX.beginElement(); }, 6000); setTimeout(() => { rightX.beginElement(); }, 9000); }, 12000); })(); })
我设置的是让animateMotion元素的起始值begin
为infinite
,在页面加载完毕后动画不会自动执行。
我用jQuery来控制动画的执行顺序。
首先获取每个动画元素const animate = document.getElementsByTagName('animateMotion');
,之后将每个动画元素都标记好。
接着使用计时器setInterval
和setTimeout
计时器来控制动画。首先使用setInterval
来控制动画循环执行。接着在setInterval
中用setTimeout
设置动画执行的顺序,每个setTimeout
计时器的延迟都为之前所有动画时间之和。模块化的最好先设置一个动画对象数组,然后将动画顺序加入数组。
最后附加两个API
// svg指当前svg DOM元素 // 暂停 svg.pauseAnimations(); // 重启动 svg.unpauseAnimations()
两个API可已暂停当前动画,关于详情请看张鑫旭大佬的文章鑫空间
旋转控制是我还没完善的地方,用户体验不是十分好,还望大佬们帮我指出错误。另一个旋转方案过一两天再添加上来。
代码:
var X = 0;//记录X轴旋转过的角度 var Y = 0;//记录Y轴旋转过的角度 // 旋转控制 $('.container').mousedown(function (event) { var mousedownX = event.clientX; var mousedownY = event.clientY; $('body').mousemove(function (event) { var mousemoveX = event.clientX; var mousemoveY = event.clientY; var scaleY = ((mousemoveX - mousedownX) / 200); var scaleX = ((mousemoveY - mousedownY) / 200); Y = ((Y + scaleY) % 360); X = ((X + scaleX) % 360); $('.container').animate({}, function () { $('.container').css({ 'transform': `rotateX(${X}deg) rotateY(${Y}deg)` }); }) }) }) $('body').mouseup(function () { $('body').unbind('mousemove'); $('body').unbind('mousedown'); })
首先设置两个变量X,Y这是记录这个立方体真正旋转了多少角度。
然后在立方体.container
元素上添加鼠标点击事件mousedown
事件,在回调函数中做处理内容。回调函数中先记录下来鼠标点击的位置。
接着在body
上添加鼠标移动mousemove
事件因为我们移动不能只在立方体上,移动的范围要扩大到页面上。接着在移动事件中的回调函数做旋转处理,首先记录下来移动后鼠标的位置,计算出鼠标移动的距离然后除以一个数var scaleY = ((mousemoveX - mousedownX) / 200);
注意其中的200没有真正代表的意义,只是因为鼠标移动的距离数值相对于旋转的角度来说比较大,所以除以200减少它移动的角度,可以随意更改,其中鼠标在X轴上移动用户需要的是图形在以Y轴旋转。接着我么要计算立方体真正对于初始旋转了多少角度我们最好做一下范围限制(取模运算),Y = ((Y + scaleY) % 360);
最后就是设置属性了,先用$('.container').animate()
让旋转用动画的形式来做成,然后设置旋转角度$('.container').css({ 'transform':
rotateX(${X}deg) rotateY(${Y}deg) });
用css()
方法来设置旋转角度。
最后将github库放上来
github库