实现效果:
代码实现:
<view class="page">
<view class="m-30" style="margin-top: 90rpx;">翻滚吧!立方体!</view>
<view class="board">
<!-- start 参考线 -->
<view style="position: absolute; font-size: 0; transform: translateZ(-72rpx);">
<view v-for="(item, index) in [,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,]" :key="index" style="width:144rpx; height: 144rpx; border: 1px solid #333333; box-sizing: border-box; display: inline-block;"></view>
</view>
<!-- end 参考线 -->
<view class="cube transition-anim" :style="animStyle">
<view class="bottom">下</view>
<view class="top">上</view>
<view class="left">左</view>
<view class="front">前</view>
<view class="back">后</view>
<view class="right">右</view>
</view>
</view>
<view class="flex-row m-30">
<button @click="left">左翻</button>
<button @click="right">右翻</button>
<button @click="top">上翻</button>
<button @click="bottom">下翻</button>
</view>
</view>
const ANIM_DURATION = 300; // 动画持续时间
var SIZE = 144;
var animBeginning; // 现在是否在执行动画中,为true即在执行动画时将拦截下个动画执行,直到ANIM_DURATION结束(ANIM_DURATION=0可达到不拦截的效果)
var rotate; // 三维旋转,x、y、z角度值,单位deg
var translate; // 二维位移,x、y位移值,单位rpx
export default {
data() {
return {
animStyle: 'transform: translate(0) rotate(0deg);',
}
},
onLoad() {
this.initData();
},
methods: {
initData() {
animBeginning = false;
rotate = { x: 0, y: 0, z: 0 };
translate = { x: 0, y: 0 };
},
left(e) {
if(animBeginning) {
return;
}
if(rotate.x % 180 == 0) {
rotate.y += rotate.x % 360 != 0 ? 90 : -90;
} else if(Math.abs(rotate.y % 360) == 180) {
rotate.z += rotate.x % 360 == 90 || rotate.x % 360 == -270 ? -90 : 90;
} else {
rotate.z += rotate.x % 360 == 90 || rotate.x % 360 == -270 ? 90 : -90;
}
translate.x -= SIZE;
this.startAnimation();
},
right(e) {
if(animBeginning) {
return;
}
if(rotate.x % 180 == 0) {
rotate.y += rotate.x % 360 != 0 ? -90 : 90;
} else if(Math.abs(rotate.y % 360) == 180) {
rotate.z += rotate.x % 360 == 90 || rotate.x % 360 == -270 ? 90 : -90;
} else {
rotate.z += rotate.x % 360 == 90 || rotate.x % 360 == -270 ? -90 : 90;
}
translate.x += SIZE;
this.startAnimation();
},
top(e) {
if(animBeginning) {
return;
}
if(rotate.y % 180 == 0) {
rotate.x += 90;
} else {
rotate.z += rotate.y % 360 == -90 || rotate.y % 360 == 270 ? -90 : 90;
}
translate.y -= SIZE;
this.startAnimation();
},
bottom(e) {
if(animBeginning) {
return;
}
if(rotate.y % 180 == 0) {
rotate.x -= 90;
} else {
rotate.z += rotate.y % 360 == -90 || rotate.y % 360 == 270 ? 90 : -90;
}
translate.y += SIZE;
this.startAnimation();
},
startAnimation() {
animBeginning = true;
this.animStyle = 'transform: translate(' + translate.x + 'rpx, ' + translate.y + 'rpx) rotateX(' + rotate.x + 'deg) rotateY(' + rotate.y + 'deg) rotateZ(' + rotate.z + 'deg);';
setTimeout(() => {
// console.log(translate, rotate);
animBeginning = false;
}, ANIM_DURATION);
}
}
}
.flex-row {
flex-direction: row;
display: flex;
position: relative;
}
.m-30 {
margin: 30rpx;
}
.page {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
}
.board {
transform-style: preserve-3d;
transform: rotateX(-30deg) rotateY(30deg) rotateZ(0deg);
flex: 1;
}
.cube {
transform-style: preserve-3d;
animation-fill-mode : forwards;
width: 144rpx;
height: 144rpx;
}
.cube view {
width: 144rpx;
height: 144rpx;
position: absolute;
/* opacity: 0.5; */
display: flex;
justify-content: center;
align-items: center;
}
.back {
background-color: #9900FF;
transform: rotateY(180deg) translateZ(72rpx);
}
.right {
background-color: #99CCCC;
transform: rotateY(90deg) translateZ(72rpx);
}
.left {
background-color: #00CC00;
transform: rotateY(-90deg) translateZ(72rpx);
}
.top {
background-color: #0099FF;
transform: rotateX(90deg) translateZ(72rpx);
}
.bottom {
background-color: #FFCC00;
transform: rotateX(-90deg) translateZ(72rpx);
}
.front {
background-color: #FF5757;
transform: rotateY(0deg) translateZ(72rpx);
}
/* 过渡动画 */
.transition-anim {
transition: all 0.3s;
/* Firefox 4 */
-moz-transition: all 0.3s;
/* Safari 和 Chrome */
-webkit-transition: all 0.3s;
/* Opera */
-o-transition: all 0.3s;
}