css动效:气泡在屏幕上随机漂浮

css实现气泡随机漂浮效果

  • 实现效果
  • React代码
  • css代码
  • 总结


实现效果

React代码

// 泡泡随机漂浮
// 这里的封装逻辑是基于每个泡泡大小一样进行封装的

import React,{FC,useState,useEffect,useRef} from 'react'
import styles from './index.less'

// 泡泡的背景图
import bubble1 from '../../img/bubblefloat/bubble1.png'
import bubble2 from '../../img/bubblefloat/bubble2.png'
import bubble3 from '../../img/bubblefloat/bubble3.png'
import bubble4 from '../../img/bubblefloat/bubble4.png'
import bubble5 from '../../img/bubblefloat/bubble5.png'
import bubble6 from '../../img/bubblefloat/bubble6.png'
import bubble7 from '../../img/bubblefloat/bubble7.png'


const Bubble:FC<any>  = (props)=>{

  useEffect(()=>{
      document.documentElement.style.fontSize=(document.documentElement.clientWidth/750)*100+'px';
  },[])
  

  const mainRef = useRef<any>()

  const BubblesRef = useRef<any>([])
  
  const getBubbles = (dom: any) => {
    if(!dom) return;
    BubblesRef.current.push(dom)
  }

  const bubblesArr = [
   {key:1,bgImg:bubble1},
   {key:2,bgImg:bubble2},
   {key:3,bgImg:bubble3},
   {key:4,bgImg:bubble4},
   {key:5,bgImg:bubble5},
   {key:6,bgImg:bubble6},
   {key:7,bgImg:bubble7},
  ]

 // 气泡运动的函数
  const bubbleMovement = ()=>{
        // 气泡运动区域
     const main  = mainRef.current

     //  气泡数组
      const  circles =  BubblesRef.current
 
      //初始化运动的最大宽和高, 为了让气泡不卡在屏幕边缘,因此要减去自身的宽高
     const maxW = main.clientWidth- circles[0].clientWidth; 
     const  maxH = main.clientHeight - circles[0].clientHeight; 
     
     const container:any = []; //存放气泡的具体信息,包括坐标,速度等值
     const cwidth = circles[0].clientWidth; //小球的直径
 
     let arr;
 
     //数组对象的初始化
     for (let i = 0; i < circles.length; i++) {
         arr = {};
         arr.x = Math.floor(Math.random() * (maxW + 1)); //初始x坐标
         arr.y = Math.floor(Math.random() * (maxH + 1)); //初始y坐标
         arr.cx = arr.x + circles[0].clientWidth / 2; //圆心x坐标
         arr.cy = arr.y + circles[0].clientHeight / 2; //圆心y坐标
         arr.movex = Math.floor(Math.random() * 2); //x轴移动方向 1:右 0:左
         arr.movey = Math.floor(Math.random() * 2); //y轴移动方向 1:下 0:上
         arr.speed = 1 + Math.floor(Math.random() ); //随机移动距离
         arr.timer = null; //计时器
         arr.index = i; //索引值
         container.push(arr); //存放全部的属性值
         // 气泡位置初始化
         circles[i].style.left = arr.x + 'px'; 
         circles[i].style.top = arr.y + 'px';
     }
 
     // 碰撞检测函数
     const crash = (a) => {
         const bubble1x = container[a].cx; //在数组中任意球的圆心坐标
         const bubble1y = container[a].cy; 
 
         for (let i = 0; i < container.length; i++) {
             if (i !== a) { //判断取出来的气泡是不是本身,才能和其余球进行距离判断
                 const bubble2x = container[i].cx; //将其余气泡的圆心坐标赋值给气泡2
                 const bubble2y = container[i].cy;
                 //圆心距 求两个点之间的距离,开平方
                 const distence = Math.sqrt((bubble1x - bubble2x) * (bubble1x - bubble2x) + (bubble1y - bubble2y) * (bubble1y - bubble2y));
                 // //球心距离和求直径比较 碰撞
                 if (distence <= cwidth) { 
                     //气泡1位于气泡2的右方
                     if (bubble1x > bubble2x) { 
                           //气泡1位于气泡2的下方(右下)
                         if (bubble1y > bubble2y) { 
                             container[a].movex = 1; //1表示为正值,对应的右和下
                             container[a].movey = 1; //0表示为负值,对应的左和上
                         } 
                           //气泡1位于气泡2的上方(右上)
                         else if (bubble1y < bubble2y) {
                             container[a].movex = 1;
                             container[a].movey = 0;
                         } 
                           //气泡1气泡2的垂直位置相同(水平)
                         else {
                             container[a].movex = 1;
                         }
                     }
                      //气泡1位于气泡2的左方
                      else if (bubble1x < bubble2x) {
                             //气泡1位于气泡2的下方(左下)
                         if (bubble1y > bubble2y) {
                             container[a].movex = 0;
                             container[a].movey = 0;
                         }
                           //气泡1位于气泡2的上方(左上)
                          else if (bubble1y < bubble2y) {
                             container[a].movex = 0;
                             container[a].movey = 1;
                         } 
                           //气泡1气泡2的垂直位置相同(水平)
                         else {
                             container[a].movex = 0;
                         }
                     }
                       //气泡1和气泡2的水平方位位置相同(垂直)
                      else {
                         //  气泡1在气泡2正下方
                         if (bubble1y > bubble2y) {
                             container[a].movey = 1;
                         }
                         //  气泡1在气泡2正上方
                          else if (bubble1y < bubble2y) {
                             container[a].movey = 0;
                         }
                     }
                 }
             }
 
         }
     }
 
     //移动函数
     const move = (bubble)=> { //每一个球单独有定时器
         bubble.timer = setInterval(()=>{
            //若是往右跑,则一直加速度,碰到边界,改成反方向运动
             if (bubble.movex === 1) {
                 bubble.x += bubble.speed;
                 if (bubble.x + bubble.speed >= maxW) { //防止小球出界
                     bubble.x = maxW;
                     bubble.movex = 0; //小球运动方向发生改变
                 }
             } else {
                 bubble.x -= bubble.speed; 
                 if (bubble.x - bubble.speed <= 0) {
                     bubble.x = 0;
                     bubble.movex = 1;
                 }
             }
               //若是往下跑,则一直加速度,碰到边界,改成反方向运动
             if (bubble.movey === 1) {
                 bubble.y += bubble.speed;
                 if (bubble.y + bubble.speed >= maxH) {
                     bubble.y = maxH;
                     bubble.movey = 0;
                 }
             } else {
                 bubble.y -= bubble.speed;
                 if (bubble.y - bubble.speed <= 0) {
                     bubble.y = 0;
                     bubble.movey = 1;
                 }
             }
 
             // 计算坐标圆心是为了判断气泡和气泡之间是否碰撞
             bubble.cx = bubble.x + circles[0].clientWidth / 2; //圆心等于:运动中x的值加上自身的半径
             bubble.cy = bubble.y + circles[0].clientHeight / 2;
       
             circles[bubble.index].style.left = bubble.x + 'px'; 
             circles[bubble.index].style.top = bubble.y + 'px';
             crash(bubble.index); //每一个小球进行碰撞检测
         }, 30); //改变定时器的数值可以修改小球运动速度
     }
 
     // //对每个小球绑定计时器,让小球动起来
     for (let i = 0; i < container.length; i++) {
         move(container[i]);
     }
  }


  useEffect(()=>{
    bubbleMovement()
  },[])


  return(
    <div className={styles.bubbles} ref={mainRef}>
       {
         bubblesArr.map((item)=>{
           return <div key={item.key} 
           className={styles.bubble}  
           style={{  backgroundImage:`url(${item.bgImg})`}}
           ref={getBubbles}/>
         })
       }
    </div>

  )
}

