场景:实现一个多个星球切换的交互效果,要有科技感。因此做了一个弧形的可触控星球切换效果。一屏展示三个星球,旁边两个星球加了蒙版,中间的为目标星球,加了放大效果使之更加突出。
实现效果:
星球触控转动
思路:
- 布局是一个进行圆周运动的容器,容器内固定展示有 6 个星球,一半隐藏,一半展示
- 根据 touch 事件,获取判断触控的位置,让容器正向或逆向转动
- 写了一个负数索引的方法,通过 ref 获取到对应节点,每次转动都会给当前星球添加对应位置的 class 名,进行特殊样式展示
代码:
<template>
<div class="kp-wrap">
<div id="box" ref="box" style="transform:rotate(0deg)" @touchstart="boxTouchStart" @touchmove="boxTouchMove" @touchend="boxTouchEnd">
<div class="out-circle">
<div v-for="(item, index) in newStarList" :key="index" class="star-item">
<p :ref="'itemImg'+index" class="star-info">
<img :src="item.imgUrl" alt="">
</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'StarDemo',
data () {
return {
deg: 0,
startX: 0,
endX: 0,
startList: [],
newStarList: [],
anIdxList: ['1', '2', '3', '4', '5', '6'],
anIdx: 0
}
},
created () {
this.initData()
},
mounted () {
this.$refs.itemImg1[0].classList.add('star-2')
},
methods: {
initData () {
this.startList = [{
imgUrl: require('../assets/images/01.png')
},
{
imgUrl: require('../assets/images/02.png')
},
{
imgUrl: require('../assets/images/03.png')
},
{
imgUrl: require('../assets/images/04.png')
},
{
imgUrl: require('../assets/images/05.png')
},
{
imgUrl: require('../assets/images/06.png')
}
]
this.newStarList = JSON.parse(JSON.stringify(this.startList))
},
boxTouchStart (e) {
this.pointShow = false
if (this.startList.length > 3) {
const touch = e.touches[0] // 获取触摸对象
this.startX = touch.clientX // 获取触摸坐标
this.endX = 0
console.log(touch)
}
},
boxTouchMove (e) {
if (this.startList.length > 3) {
this.endX = e.touches[0].clientX // 获取触摸坐标
}
},
boxTouchEnd (e) {
if (this.startList.length > 3 && this.endX) {
this.deg = parseInt(this.$refs.box.style.transform.replace('rotate(', ''))
// 向右边滑动
if (this.endX - this.startX > 0) {
if (this.deg === 0) {
console.log('已经到第一个了')
} else {
this.anIdx++
this.deg = this.deg + 60
console.log(this.deg)
this.$refs.box.style.transform = 'rotate(' + this.deg + 'deg)'
this.newStarList.forEach((item, index) => {
this.$refs[`itemImg${index}`][0].className = `star-${this.createArray(this.anIdxList)[index + this.anIdx]}`
})
}
} else {
if (Math.abs(this.anIdx) === this.startList.length - 2) {
console.log('已经到最后一个了')
} else {
this.anIdx--
this.deg = this.deg - 60
console.log(this.deg)
this.$refs.box.style.transform = 'rotate(' + this.deg + 'deg)'
this.newStarList.forEach((item, index) => {
// debugger
this.$refs[`itemImg${index}`][0].className = `star-${this.createArray(this.anIdxList)[index - Math.abs(this.anIdx)]}`
})
}
}
}
},
createArray (arr) {
const length = arr.length
return new Proxy(arr, {
get (target, key) {
key = +key
while (key < 0) {
key += length
}
return target[key]
}
})
}
}
}
</script>
<style lang="scss" scoped>
.kp-wrap {
position: relative;
width: 100%;
height: 100vh;
top: 0;
left: 0;
background: #040c27;
background-size: 100%;
overflow: hidden;
#box{
position: relative;
top: 30%;
left: -3rem;
height:6rem;
width:6rem;
text-align:Center;
color:#fff;
font-size: .2rem;
transition: all 1s;
border-radius:50%;
}
#box .out-circle{
position: absolute;
top: 50%;
left: 50%;
margin-left: -1rem;
margin-top: -1rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
display: inline-block;
}
#box .star-item{
position: absolute;
left: 50%;
top: 50%;
margin-left: -2rem;
margin-top: -2rem;
width: 4rem;
height: 4rem;
border-radius: 50%;
}
#box .star-item .star-info {
display: flex;
flex-direction: column;
}
#box .out-circle div:nth-of-type(1){transform: rotate(120deg);}
#box .out-circle div:nth-of-type(2){transform: rotate(180deg);}
#box .out-circle div:nth-of-type(3){transform: rotate(240deg);}
#box .out-circle div:nth-of-type(4){transform: rotate(300deg);}
#box .out-circle div:nth-of-type(5){transform: rotate(0deg);}
#box .out-circle div:nth-of-type(6){transform: rotate(60deg);}
#box .out-circle div p {
height:3rem;
width:3rem;
border-radius:50%;
img {
display:block;
height:100%;
width:100%;
border-radius: 50%;
box-shadow: 0 0 0.5rem 0rem #2a9bf4;
}
&:before {
content: '';
display: block;
width: 0.01rem;
height: 0.01rem;
position: absolute;
left: 50%;
top: 50%;
box-shadow: 0 0 2rem 1.5rem #040c27;
z-index: 999;
color: #333848;
}
}
//#box .out-circle div p {transform: translateX(-4rem)}
#box .out-circle div:nth-of-type(1) p{ transform: translateX(-4rem) rotate(-120deg); }
#box .out-circle div:nth-of-type(2) p{ transform: translateX(-4rem) rotate(-180deg); }
#box .out-circle div:nth-of-type(3) p{ transform: translateX(-4rem) rotate(-240deg); }
#box .out-circle div:nth-of-type(4) p{ transform: translateX(-4rem) rotate(-300deg); }
#box .out-circle div:nth-of-type(5) p{ transform: translateX(-4rem) rotate(0deg); }
#box .out-circle div:nth-of-type(6) p{ transform: translateX(-4rem) rotate(-60deg);}
}
@keyframes an-point {
0% {
left: 3rem;
top: 3rem;
}
100% {
left: 5rem;
top: 7rem;
}
}
@keyframes an-1 {
0% {
transform: translateX(-4rem) scale(1) rotate(-120deg);
}
100% {
transform: translateX(-4rem) scale(1) rotate(-120deg);
}
}
@keyframes an-2 {
0% {
transform: translateX(-4rem) rotate(-180deg) scale(1);
}
100% {
transform: translateX(-4rem) rotate(-180deg) scale(1.3);
}
}
@keyframes an-3 {
0% {
transform: translateX(-4rem) scale(1) rotate(-240deg);
}
100% {
transform: translateX(-4rem) scale(1) rotate(-240deg);
}
}
@keyframes an-4 {
0% {
transform: translateX(-4rem) scale(1) rotate(-300deg);
}
100% {
transform: translateX(-4rem) scale(1) rotate(-300deg);
}
}
@keyframes an-5 {
0% {
transform: translateX(-4rem) scale(1) rotate(0);
}
100% {
transform: translateX(-4rem) scale(1) rotate(0);
}
}
@keyframes an-6 {
0% {
transform: translateX(-4rem) scale(1) rotate(-120deg);
}
100% {
transform: translateX(-4rem) scale(1) rotate(-120deg);
}
}
.star-1 { animation: an-1 2s forwards; }
.star-2 {
animation: an-2 2s forwards;
&:before {
content: '';
box-shadow: none !important;
}
}
.star-3 { animation: an-3 2s forwards; }
.star-4 { animation: an-4 2s forwards; }
.star-5 { animation: an-5 2s forwards; }
.star-6 { animation: an-6 2s forwards; }
</style>