H5实现类蚂蚁森林动态气泡的渲染与收集
需求:积分气泡实现类蚂蚁森林动态效果UI图:技术栈:vue,ts需求分析:1.在特定的区域内随机产生积分气泡,气泡之间不重合,气泡与总能量球之间不重合;2.每个能量球上下浮动,点击后向总能量球移动逐渐消失。实现思路:1.不重合能量气泡的产生:随机数产生能量气泡的球心,递归判断产生的球心是否可用,若可用则push到数组,否则重新递归;2.能量球的动态效果:通过css3的animati...
·
需求:积分气泡实现类蚂蚁森林动态效果
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;
}
}
若有错误或者建议,欢迎指出~
更多推荐
已为社区贡献3条内容
所有评论(0)