vue中多个小球运动 碰撞之后弹开效果

<template>

  <div class="about">

    <div class="chart" ref="main">

      <div class="circle1 circle" ref="circle2">

        <span>球1<br /><span style="top: 20px;position: relative;">30分</span></span>
      </div>

      <div class="circle2  circle" ref="circle2">

        <span>球2<br /><span style="top: 20px;position: relative;">20分</span></span>

      </div>

      <div class="circle3 circle" ref="circle3">

        <span>球3<br /><span style="top: 20px;position: relative;margin-left: 1.5rem">25分</span></span>
      </div>

      <div class="circle4  circle" ref="circle4">

        <span>球4<br /><span style="top: 20px;position: relative;margin-left: 2.5rem">10分</span></span>
      </div>
      <div class="circle5  circle" ref="circle5">
        <span>球5<br /><span style="top: 20px;position: relative;margin-left: 2rem">14分</span></span>

      </div>

    </div>

  </div>

</template>



<script>

export default {

  data() {

    return {

      main: "",

      //获取五个小球的div

      circlesL: "",

      //用来存放小球div每个的具体信息,包括坐标,速度等值

      container: [],

      arr: {},

      //初始化运动的最大宽高,初始定义为0

      maxW: [],

      maxH: [],

      //获取小球的直径

      cwidth: [],

    };

  },
  mounted() {

    this.main = this.$refs.main;

    this.circles = this.main.getElementsByClassName('circle');

    // 为了让小球不卡在盒子边缘,所以要减去自身宽高

    for (let index = 0; index < this.circles.length; index++) {

      this.maxW.push(this.main.offsetWidth - this.circles[index].clientWidth);

      this.maxH.push(this.main.offsetHeight - this.circles[index].clientHeight);

      this.cwidth.push(this.circles[index].offsetWidth);

    }

    this.initializeCircle();

    // 对每一个小球绑定计时器,让小球动起来

    for (let i = 0; i < this.container.length; i++) {

      this.cricleMove(this.container[i]);

    }

  },

  methods: {

    //初始化球体信息

    initializeCircle() {

      // 数组对象初始化

      for (let i = 0; i < this.circles.length; i++) {

        this.arr = {};

        // 初始x坐标

        this.arr.x = Math.floor(Math.random() * (this.maxW[i] + 3));

        // 初始y坐标

        this.arr.y = Math.floor(Math.random() * (this.maxH[i] + 5));

        // 圆心x坐标

        this.arr.cx = this.arr.x + this.circles[i].offsetWidth / 2;

        // 圆心y坐标

        this.arr.cy = this.arr.y + this.circles[i].offsetHeight / 2;

        // x轴移动方向

        this.arr.movex = Math.floor(Math.random() * 3);

        // y轴移动方向

        this.arr.movey = Math.floor(Math.random() * 3);

        // 随机速度

        this.arr.speed = 1 + Math.floor(Math.random() * 2);

        // 计数器

        this.arr.timer = null;

        // 索引值

        this.arr.index = i;

        // 存放所有属性值

        this.container.push(this.arr);

        // 初始化小球位置

        this.circles[i].style.left = `${this.arr.x}px`;

        this.circles[i].style.top = `${this.arr.y}px`;

      }

    },

    // 球体碰撞函数

    circleCrash(index) {

      const currentBall = this.container[index];

      for (let i = 0; i < this.container.length; i++) {

        if (i !== index) {

          const otherBall = this.container[i];

          // 计算两个球心的距离

          const dx = otherBall.cx - currentBall.cx;

          const dy = otherBall.cy - currentBall.cy;

          const distance = Math.sqrt(dx * dx + dy * dy);



          // 碰撞阈值,这里应该是两个球的半径之和

          const collisionThreshold = (this.cwidth[currentBall.index] / 2) + (this.cwidth[otherBall.index] / 2);



          if (distance < collisionThreshold) {

            // 简单的互斥逻辑,像弹簧一样把球推开

            // 计算重合部分的长度

            const overlap = collisionThreshold - distance;



            // 计算互斥方向上的单位向量

            const unitX = dx / distance;

            const unitY = dy / distance;



            // 将球体移开使其不重叠

            // 移动当前球

            currentBall.x -= overlap * unitX * 0.5;

            currentBall.y -= overlap * unitY * 0.5;



            // 同时移动另一个球

            otherBall.x += overlap * unitX * 0.5;

            otherBall.y += overlap * unitY * 0.5;



            // 更新球的圆心位置

            currentBall.cx = currentBall.x + this.cwidth[currentBall.index] / 2;

            currentBall.cy = currentBall.y + this.cwidth[currentBall.index] / 2;

            otherBall.cx = otherBall.x + this.cwidth[otherBall.index] / 2;

            otherBall.cy = otherBall.y + this.cwidth[otherBall.index] / 2;



            // 简单地反转当前球的运动方向来模拟碰撞的响应

            currentBall.movex = -unitX > 0 ? 1 : 0;

            currentBall.movey = -unitY > 0 ? 1 : 0;



            // 反转另一个球的运动方向

            otherBall.movex = unitX > 0 ? 1 : 0;

            otherBall.movey = unitY > 0 ? 1 : 0;

          }

        }

      }

    },

    //球体移动函数

    cricleMove(balls) {

      // 每个球都有单独的定时器

      balls.timer = setInterval(() => {

        if (balls.movex === 1) {

          // 如果向右跑,则一直加速度,碰到边界,改为反方向运动

          balls.x += balls.speed;

          if (balls.x + balls.speed >= this.maxW[balls.index]) {

            // 防止小球移动出界限

            balls.x = this.maxW[balls.index];

            balls.movex = 0;

          }

        } else {

          balls.x -= balls.speed;

          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 >= this.maxH[balls.index]) {

            balls.y = this.maxH[balls.index];

            balls.movey = 0;

          }

        } else {

          balls.y -= balls.speed;

          if (balls.y - balls.speed <= 0) {

            balls.y = 0;

            balls.movey = 1;

          }

        }

        balls.cx = balls.x + this.circles[0].offsetWidth / 2; // 小球圆心等于:运动中x的值加上自身的半径

        balls.cy = balls.y + this.circles[0].offsetHeight / 2;

        this.circles[balls.index].style.left = balls.x + 'px'; // 小球相对于屏幕的位置

        this.circles[balls.index].style.top = balls.y + 'px';

        this.circleCrash(balls.index); // 每个小球进行碰撞检测

      }, 25);

    }



  }

};

