先上图:

实现过程:
1、长方体六个面
before,after, top ,buttom,left,right
2、每个面不同旋转角度
before: 向前移动一半width宽度,transform: translateZ(${width/2}px)
after: 向后移动一半width宽度、y轴旋转180度 transform: `translateZ(${-width/2}px) rotateY(180deg)`
top: 以top为轴,x轴旋转90度,向后移动一半宽度 transform-origin: top; transform: `rotateX(90deg) translate(0, ${-width/2}px)`}
bottom: 以bottom为轴,x轴旋90度,向后移动一半宽度, 放缩width/height,width: `${width}px`, height: `${height}px`,transform: `rotateX(90deg) translate(0, ${-width/2}px) scale(0,width/height)`
left: 以left为轴,y轴旋转-90度,向左移动一半宽度,{transform: `rotateY(-90deg) translate(${-width/2}px, 0)`}
right: 以right为轴,y轴旋转90度,向右移动一半宽度,{transform: `rotateY(90deg) translate(${width/2}px, 0)`}
3、在外圈div做旋转
4、代码:box.vue
<template>
<div class="box-wrap">
<div class="box" id="box" style="transform: rotateX(0deg) rotateY(0deg);" :style="{width: `${width}px`, height: `${height}px`}" @mousedown="mousedown" @mousemove="mousemove" @mouseup="mouseup" @mouseout="mouseup">
<div id="face-before" class="face face-before" :style="{transform: `translateZ(${width/2}px)`}">
<slot name="before" ></slot>
</div>
<div id="face-after" class="face face-after" :style="{transform: `translateZ(${-width/2}px) rotateY(180deg)`}">
<slot name="after"></slot>
</div>
<div id="face-top" class="face face-top" :style="{width: `${width}px`, height: `${width}px`, transform: `rotateX(90deg) translate(0, ${-width/2}px)`}">
<slot name="top"></slot>
</div>
<div id="face-bottom" class="face face-bottom" :style="{width: `${width}px`, height: `${height}px`,transform: `rotateX(90deg) translate(0, ${width/2}px) scale(0,width/height)`}">
<slot name="buttom"></slot>
</div>
<div id="face-left" class="face face-left" :style="{transform: `rotateY(-90deg) translate(${-width/2}px, 0)`}" >
<slot name="left"></slot>
</div>
<div id="face-right" class="face face-right" :style="{transform: `rotateY(90deg) translate(${width/2}px, 0)`}">
<slot name="right"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'box',
data() {
return {
canMove: false,
startX: 0,
startY: 0,
currentDeg: 0,
}
},
props: {
width: {
type: Number,
default: 300,
},
height: {
type: Number,
default: 300,
},
},
methods: {
// mouseenter(ev) { // 按下鼠标
// this.canMove = true;
// this.startX = ev.clientX;
// this.startY = ev.clientY;
// },
mousedown(ev) { // 按下鼠标
this.canMove = true;
this.startX = ev.clientX;
this.startY = ev.clientY;
},
mousemove(ev) { // 鼠标移动
if(this.canMove) {
const $box = document.getElementById('box');
const obj = this.getTransform();
const x = this.startX - ev.clientX;
const y = this.startY - ev.clientY;
const newx = (obj.x - (y / this.height * 180));
const newy = (obj.y - (x / this.width * 180));
console.log('x / this.width * 90===', x / this.width * 180);
console.log('newy===', newy);
this.startX = ev.clientX;
this.startY = ev.clientY;
$box.style.transform = `rotateX(-5deg) rotateY(${newy}deg)`;
this.currentDeg = newy;
}
},
mouseout() { // 鼠标离开
if(this.canMove) {
const $box = document.getElementById('box');
const value = this.currentDeg % 90;
if(Math.abs(value) >= 45) {
$box.style.transform = `rotateX(0deg) rotateY(${this.currentDeg + (90 - value) + 0.01}deg)`;
}else {
$box.style.transform = `rotateX(0deg) rotateY(${this.currentDeg - value + 0.01}deg)`;
}
this.canMove = false;
}
},
showAuto() {
const $box = document.getElementById('box');
$box.style['transform-origin'] = 'center';
$box.style.transform = `rotateX(-20deg) rotateY(20deg)`;
},
showTop() {
const $box = document.getElementById('box');
$box.style['transform-origin'] = 'center';
$box.style.transform = `rotateX(-89.999deg) rotateY(0deg)`;
},
showLeft() {
const $box = document.getElementById('box');
$box.style['transform-origin'] = 'center';
$box.style.transform = `rotateX(0deg) rotateY(89.999deg)`;
},
mouseup() { // 鼠标离开
if(this.canMove) {
const $box = document.getElementById('box');
const value = this.currentDeg % 90;
if(Math.abs(value) >= 45) {
if(value < 0) {
$box.style.transform = `rotateX(0deg) rotateY(${this.currentDeg - (90 - Math.abs(value)) + 0.01}deg)`;
}else {
$box.style.transform = `rotateX(0deg) rotateY(${this.currentDeg + (90 - Math.abs(value)) + 0.01}deg)`;
}
}else {
if(value < 0) {
$box.style.transform = `rotateX(0deg) rotateY(${this.currentDeg - value + 0.01}deg)`;
}else {
$box.style.transform = `rotateX(0deg) rotateY(${this.currentDeg + value + 0.01}deg)`;
}
}
this.canMove = false;
}
},
reset() {
const $box = document.getElementById('box');
$box.style.transform = `rotateX(-20deg) rotateY(20deg)`;
},
getTransform() {
const $box = document.getElementById('box');
const ts = $box.style.transform.split(' ');
const xs = ts[0].indexOf('X(');
const xe = ts[0].indexOf('deg');
const ys = ts[1].indexOf('Y(');
const ye = ts[1].indexOf('deg');
const x = ts[0].substring(xs + 2, xe);
const y = ts[1].substring(ys + 2, ye);
return {
x,
y,
}
},
},
mounted() {
},
}
</script>
<style lang="scss" scoped>
.box-wrap {
.box {
position: relative;
z-index: 6;
margin: auto;
transform-style: preserve-3d;
transform-origin: center, center;
transition: transform 1s;
.face {
position: absolute;
width: 100%;
height: 100%;
opacity: 0.3;
font-size: 30px;
}
.face-before {
transform: translateZ(150px);
background-color: rgb(0, 255, 170);
text-align: left;
}
.face-after {
transform-origin: center;
transform: translateZ(-150px) rotateX(-180deg);
background-color: #0088cc;
}
.face-top {
transform-origin: top;
transform: rotateX(90deg) translate(0, -150px);
background-color: rgb(255, 0, 170);
}
.face-bottom {
transform-origin: bottom;
transform: rotateX(-90deg) translate(0, 150px);
background-color: rgb(0, 255, 85);
}
.face-left {
transform-origin: left;
transform: rotateY(-90deg) translate(-150px, 0);
background-color: rgb(255, 208, 0);
}
.face-right {
transform-origin: right;
transform: rotateY(90deg) translate(150px, 0);
background-color: rgb(255, 208, 0);
}
}
}
</style>
5、使用:
<template>
<div class="app-home">
<header class="header">
<div class="content">
<div class="header-left">
<span class="title">凡夫俗子</span>
<span class="desc">我们追寻梦想, 因为那最令人开心</span>
</div>
<div class="header-right">
</div>
</div>
</header>
<box :height="300" :width="600" ref="box">
<template slot="before">
<div>before</div>
</template>
<template slot="after">
<div>after</div>
</template>
<template slot="left">
<div>left</div>
</template>
<template slot="right">
<div>right</div>
</template>
<template slot="top">
<div>top</div>
</template>
<template slot="bottom">
<div>bottom</div>
</template>
</box>
</div>
</template>
<script>
import box from '@/web/components/box3.vue'
export default {
name: 'HelloWorld',
data () {
return {
boxWidth: 1152,
boxHeight: 600,
}
},
components: {
box
},
methods: {
goTopClick() {
this.$refs.box.showTop();
},
goLeftClick() {
this.$refs.box.showLeft();
},
showAuto() {
this.$refs.box.showAuto();
}
},
mounted() {
this.boxHeight = document.documentElement.clientHeight - 15;
window.onresize = () => {
this.boxHeight = document.documentElement.clientHeight - 15;
};
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss">
.app-home {
background-color: #f5f5f5;
.header {
width: 100%;
height: 60px;
margin-bottom: 15px;
background-color: rgba(255,255,255,.5);
.content {
width: 1200px;
min-width: 1200px;
margin: auto;
overflow: auto;
padding-left: 24px;
padding-right: 24px;
color: #00bbd3;
.header-left {
.title {
display: inline-block;
vertical-align: middle;
margin-right: 24px;
line-height: 60px;
font-size: 25px;
letter-spacing: 0.2em;
}
.desc {
display: inline-block;
vertical-align: middle;
line-height: 60px;
font-size: 14px;
}
}
.header-right {
font-size: 12px;
}
}
}
}
</style>