<template>
<GZNormalWrap title="建筑工业化" style="position: relative;" type= 'three'>
<div class="button-style" @click="handleLink('bimBuildingIndustrialization')">
决策BI
</div>
<ul id="constructionIndustryMain" v-if="dataArr.length>0">
<li v-for="(item, index) in circleData" :key="index" class="ball-style" @click="handleClick('bimBuildingIndustrialization')">
<div style="width:100%">
<div class="num-style">{{ item.value }}</div>
<div class="name-style">{{item.name}}</div>
</div>
</li>
</ul>
</GZNormalWrap>
</template>
<script>
import GZNormalWrap from '../../common/GZNormalWrapQds'
import api from 'api'
import GetUrlParams from 'mixins/getUrlParams'
import GetUserInfo from 'mixins/getUserInfo'
// import ball from './components/ball'
export default {
data() {
return {
circleData: [],
circleDom: [],
circleArr: [],
// 初始化运动的最大宽和高,初始定义0
maxW: 0,
maxH: 0,
timer: null,
getValueTimer: null,
timerArr: [],
count: 7,
clientWidthValue: 0,
ballWidth: 0,
ballWidthArr: [],
divVis: true,
// dataArr: ''
dataArr: [{ name: '装配式智能建造', value: 0, type: 2 }, { name: '机器人应用', value: 0, type: 3 }, { name: '放样机器人应用', value: 0, type: 5 }, { name: '三维激光机器', value: 0, type: 4 }, { name: '远程遥控', value: 0, type: 7 }, { name: '智慧展馆', value: 0, type: 1 }, { name: '倾斜摄影', value: 0, type: 6 }]
};
},
components: {
GZNormalWrap
// ball
// NoData
},
mixins: [GetUrlParams, GetUserInfo],
mounted() {
this.getData()
// 处理接口返回后小球之间来回不碰撞 按逻辑分析是因为 crash 里的引用改变 暂时这样处理 时间紧张
this.getValueTimer = setInterval((i) => {
if(this.divVis === true) {
this.divVis = false
this.getLatest_sign_users()
clearInterval(this.getValueTimer)
}
}, 500)
this.$nextTick(() => {
this.clientWidthValue = document.querySelector('#constructionIndustryMain').clientWidth
this.ballWidth = Math.floor(this.clientWidthValue / this.count)
this.handleBallWidth(this.ballWidth)
// this.getLatest_sign_users()
})
},
methods: {
getData() {
this.divVis = false
this.$get(api.QDSQHomePageWisdomPavilionCircleByModule(this.globalTenantId, this.type, this.id)).then(
(res) => {
if(res.data && res.data.data.length > 0) {
res.data.data.forEach(item => {
this.dataArr.forEach(ele => {
if(item.buildingType === ele.type.toString()) {
ele.value = item.applicationProjectNum
}
})
})
this.divVis = true
}
}
)
},
handleLink(item) {
if(item) {
this.$router.push({ path: '/' + item, query: this.$route.query })
sessionStorage.setItem('supervisePath', '/' + item)
} else {
return false
}
},
handleClick(item) {
if(item) {
this.$router.push({ path: '/' + item, query: this.$route.query })
sessionStorage.setItem('supervisePath', '/' + item)
} else {
return false
}
},
handleBallWidth(ballWidth) {
this.ballWidthArr.push(this.ballWidth)
const cell = ballWidth / (this.count)
// const num = (this.count - 1) % 2 + Math.floor((this.count - 1) / 2)
for(let i = 0; i < (this.count - 1); i++) {
const value = Math.floor(ballWidth - (cell * (i + 1)))
// const value2 = Math.floor(ballWidth + (cell * (i + 1)))
this.ballWidthArr.push(value)
// this.ballWidthArr.unshift(value2)
}
this.ballWidthArr.sort((a, b) => {
return a - b
})
},
getLatest_sign_users() {
// const data = [{ 'name': '三维激光机器', 'value': 0, 'type': 4 }, { 'name': '远程遥控', 'value': 0, 'type': 7 }, { 'name': '智慧展馆', 'value': 0, 'type': 1 }, { 'name': '倾斜摄影', 'value': 0, 'type': 6 }, { 'name': '装配式智能建造', 'value': 1, 'type': 2 }, { 'name': '机器人应用', 'value': 1, 'type': 3 }, { 'name': '放样机器人应用', 'value': 1, 'type': 5 }]
// this.dataArr = data
const data = this.dataArr
data.sort((a, b) => {
return a.value - b.value
})
this.circleData = [...data]
// 计算球体比例
// 计算比例 处理特殊场景
let vallConst = ''
const min = this.ballWidthArr[0] > 60 ? this.ballWidthArr[0] : 60
if((data[data.length - 1].value - data[0].value) === 0 && data[data.length - 1].value === 0) {
vallConst = min
} else if((data[data.length - 1].value - data[0].value) === 0 && data[data.length - 1].value !== 0) {
vallConst = this.ballWidthArr[this.ballWidthArr.length - 1]
} else {
this.ballRatio = (this.ballWidthArr[this.ballWidthArr.length - 1] - min) / (data[data.length - 1].value - data[0].value)
}
this.$nextTick(() => {
if (data.length) {
this.initBubble(vallConst)
}
})
},
initBubble(vallConst) {
const main = document.getElementById('constructionIndustryMain');
const divDom = main.querySelectorAll('.ball-style'); // 获取新增加的dom
// 给新增加的dom设置宽高
for (let i = 0; i < divDom.length; i++) {
const textValue = divDom[i].querySelector('.name-style').innerText
const indexValue = this.circleData.findIndex(item => item.name === textValue)
// divDom[i].style.boxShadow = '0 0 20px' + ' ' + colors[this.circleData[i].gender] + ' ' + 'inset';
divDom[i].style.background = 'linear-gradient(123deg, rgba(4, 4, 45, 0.43), rgba(38, 142, 161, 0.43))'
// 10个以上尺寸变小
// let sizeValue = this.ballWidthArr[indexValue]
const min = this.ballWidthArr[0] > 60 ? this.ballWidthArr[0] : 60
let sizeValue = vallConst ? vallConst : (Math.floor(this.circleData[indexValue].value * this.ballRatio) + min)
if(sizeValue < 60) {
sizeValue = 60
}
divDom[i].style.width = sizeValue + 'px' // '80px';
divDom[i].style.height = sizeValue + 'px';
divDom[i].style.fontSize = '12px';
divDom[i].style.lineHeight = '16px';
this.circleDom.push(divDom[i])
}
// 根据浏览器窗口的大小自动调节小球的运动空间
window.onresize = () => {
this.maxW = main.clientWidth - divDom[0].clientWidth; // 为了让小球不卡在浏览器边缘
this.maxH = main.clientHeight - divDom[0].clientHeight; // 所以要减去自身的宽高
};
onresize();
// 数组对象的初始化
for (let i = 0; i < this.circleDom.length; i++) {
const obj = {};
// 增加每个小球自己的maxW 和 maxH
const maxW = main.clientWidth - this.circleDom[i].clientWidth; // 为了让小球不卡在浏览器边缘
const maxH = main.clientHeight - this.circleDom[i].clientHeight;
// if (this.circleDom[i].getAttribute('class') === 'active') {
obj.x = Math.floor(Math.random() * (maxW + 1)); // 初始x坐标
obj.y = Math.floor(Math.random() * (maxH + 1)); // 初始y坐标
obj.cx = obj.x + this.circleDom[i].offsetWidth / 2;// 圆心x坐标
obj.cy = obj.y + this.circleDom[i].offsetHeight / 2;// 圆心y坐标
obj.movex = Math.floor(Math.random() * 2); // x轴移动方向
obj.movey = Math.floor(Math.random() * 2); // y轴移动方向
obj.speed = 0.2; // 随机速度
obj.timer = null; // 计时器
obj.index = i; // 索引值
obj.maxW = maxW; // 每个小球最大宽
obj.maxH = maxH; // 每个小球最大高
obj.offsetWidthValue = this.circleDom[i].offsetWidth
this.circleArr.push(obj)
// 小球位置初始化
this.circleDom[i].style.left = obj.x + 'px';
this.circleDom[i].style.top = obj.y + 'px';
// } else {
// // 保留之前数据得位置信息,不刷新位置
// obj = this.circleArr[i]
// }
this.move(obj);
}
},
// 移动函数
move(balls) {
const self = this
self.timer = requestAnimationFrame(function fn() {
if (balls.movex === 1) {
// 如果往右跑,则一直加速度,碰到边界,改为反方向运动
balls.x += balls.speed;
if (balls.x + balls.speed >= balls.maxW) {
// 防止小球出界
balls.x = balls.maxW;
balls.movex = 0; // 小球运动方向发生改变
}
} else {
balls.x -= balls.speed; // 1和0表示正反方向
if (balls.x - balls.speed <= 0) {
balls.x = 0;
balls.movex = 1;
}
}
if (balls.movey === 1) {
balls.y += balls.speed;
if (balls.y + balls.speed >= balls.maxH) {
balls.y = balls.maxH;
balls.movey = 0;
}
} else {
balls.y -= balls.speed;
if (balls.y - balls.speed <= 0) {
balls.y = 0;
balls.movey = 1;
}
}
if (self.circleDom[balls.index]) {
balls.cx = balls.x + self.circleDom[balls.index].offsetWidth / 2;// 小球圆心等于:运动中x的值加上自身的半径
balls.cy = balls.y + self.circleDom[balls.index].offsetHeight / 2;
self.circleDom[balls.index].style.left = balls.x + 'px'; // 小球相对于屏幕的位置
self.circleDom[balls.index].style.top = balls.y + 'px';
self.crash(balls.index); // 每个小球进行碰撞检测
}
requestAnimationFrame(fn);
});
// // 每个球单独有定时器
// balls.timer = setInterval(() => {
// if (balls.movex === 1) {
// // 如果往右跑,则一直加速度,碰到边界,改为反方向运动
// balls.x += balls.speed;
// if (balls.x + balls.speed >= balls.maxW) {
// // 防止小球出界
// balls.x = balls.maxW;
// balls.movex = 0; // 小球运动方向发生改变
// }
// } else {
// balls.x -= balls.speed; // 1和0表示正反方向
// if (balls.x - balls.speed <= 0) {
// balls.x = 0;
// balls.movex = 1;
// }
// }
// if (balls.movey === 1) {
// balls.y += balls.speed;
// if (balls.y + balls.speed >= balls.maxH) {
// balls.y = balls.maxH;
// balls.movey = 0;
// }
// } else {
// balls.y -= balls.speed;
// if (balls.y - balls.speed <= 0) {
// balls.y = 0;
// balls.movey = 1;
// }
// }
// if (this.circleDom[balls.index]) {
// balls.cx = balls.x + this.circleDom[balls.index].offsetWidth / 2;// 小球圆心等于:运动中x的值加上自身的半径
// balls.cy = balls.y + this.circleDom[balls.index].offsetHeight / 2;
// this.circleDom[balls.index].style.left = balls.x + 'px'; // 小球相对于屏幕的位置
// this.circleDom[balls.index].style.top = balls.y + 'px';
// this.crash(balls.index); // 每个小球进行碰撞检测
// }
// }, 25);
// this.timerArr.push(balls.timer)
},
// 碰撞函数
crash(a) {
const container = [...this.circleArr]
const ball1x = container[a].cx; // 在数组中任意球的圆心坐标
const ball1y = container[a].cy;// 思路:先随便拿一个球,然后遍历所有球,拿这个球和所有球的圆心距离比较
for (let i = 0; i < container.length; i++) {
if (i !== a) { // 判断取出来的球不是本身,才能和其他球进行距离判断
const ball2x = container[i].cx; // 将其他球的圆心坐标赋值给球2
const ball2y = container[i].cy;
// 圆心距 求两个点之间的距离,开平方
const distence = Math.sqrt((ball1x - ball2x) * (ball1x - ball2x) + (ball1y - ball2y) * (ball1y - ball2y));
if (distence <= ((this.circleDom[a].offsetWidth + container[i].offsetWidthValue) / 2)) { // 球心距离和求直径比较
if (ball1x > ball2x) {
// 当前位于未知求的右方
if (ball1y > ball2y) { // 预设未知球撞当前球,然后当前球改变运动
container[a].movex = 1; // 1表示为正值,对应的右和下
container[a].movey = 1;// 0表示为负值,对应的左和上
} else if (ball1y < ball2y) {
container[a].movex = 1;
container[a].movey = 0;
} else {
container[a].movex = 1;
}
} else if (ball1x < ball2x) {
if (ball1y > ball2y) {
container[a].movex = 0;
container[a].movey = 0;
} else if (ball1y < ball2y) {
container[a].movex = 0;
container[a].movey = 1;
} else {
container[a].movex = 0;
}
} else {
if (ball1y > ball2y) {
container[a].movey = 1;
} else if (ball1y < ball2y) {
container[a].movey = 0;
}
}
}
}
}
}
},
beforeDestroy() {
cancelAnimationFrame(this.timer)
// 清理每个球得定时器
this.timerArr.forEach(item => {
clearInterval(item)
})
// 清理签到数据
clearInterval(this.timer)
}
};
</script>
<style lang='less' scoped>
#constructionIndustryMain {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
padding: 0;
li {
position: absolute;
overflow: hidden;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
cursor: pointer;
&.active {
animation: scaleBox 1s 1;
}
@keyframes scaleBox {
0% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
100% {
transform: scale(1);
}
}
div {
span {
display: block;
width: 100%;
color: #fff;
text-align: center;
}
}
}
}
.num-style{
font-size: 18px;
font-weight: bold;
color: #20E3E1;
text-align: center;
}
.name-style{
margin-top: 5px;
font-size: 12px;
color: #FFFFFF;
text-align: center;
}
.button-style{
position: absolute;
right: 30px;
top: 20px;
cursor: pointer;
background: rgba(56, 137, 255, 0.6);
width: 80px;
height: 24px;
line-height: 24px;
text-align: center;
color: #FFFFFF;
border-radius: 2px;
border: 1px solid #3889FF;
}
</style>
借鉴于此
https://blog.csdn.net/weixin_39150852/article/details/123825240