</script>

<style lang="scss" scoped>

@mixin lineHeight($height) {

  height: $height;

  line-height: $height;

}



.about {

  .chart {

    height: 50rem;

    margin: 0 auto;

    // border: 0.1rem solid rgba(6, 143, 106, 1);

    position: relative;



    .font_style {

      text-align: center;

      color: #FFFFFF;

      font-size: 1.3rem;

    }



    .circle1 {

      position: absolute;

      top: 1rem;

      left: 5rem;

      width: 12rem;

      height: 12rem;

      // @include lineHeight(12rem);

      border-radius: 50%;

      box-shadow: inset 0px 0px 2rem 0.3rem #8F8915;

      font-size: 1.5rem;

      // text-align: center;

      align-items: center;

      display: flex;

      justify-content: center;

      flex-direction: row;

      // display: flex;

    }



    .circle2 {

      position: absolute;

      top: 16rem;

      left: 10rem;

      width: 10rem;

      height: 10rem;

      // @include lineHeight(10rem);

      border-radius: 50%;

      box-shadow: inset 0px 0px 3.3rem 1.1rem #14C424;

      font-size: 1.1rem;

      align-items: center;

      display: flex;

      justify-content: center;

      flex-direction: row;



    }



    .circle3 {

      position: absolute;

      top: 28rem;

      left: 15rem;

      width: 8rem;

      height: 8rem;

      // @include lineHeight(8rem);

      border-radius: 50%;

      box-shadow: inset 0px 0px 4rem 1.1rem #0C5EB9;

      font-size: 1rem;



      align-items: center;

      display: flex;

      justify-content: center;

      flex-direction: row;

    }



    .circle4 {

      position: absolute;

      top: 25rem;

      left: 2rem;

      width: 10rem;

      height: 10rem;

      border-radius: 50%;

      box-shadow: inset 0px 0px 4rem 1.1rem #5C15BD;

      font-size: 1.3rem;



      align-items: center;

      display: flex;

      justify-content: center;

      flex-direction: row;

    }



    .circle5 {

      position: absolute;

      top: 38rem;

      left: 5rem;

      width: 8rem;

      height: 8rem;

      // @include lineHeight(8rem);

      border-radius: 50%;

      box-shadow: inset 0rem 0rem 4rem 1.1rem #256BD1;

      font-size: 1rem;



      align-items: center;

      display: flex;

      justify-content: center;

      flex-direction: row;

    }

  }

}</style>

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Vue 实现多个小球碰撞,可以按照以下步骤进行: 1. 创建一个 Ball 组件,用于显示单个小球的界面。这个组件需要包含小球的位置、速度等信息,并且需要实现小球的移动和碰撞检测等功能。 ``` <template> <div :style="{ left: x + 'px', top: y + 'px' }"></div> </template> <script> export default { props: { x: Number, y: Number, speedX: Number, speedY: Number, radius: Number, color: String, bounds: Object }, data() { return { timerId: null } }, created() { this.timerId = setInterval(this.move, 10); }, methods: { move() { let x = this.x + this.speedX; let y = this.y + this.speedY; if (x - this.radius < this.bounds.left || x + this.radius > this.bounds.right) { this.speedX = -this.speedX; } if (y - this.radius < this.bounds.top || y + this.radius > this.bounds.bottom) { this.speedY = -this.speedY; } this.$emit('update:x', x); this.$emit('update:y', y); } } } </script> <style> div { position: absolute; width: 20px; height: 20px; border-radius: 50%; } </style> ``` 2. 在父组件,创建多个 Ball 组件,并为每个组件设置不同的位置、速度等信息。同时,还需要为每个组件设置边框信息,用于碰撞检测。 ``` <template> <div ref="container" style="position: relative; width: 400px; height: 400px; border: 1px solid black;"> <Ball v-for="(ball, index) in balls" :key="index" :x.sync="ball.x" :y.sync="ball.y" :speedX="ball.speedX" :speedY="ball.speedY" :radius="ball.radius" :color="ball.color" :bounds="bounds" /> </div> </template> <script> import Ball from './Ball.vue'; export default { components: { Ball }, data() { return { balls: [ { x: 50, y: 50, speedX: 5, speedY: 3, radius: 10, color: 'red' }, { x: 100, y: 100, speedX: -3, speedY: 4, radius: 15, color: 'blue' }, { x: 150, y: 150, speedX: 4, speedY: -5, radius: 20, color: 'green' } ], bounds: { left: 0, top: 0, right: 400, bottom: 400 } } } } </script> ``` 3. 在 Ball 组件实现碰撞检测的功能。可以在 move 方法,遍历所有其他的 Ball 组件,检测当前小球是否与其他小球相交。如果相交了,就需要计算当前小球的新速度,并更新位置。 ``` move() { let x = this.x + this.speedX; let y = this.y + this.speedY; let radius = this.radius; if (x - radius < this.bounds.left || x + radius > this.bounds.right) { this.speedX = -this.speedX; } if (y - radius < this.bounds.top || y + radius > this.bounds.bottom) { this.speedY = -this.speedY; } this.balls.forEach(ball => { if (ball !== this) { let dx = ball.x - x; let dy = ball.y - y; let distance = Math.sqrt(dx * dx + dy * dy); if (distance < radius + ball.radius) { let angle = Math.atan2(dy, dx); let sin = Math.sin(angle); let cos = Math.cos(angle); // 旋转坐标系 let vx1 = this.speedX * cos + this.speedY * sin; let vy1 = this.speedY * cos - this.speedX * sin; let vx2 = ball.speedX * cos + ball.speedY * sin; let vy2 = ball.speedY * cos - ball.speedX * sin; // 碰撞后的新速度 let vx1Final = ((this.radius - ball.radius) * vx1 + (2 * ball.radius) * vx2) / (this.radius + ball.radius); let vx2Final = ((2 * this.radius) * vx1 + (ball.radius - this.radius) * vx2) / (this.radius + ball.radius); // 更新速度 this.speedX = vx1Final * cos - vy1 * sin; this.speedY = vy1 * cos + vx1Final * sin; ball.speedX = vx2Final * cos - vy2 * sin; ball.speedY = vy2 * cos + vx2Final * sin; // 更新位置 x += this.speedX; y += this.speedY; ball.x += ball.speedX; ball.y += ball.speedY; } } }); this.$emit('update:x', x); this.$emit('update:y', y); } ``` 通过以上步骤,就可以实现多个小球碰撞,并且在 Vue 动态更新小球的位置和速度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值