小球大小随机,随机运动碰撞

在这里插入图片描述

<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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值