export default Bubble;

css代码

.bubbles{
  width: 100vw;
  height: 95vh;
  position: relative;
  .bubble{
    position: absolute;
    background-repeat: no-repeat;
    background-size: contain;
    width: 1rem;
    height: 1rem;
  }
}

总结

1、实现方式:定位+随机改变x值和y值
2、需要注意的是:这里的实现逻辑是基于每个气泡的大小都一样的前提下实现的
3、具体的实现逻辑在bubbleMovement函数
(1)初始化,设置每个气泡的随机位置,移动距离、移动方向
(2)增加setInterval函数让每个气泡动起来 运动起来具体逻辑看move函数,大概的实现逻辑就是根据初始化的方向,往左或右或上或下以初始化时给定的速度走,走的时候需要监听是否到达边界,如果到达,继续往反方向走,走的时候并且需要监听是否碰撞
(3)监听碰撞:具体看这个函数 crash(bubble.index);这个函数大概的逻辑就是判断一下有没有碰撞到别的气泡,如果碰撞了,就判断一下当前气泡处于这个气泡的哪个方位,如果在左上方,那就继续往这个左上方走(既然碰撞了 并且气泡1位于气泡2的左上方,就说明之前气泡1运动的方向是右下才会导致碰撞)

你可能感兴趣的:(#,动画效果,css,javascript,前端)