<template>
<view class="tagBall">
<view class="tag" v-for="(item, index) in texts" :key="index" :style="[getTagStyle(index)]">
<view>{{item.userName}}</view>
<view class="dot"></view>
</view>
</view>
</template>
<script>
export default {
props: {
speed: {
type: Number,
default: 0.5
},
texts: {
type: Array,
default: () => {
return [];
}
}
},
data() {
return {
tagEle: [],
RADIUS: 150,
fallLength: 240,
angleX: Math.PI / 400,
angleY: Math.PI / 400,
tags: [],
liviews: [],
CX: 0,
CY: 0,
timer: null,
clickX: 0,
clickY: 0
};
},
computed: {
getTagStyle() {
return (index) => {
const style = this.liviews[index] || {};
return {
fontSize: ${style.fontSize}
,
opacity: style.opacity,
transition: style.transition,
filter: style.filter,
zIndex: style.zIndex,
transformStyle: style.transformStyle,
left: ${style.left}
,
top: ${style.top}
,
perspective: '20000rpx',
transition: 'all linear 0.1s',
transform: style.transform,
};
};
}
},
destroyed() {
this.clearTimer();
},
mounted() {
this.initTags();
},
methods: {
clearTimer() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
getTags(option) {
this.tagEle = option.tagEle;
this.CX = option.CX;
this.CY = option.CY;
this.init();
},
rotateX() {
const cos = Math.cos(this.angleX this.speed);
const sin = Math.sin(this.angleX this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const y1 = t.y cos + t.z sin;
const z1 = t.z cos - t.y sin;
t.y = y1;
t.z = z1;
}
},
rotateY() {
const cos = Math.cos(this.angleY this.speed);
const sin = Math.sin(this.angleY this.speed);
for (let i = 0; i < this.tags.length; i++) {
let t = this.tags[i];
const x1 = t.x cos - t.z sin;
const z1 = t.z cos + t.x sin;
t.x = x1;
t.z = z1;
}
},
init() {
this.tags = [];
this.liviews = [];
for (let i = 0; i < this.tagEle.length; i++) {
const k = (2 (i + 1) - 1) / this.tagEle.length - 1;
const a = Math.acos(k);
const b = a Math.sqrt(this.tagEle.length Math.PI);
const x = this.RADIUS Math.sin(a) Math.cos(b);
const y = this.RADIUS Math.sin(a) Math.sin(b);
const z = this.RADIUS Math.cos(a);
const t = this.tag(this.tagEle[i], x / 2, y / 2, z / 2);
this.tags.push(t);
}
this.animate(this.tags);
},
tag(ele, x, y, z) {
return {
ele,
x,
y,
z
};
},
move(t, i) {
const scale = this.fallLength / (this.fallLength - t.z 1.3);
const alpha = (t.z + this.RADIUS) / (2 this.RADIUS);
this.liviews.push({
fontSize: 5 scale + 'px',
opacity: alpha + 0.5,
transition: 'all linear 0.5s',
filter: 'alpha(opacity=' + (alpha + 0.5) 100 + ')',
zIndex: parseInt(scale 100),
transformStyle: 'preserve-3d',
left: t.x + this.CX - t.ele.offsetWidth / 2 + 'px',
top: t.y + this.CY - t.ele.offsetHeight / 2 + 'px',
perspective: '2000rpx',
transformStyle: 'preserve-3d',
transform: scale(${alpha+0.5})
,
});
},
animate(x) {
this.clearTimer();
this.timer = setInterval(() => {
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < x.length; i++) {
this.move(x[i], i);
}
}, 25)
},
touchstartscene(e) {
this.clickX = e.touches[0].clientX;
this.clickY = e.touches[0].clientY;
this.clearTimer();
},
touchendscene() {
this.animate(this.tags);
this.clearTimer();
},
touchmovescene(e) {
const fx = this.getDirection(this.clickX, this.clickY, e.touches[0].clientX, e.touches[0].clientY);
let x = this.clickX - e.touches[0].clientX - this.CX;
let y = this.clickY - e.touches[0].clientY - this.CY;
if (fx === 1) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY - this.clickY - this.CY;
} else if (fx === 2) {
x = e.touches[0].clientX - this.clickX;
y = e.touches[0].clientY + this.CY;
} else if (fx === 3) {
x = this.clickX + e.touches[0].clientX + this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
} else {
x = this.clickX - e.touches[0].clientX - this.CX;
y = this.clickY - e.touches[0].clientY - this.CY;
}
this.angleY = x 0.0001;
this.angleX = y 0.0001;
this.rotateX();
this.rotateY();
this.liviews = [];
for (let i = 0; i < this.tags.length; i++) {
this.move(this.tags[i], i);
}
},
getDirection(startx, starty, endx, endy) {
const angx = endx - startx;
const angy = endy - starty;
let result = 0;
if (Math.abs(angx) < 2 && Math.abs(angy) < 2) {
return result;
}
const angle = Math.atan2(angy, angx) 180 / Math.PI;
if (angle >= -135 && angle <= -45) {
result = 1;
} else if (angle > 45 && angle < 135) {
result = 2;
} else if ((angle >= 135 && angle <= 180) || (angle >= -180 && angle < -135)) {
result = 3;
} else if (angle >= -45 && angle <= 45) {
result = 4;
}
return result;
},
initTags() {
let tagEles = [];
this.$nextTick(() => {
const query = uni.createSelectorQuery().in(this);
query.selectAll('.tag').boundingClientRect(rects => {
tagEles = rects.map(rect => ({
offsetWidth: rect.width,
offsetHeight: rect.height
}));
}).exec();
query.select('.tagBall').boundingClientRect(rect => {
const CX = rect.width / 2;
const CY = rect.height / 2;
this.getTags({
tagEle: tagEles,
CX,
CY
});
}).exec();
});
}
}
};
</script>
<style scoped>
.tagBall {
height: 214rpx;
position: relative;
perspective: 2000rpx;
transform-style: preserve-3d;
left: 0;
top: 0;
transition: all linear 0.1s;
margin-left: 40rpx;
}
.tag {
width: 150rpx;
position: absolute;
font-weight: 500;
font-size: 12rpx;
color: #000000;
display: flex;
flex-direction: column;
align-items: center;
transition: all ease-in-out 0.3s;
}
.dot {
width: 16rpx;
height: 16rpx;
background: #FCC928;
border-radius: 50%;
margin-top: 10rpx;
}
</style>
0 个评论
要回复文章请先登录或注册