果冻效果

发布时间 2023-07-01 13:18:50作者: oOLzYOo

tween中的progress

​ 看陈皮皮的项目时看到一个果冻的按钮效果,感觉很不错翻了下代码,发现一个平时很少用的tween姿势。稍微解释下,在bouncingTime内,该节点的scaleX变化成this.originalScale,怎么变过去呢?通过progress中描述的方法来演变。

Snipaste_2022-09-03_21-28-32

最后解释下progress中4个参数的含义

  • start:起始的scaleX
  • end:目标scaleX,也就是this.originalScale
  • current:变化过程中的scaleX
  • t:是该运动0~1的进度,例如:t=0.2,表示start+(end-start)*0.2,再一例子要跑100米,t=0.2表示我已经跑了20m了

弹动“秘方”

​ 那么scaleX的变化主要就看progress内的函数了。已知end是目标大小,而是amplitude是输入的固定已知震幅参数震幅,t是变化过程中的进度,也是已知,那我们继续分析getDifference函数。

 /**
     * 获取目标时刻弹性幅度
     * @param amplitude 幅度
     * @param t 进度(0~1)
     */
    private getDifference(amplitude: number, t: number) {
        // 角速度(ω=2nπ)
        const angularVelocity = this.frequency * Math.PI * 2;
        return amplitude * (Math.sin(t * angularVelocity) / Math.exp(this.decay * t) / angularVelocity);
    }

​ 乍一看这啥玩意,好像是三角函数,又有一堆莫名其妙的参数。其实简化一下就看得懂了。一个sin三角函数除以一个e指数函数。(省略了一些自定义参数)

\[y=\left( \frac{\sin \left( 2\cdot \pi \cdot x \right)}{e^{x/2\cdot \pi }} \right)\; \]

​ 而在t>0的范围内,该函数的函数图如上,可见是一个振幅逐渐变小的函数,非常符合我们想要的“果冻”效果。那现在我们的progress函数再次翻译:end - this.getDifference(amplitude, t),目标scaleX大小-一个振幅逐渐递减变化的函数。

还有一个细节,需要完成“挤压”的效果。scaleX和scaleY的变化是需要方向相反。

scaleX中的是end - this.getDifference(amplitude, t)

scaleY中的是end + this.getDifference(amplitude, t)

加一点“佐料”

大佬的弹跳效果已经理解了,现在我们来加点佐料,优化如下效果。

JellyFall

这个动画除了上面展示的弹性缩放,还添加了弹性位移。具体为一个自由落体运动后+一个原地弹性运动

下面贴的是弹性运动的代码,一个要点是向量需要差值计算,佐料是乘以this.quartOut(t),让衰减更加线性流畅。

 jellyFall(targetPos:cc.Vec2){
        if(this.fallTween){
            this.fallTween.stop()
            this.fallTween=null
        }
        this.originalPos = this.node.getPosition()
        this.sitTween = cc.tween(this.node)
            .repeat(1,
                cc.tween()
                    .to(this.totalTime*0.15,{position:cc.v2(0,0)},{easing:'quartOut'})
                    .to(this.totalTime*0.85, {
                        position:{
                            value:targetPos,
                            progress:(start: cc.Vec2, end: cc.Vec2, current: number, t: number) => {
                                return  start.add(this.getSinglePos(end,t).mul(this.quartOut(t)))
                            }
                        }
                    })
            )
            .start()
    }

    private getSinglePos(amplitude: cc.Vec2, t: number){
        const angularVelocity = this.frequency * Math.PI;
        return cc.Vec2.ZERO.lerp(amplitude.mul(10),Math.abs((Math.sin(t * angularVelocity) / Math.exp(this.decay * t) / angularVelocity)) ) 
    }
    quartOut (k) { return 1 - ( --k * k * k * k ); }

参考链接

陈皮皮的项目

[AE中的果冻效果](