H5实现类蚂蚁森林动态气泡的渲染与收集

需求:积分气泡实现类蚂蚁森林动态效果

UI图
在这里插入图片描述

技术栈:vue,ts

需求分析
1.在特定的区域内随机产生积分气泡,气泡之间不重合,气泡与总能量球之间不重合;
2.每个能量球上下浮动,点击后向总能量球移动逐渐消失

实现思路
1.不重合能量气泡的产生:随机数产生能量气泡的球心,递归判断产生的球心是否可用,若可用则push到数组,否则重新递归;
2.能量球的动态效果:通过css3的animation实现。
思路来源于大佬:思路来源

主要代码
获取有效气泡球心坐标:

 // 产生随机数
 _rnd(n: number, m: number): number {
    const random = Math.floor((Math.random() * (m - n + 1)) + n);
    return random;
  }
  /**
   * 判断两圆心之间距离是否大于直径 或者球心距totalBall球心距离小于
   * p1 -- 已创建可用Ball的ballList中item
   * p2 -- 当前创建ball
   * distanceToBall -- 两个ball球心之间最小距离
   */
  _getDistanceForBallCenter(p1: object, p2: object, distanceToBall: number): boolean {
    const dx = Math.abs(p2.x - p1.x);
    const dy = Math.abs(p2.y - p1.y);
    const disToBall = Math.sqrt((dx ** 2) + (dy ** 2));
    return disToBall > distanceToBall;
  }
  // 产生有效气泡球心坐标
  _getSingleIntegralBallCenter(arrList: Array<object>): any {
    const currentArrList: any = [];
    arrList.forEach((arrItem) => { // 过滤掉上一页的
      if (!arrItem.click) {
        currentArrList.push(arrItem);
      }
    });
    const _ballCenterPos = {
      x: this._rnd(0.1 * this.viewW, 0.9 * this.viewW),
      y: this._rnd(0.1 * this.viewH, 0.6 * this.viewH)
    };
    const _noCoverToTal = this._getDistanceForBallCenter(
      this.totalCenterPos,
      _ballCenterPos,
      this.distanceToTotal
    );
    if (_noCoverToTal) {
      if (currentArrList && currentArrList.length > 0) {
        let isFlag = true; // 该pos是否全部不重合标志
        currentArrList.forEach((listItem: any) => {
          const _noCover = this._getDistanceForBallCenter(
            listItem,
            _ballCenterPos,
            this.distanceToBall
          ); // 该pos是否与该ball不重合
          if (!_noCover) {
            isFlag = false;
          }
        });
        if (isFlag) {
          return _ballCenterPos;
        }
        return this._getSingleIntegralBallCenter(this.ballCenterPosList);
      }
      return _ballCenterPos;
    }
    return this._getSingleIntegralBallCenter(this.ballCenterPosList);
  }

渲染能力气泡:

<!-- 类蚂蚁森林HTML -->
    <div
      class="antForest-container"
      ref="antForest"
    >
      <template v-if="integralAvailabel && nodeList && nodeList.length > 0">
        <template v-if="nodeList && nodeList.length > 0">
          <div id = "antForest-ball__container">
            <div
              v-for="(ballItem, ballIndex) in nodeList"
              :key="ballIndex"
            >
              <!-- 多一层实现悬浮晃动效果 -->
              <div
                :id="`antForest-ball__item_${ballIndex}`"
                @click="clickIntegralBall(ballIndex, ballItem)"
              >
                <div class="antForest-ball__item">
                  <div class="ball-item__ball ds-flex align-center justify-center">
                    <span
                      class="ball-item__count font-number-medium"
                    >{{ballItem.scoreValue > 0 ? `+${ballItem.scoreValue}` : ballItem.scoreValue}}</span>
                  </div>
                  <div class="ball-item__type">{{ballItem.description}}</div>
                </div>
              </div>
            </div>
          </div>
        </template>
        <div v-if="userIntegralInfo.totalScore === 0" class="noIntegral-tip">
          <div class="noIntegral-tip__topBox">快去完成任务来获取积分吧!</div>
          <div class="noIntegral-tip__bottomBox"></div>
        </div>
        <div class="antForest-container__totalTegral">
          <span class="totalTegral-num font-number-medium">{{userIntegralInfo.totalScore || 0}}</span>
          <img
            v-if="showGetAllIcon"
            class="totalTegral-getAll__img"
            id="totalTegral-getAll__img"
            @click="clickGetAll"
            src="https://img1.tuhu.org/PeccancyCheXingYi/FobgTRJWhXW4LvPOA5xCVAmWG7Rr_w408_h128.png@100Q.png"
          />
        </div>
      </template>
	</div>
	
