仿蚂蚁森林收集能量效果实现-vue版

2,766 阅读4分钟

因为项目需要实现类似蚂蚁森林收集能量的效果,于是上网找了一波,没能找到完整的案例,于是自己着手写了一个,以供有需要的人参考,如有需要优化及不足的地方,欢迎指出。

github地址:github.com/Xiangfs/ant…

最开始想采用纯canvas实现,但是实现的过程中发现canvas实现有诸多问题点,于是最后还是放弃了

实现过程

实现思路:其实就是在树的上方生成若干个浮动的点,然后点击的时候以树中心为目标移动,到达之后消失的一个过程

定义变量:

export default {
    data() {
        return {
            cWitdh: window.innerWidth, // 屏幕宽度
            cHeight: window.innerHeight, // 屏幕高度
            circlePadding: 50, // 左右边距,也会影响到生成的球的横坐标边界
            treeWidth: 200, // 树的宽度
            treeHeight: 300, // 树的高度
            // 上面是基本的变量,下面的变量在点的移动和计算的时候需要用到
            moveTarget: null, // 移动对象
            targetIndex: null, // 被点击的对象的索引
            isMoving: false, // 是否在移动中
            moveEnd: false, // 移动结束状态,用于触发树的动画
            circleArr: [], // 所有点的集合数组
            xArr: [], // 所有点的横坐标数组
            easing: 0.15, // 阻尼系数
            r: 24 // 圆形半径
        }
    }
}

生成随机点

// 生成随机数方法
randomNum (from, to) {
    return Math.floor(Math.random() * (to - from + 1) + from)
},
createList(count) {
    let { cWidth, cHeight, circlePadding, treeHeight } = this
        let index = 0
        while (this.circleArr.length < count) {
            if (index > 100) return false // 防止死循环
            index++
            var x = this.randomNum(circlePadding, cWidth - circlePadding)
            // y的范围为树的最高点到屏幕顶部
            var y = this.randomNum(cHeight - treeHeight - 220, cHeight - treeHeight - 120)
            this.circleArr.push({
                x,
                y,
                r: this.r,
                count: this.randomNum(1, 20),
                scale: 1 // 用于动画缩放倍数的参数,默认为1
            )}
        }
      }
}

这样生成的点与点之间会存在太近导致重叠过多的问题,于是做了如下处理:

// 生成随机数方法
randomNum (from, to) {
    return Math.floor(Math.random() * (to - from + 1) + from)
},
// 生成圆点方法
createList (count) {
    let { cWidth, cHeight, circlePadding, treeHeight } = this
    let index = 0
    while (this.circleArr.length < count) {
        if (index > 100) return false // 防止死循环
        index++
        var x = this.randomNum(circlePadding, cWidth - circlePadding)
        // y的范围为树的最高点到屏幕顶部
        var y = this.randomNum(cHeight - treeHeight - 220, cHeight - treeHeight - 120)
        // 判断是否与已有的点太近
        if (!this.isNear(x)) {
            this.circleArr.push({
                x,
                y,
                r: this.r,
                count: this.randomNum(1, 20),
                scale: 1 // 用于动画缩放倍数的参数,默认为1
            })
            // xArr用于判断点与点是否太近
            this.xArr.push(x)
        }
    }
},
isNear (x) {
    var near = false
    this.xArr.forEach(val => {
        // 本来为至少要小于2倍半径,但如果其余的点把屏幕均分了,下一个点就永远满足不了条件,故改为小于1.5倍半径,即有多于4/3个圆的部分重叠,则判断为太近
        // 这里可以根据需要更改判断条件
        if (Math.abs(x - val) < (this.r * 3) / 2) {
            near = true
        }
    })
    return near
}
}

效果图:

效果图

实现动画

startMove (index) {
    if (!this.isMoving) {
        this.moveTarget = this.circleArr[index] // 保存要移动的目标对象
        this.targetIndex = index // 保存移动目标的索引
        this.isMoving = true
        this.move()
    }
},
move () {
    if (this.moveTarget) {
        let { moveTarget, centerX, centerY, easing } = this
        // 当目标距离中心点很近的时候停止动画,否则继续执行动画
        if (Math.abs(moveTarget.x - centerX) > 5 && Math.abs(moveTarget.y - centerY) > 5) {
            // 每次移动距离目标点剩余距离的阻尼倍数的距离,以此实现阻尼效果
            moveTarget.x += (centerX - moveTarget.x) * easing
            moveTarget.y += (centerY - moveTarget.y) * easing
            moveTarget.scale -= 0.02
            window.requestAnimationFrame(this.move)
        } else {
            // 重置移动相关参数
            this.isMoving = false
            this.moveTarget = null
            this.targetIndex = null
            this.circleArr.splice(this.targetIndex, 1) // 删除移动完成的点
        }
    }
},

附上圆点上下浮动以及收集完成后树弹动的动画

/* 圆点浮动 */
@keyframes wave {
  0% {
    transform: translateY(-3px);
  }
  50% {
    transform: translateY(3px);
  }
  100% {
    transform: translateY(-3px);
  }
}
/* 树的弹动 */
@keyframes treeFlip {
  from {
    transform: scale3d(1, 1, 1) translateX(-50%);
  }
  25% {
    transform: scale3d(1, 1.05, 1) translateX(-50%);
  }
  50% {
    transform: scale3d(1, 0.95, 1) translateX(-50%);
  }
  75% {
    transform: scale3d(1, 1.05, 1) translateX(-50%);
  }
  to {
    transform: scale3d(1, 1, 1) translateX(-50%);
  }
}

最终效果

成果动图

后记

主要注重实现效果,所以背景图以及树的图片都比较粗糙~~敬请谅解(╹▽╹)

目前只能实现单个圆点动画完成后再执行下一个圆点的动画,如何让几个圆点的动画可以同时进行有待研究,希望有大神指点迷津。

如果觉得这篇文章对你有帮助,记得点star哦