1.前言
我使用了jquery編寫互動的旋轉,因為初學所以不太熟練,還請見諒。樣式我是寫stylus編碼,自動生成css。
2.正文
話不多說,先上一張效果圖。
我將其分成三部分。第一部分是正方體部分(SVG
),第二部分是svg中的路徑動畫了(animateMotion
+ jQuery
),第三部分則是互動旋轉(jQuery
)。
1.正方體
做一個正方體
我的思路是用六塊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//放置六個svg平面旋轉平移後,可視作為立方體
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)
複製程式碼
第二種:先旋轉再平移。這個的特點就是與上面的相反了。(我使用的是這種)
兩種方式都能生成正方體。
兩種正方體生成的原理圖
第一種
平移後的狀況
第二種
旋轉後的狀況
以上就是兩種立方體的構成方法的概念了。
2. 路徑動畫
先上程式碼:
比較冗雜
<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
元素來做的。
首先每個svg
中圓點的路徑是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);
})();
})
複製程式碼
我的控制方式是全部由jQuery來控制,在animateMotion
元素中設定的起始時間begin
屬性為infinite
這是在頁面接在完後不會自己執行的。
我使用jQuery來控制動畫的開始。
首先 先獲取每個動畫元素
const animate = document.getElementsByTagName('animateMotion');
將每個動畫元素都標記好是什麼動畫。
接著 我在這使用了計時器setInterval
和setTimeout
來控制動畫。
用setInterval
來迴圈播放動畫,再每次迴圈中分別用setTimeout
來控制動畫的的先後順序。
每個setTimeout
計時器的延遲等於之前所有動畫的總時間,可以獲取元素的dur等方法獲取和設定,在這為圖方便設定了固定值。有興趣的可以設定一個動畫陣列,裡面按序新增animateMotion
動畫元素。每個setTimeout
計時器的延時設定為之前動畫的時間之和即可。
最後有兩個方法可以控制動畫的停止與繼續,是svg
內建的API
如果要使用這兩個API的話,最好將動畫的begin
值設定為上一個動畫.end
,begin
的值支援很多型別,詳情請看張鑫旭大佬的文章
www.zhangxinxu.com/wordpress/2…
// svg指當前svg DOM元素
// 暫停
svg.pauseAnimations();
// 重啟動
svg.unpauseAnimations()
複製程式碼
3.旋轉控制
旋轉控制是我還沒完善的地方,體驗不是十分好,還望大佬們幫我指出錯誤。另一個旋轉方案過一兩天再新增上來
先上程式碼:
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
的變數。
接著在立方體元素中新增mousedown()
事件,在mousedown的回撥函式中先記錄下滑鼠點選的位置 var mousemoveX = event.clientX;
並且在body
元素上新增mousemove()
事件因為是要在整個頁面上移動。
最後就是最重要的移動部分,先記錄滑鼠移動的位置 var mousemoveX = event.clientX;
然後計算滑鼠移動的距離var scaleY = ((mousemoveX - mousedownX) / 200);
這裡面200是可隨意更改的,因為滑鼠移動距離對於旋轉角度來說太大了所以要除以一個倍率可以自己來設定,其次是計算正方體對於初始的位置旋轉的多少角度 Y = ((Y + scaleY) % 360);
這裡要除以360做範圍限制,其實不新增也可以,rotate
屬性支援超過360度。接著就是設定旋轉角度了
$('.container').animate({}, function () {
$('.container').css({ 'transform': `rotateX(${X}deg) rotateY(${Y}deg)` });
})
複製程式碼
通過animate()
方法來將旋轉做動畫效果在裡邊通過css()
方法來設定旋轉的角度。
第二種方案: 第二種方案是不需要使用者點選,實時監聽滑鼠移動,滑鼠移動就會旋轉。我將旋轉方向判斷的基點設定為螢幕的中點。 上程式碼
var HalfX=window.innerWidth;
var HalfY=window.innerHeight;
var mousemoveX = null;
var mousemoveY = null;
window.addEventListener('resize',onchange);
function onchange(){
var HalfX=window.innerWidth;
var HalfY=window.innerHeight;
}
$('body').mousemove((event)=>{
mousemoveX = ((event.clientX - HalfX))%360;
mousemoveY = ((event.clientY - HalfY))%360;
$('.container').animate({},function(){
$('.container').css('transform',`rotateX(${mousemoveY}deg)rotateY(${mousemoveX}deg)`);
})
})
複製程式碼
方案的更改沒有太大的差別。只是觸發的事件不同和滑鼠移動距離計算的基點不同。
在第二個方案中我們不需要儲存上一次的旋轉的角度。因為我們相對的基點是絕對的,角度的更改不會有相對座標系的問題,每一個位置相對於立方體旋轉的角度是固定的,不過我們也需要記錄下來基點的值,當螢幕改變時我們的基點也需要改變onchange()
方法。
總結
最後將github庫連結放上來