// js
// 一页面十个积分气泡
 createIntegralBallWall() {
   if (this.nodeList && this.nodeList.length > 0) {
     this.nodeList.forEach((item, index) => {
       const pos = this._getSingleIntegralBallCenter(this.ballCenterPosList);
       this.ballCenterPosList.push(pos);
       this._renderBallPos(index, pos);
     });
   }
 }

// 每个积分气泡的定位
_renderBallPos(index: number, pos: object) {
   const antForestBallItem = document.getElementById(`antForest-ball__item_${index}`);
   if (antForestBallItem) {
     (antForestBallItem as any).className = 'antForest-ball__item_container';
     (antForestBallItem as any).style.opacity = 1;
     (antForestBallItem as any).style.position = 'absolute';
     (antForestBallItem as any).style.left = `${((pos.x - 42.5) / this.viewW) * 100}%`;
     (antForestBallItem as any).style.top = `${((pos.y - 42.5) / this.viewH) * 100}%`; // 使用%定位
   }
 }
     

积分气泡的动态效果:

.antForest-ball__item {
    position: relative;
    animation: ballShaking 3s infinite;
    font-size: 28px;
    color: rgba(255, 255, 255, 1);
    line-height: 35px;
}
@keyframes ballShaking {
  0% {
    position: relative;
    top: 0;
  }
  50% {
    position: relative;
    top: 10px;
  }
  100% {
    position: relative;
    top: 0px;
  }
}

点击积分气泡效果:

// 节流
@throttle(800)
// 气泡点击
 clickIntegralBall(ballIndex: number, ballItem: object) {
   if (!this.getAllClicking && !ballItem.click) {
     this.singleClicking = true;
     this.nodeList[ballIndex].click = true;
     this.timer = setTimeout(() => {
       (document.getElementById(`antForest-ball__item_${ballIndex}`) as any).className = 'clickBall';
       (document.getElementById(`antForest-ball__item_${ballIndex}`) as any).style.opacity = 0;
     }, 300);
     const collectIntegralList = [];
     collectIntegralList.push(ballItem.operationLogId);

     // !!!不能remove Dom
     this.timer = setTimeout(() => {
       const params = {
         operationLogIds: [...collectIntegralList]
       };
       this.collectIntegralBall(params);
     }, 500);
   } else {
     console.log('该积分已经领取过啦或者正在一键领取');
   }
 }
// css
.clickBall {
  animation: ballClick 3s ease;
}
@keyframes ballClick {
  0% {
    position: absolute;
    opacity: 1;
  }
  50% {
    position: absolute;
    opacity: 0.4;
  }
  75% {
    position: absolute;
    opacity: 0.1;
  }
  100% {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translateX(-50%);
    opacity: 0;
  }
}

若有错误或者建议,欢迎指出~

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
实现聊天窗气泡效果,可以使用以下的CSS样式: HTML结构: ```html <div class="chat-container"> <div class="chat-message received"> <div class="message-bubble"> <span class="message">Hello!</span> </div> </div> <div class="chat-message sent"> <div class="message-bubble"> <span class="message">Hi there!</span> </div> </div> <div class="chat-message received"> <div class="message-bubble"> <span class="message">How are you?</span> </div> </div> <div class="chat-message sent"> <div class="message-bubble"> <span class="message">I'm good, thanks. How about you?</span> </div> </div> </div> ``` CSS样式: ```css .chat-container { display: flex; flex-direction: column; padding: 20px; background-color: #eee; height: 400px; overflow-y: scroll; } .chat-message { display: flex; margin-bottom: 10px; } .received { align-self: flex-start; } .sent { align-self: flex-end; } .message-bubble { background-color: #fff; border-radius: 10px; padding: 10px; box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1); position: relative; } .message-bubble:before { content: ""; position: absolute; top: 0; border-style: solid; border-width: 0 10px 10px 0; } .received .message-bubble:before { left: -10px; border-color: #fff transparent transparent transparent; } .sent .message-bubble:before { right: -10px; border-color: #007bff transparent transparent transparent; } .message { line-height: 1.5; font-size: 14px; } ``` 解释: - `chat-container`是聊天窗的容器,设置了`flex`布局和垂直方向的排列,以及一些基本的样式,如背景色、高度和滚动条等。 - `chat-message`是每个聊天消息的容器,设置了`flex`布局和一些基本的样式,如外边距等。 - `.received`和`.sent`分别表示接收到的消息和发送的消息,设置了不同的对齐方式。 - `message-bubble`是消息气泡的容器,设置了一些基本的样式,如背景色、边框半径、内边距和阴影等,以及相对定位。 - `.message-bubble:before`是消息气泡的三角形,使用`content`属性来插入一个空内容,设置了绝对定位、上边距、边框样式和边框宽度,并根据消息的来源设置了不同的位置、边框颜色和方向。 - `message`是消息内容的容器,设置了一些基本的样式,如行高和字体大小等。 这些样式可以根据实际需求进行调整,以达到更好的效果

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值