JS 实现抛物线动画

(点击

上方公众号

,可快速关注)

作者:henry_chen

segmentfault.com/a/1190000013355484

在做小程序的项目中,需要在添加购物车的时候,增加抛物线小球动画。

先给大家看下效果图

JS 实现抛物线动画

分析

这种不固定起始位置的动画,自然不能用 gif 图,所以只能用原生代码实现。

那我们有什么工具来实现动画呢?

  • 小程序提供了 JS API 

    createAnimation

     来创建动画

  • CSS transition

  • 工具有了,我们再看一下什么是抛物线。

    这里我们只讨论水平抛物线,水平抛物线从数学原理上来说就是【水平匀速、垂直加速的运动】,转换成代码层面就是在动画效果

    timingFunction

    中,水平动画采用

    linear

    ,垂直动画采用

    ease-in

    所以我们需要把这个抛物线动画分解成 两个 同时 进行但 不同动画效果 的动画。

    实现

    (一)小程序的实现

    JS:

  • cartAnimation(x, y) {

    // x y 为手指点击的坐标,即球的起始坐标

  •    let self =

    this

    ,

  •        cartY = app.globalData.winHeight -

    50

    ,

    // 结束位置(购物车图片)纵坐标

  •        cartX =

    50

    ,

    // 结束位置(购物车图片)的横坐标

  •        animationX = flyX(cartX, x),

    // 创建球的横向动画

  •        animationY = flyY(cartY, y)

    // 创建球的纵向动画

  •    

    this

    .setData({

  •          ballX: x,

  •          ballY: y,

  •          showBall:

    true

  •    })

  •    setTimeoutES6(

    100

    ).then(() => {

    // 100 ms 延时,确保球已经到位并显示

  •        self.setData({

  •            animationX: animationX.

    export

    (),

  •            animationY: animationY.

    export

    (),

  •        })

  •        

    return

    setTimeoutES6(

    400

    )

    // 400 ms 是球的抛物线动画时长

  •    }).then(() => {

    // 400 ms 延时后隐藏球

  •        

    this

    .setData({

  •            showBall:

    false

    ,

  •        })

  •    })

  • }

  • function

    setTimeoutES6(sec) {

    // Promise 化 setTimeout

  •    

    return

    new

    Promise

    ((resolve, reject) => {

  •        setTimeout(() => {resolve()}, sec)

  •    })

  • }

  • function

    flyX(cartX, oriX) {

    // 水平动画

  •    let animation = wx.createAnimation({

  •        duration:

    400

    ,

  •        timingFunction:

    "linear"

    ,

  •    })

  •    animation.left(cartX).step()

  •    

    return

    animation

  • }

  • function

    flyY(cartY, oriY) {

    // 垂直动画

  •    let animation = wx.createAnimation({

  •        duration:

    400

    ,

  •        timingFunction:

    "ease-in"

    ,

  •    })

  •    animation.top(cartY).step()

  •    

    return

    animation

  • }

  • HTML:

  • <view

    animation

    =

    "{{animationY}}"

    style

    =

    "

    position:

    fixed

    ;top:{{ballY}}px;

    "

    hidden

    =

    "{{!showBall}}"

    >

  •    

    <view

    class

    =

    "ball"

    animation

    =

    "{{animationX}}"

    style

    =

    "

    position:

    fixed

    ;left:{{ballX}}px;

    "

    ></view>

  • </view>

  • translate 优化

    据我所知,用

    transform:translate()

    来实现的动画会比 top & left 性能更优,但实现下来却没那么容易咯。

    研究来研究去,发现 translate 的做法比 top & left 的做法多了一步,就是需要将小球的 translate 位移还原(否则 translate 一直有值),才能保证下一次的位移从点击的位置开始。

  • cartAnimation(x, y) {

  •    let self =

    this

    ,

  •        cartY = app.globalData.winHeight -

    50

    ,

  •        cartX =

    50

    ,

  •        animationX = flyX(cartX, x),

  •        animationY = flyY(cartY, y)

  •    

    this

    .setData({

  •        leftNum: x,

  •        topNum: y,

  •        showBall:

    true

  •    })

  •    setTimeoutES6(

    100

    ).then(() => {

  •        self.setData({

  •            animationDataX: animationX.

    export

    (),

  •            animationDataY: animationY.

    export

    (),

  •        })

  •        

    return

    setTimeoutES6(

    400

    )

  •    }).then(() => {

  •        

    this

    .setData({

  •            showBall:

    false

    ,

  •            animationX: flyX(

    0

    ,

    0

    ,

    0

    ).

    export

    (),

    // 还原小球位置,即 translate 恢复默认值

  •            animationY: flyY(

    0

    ,

    0

    ,

    0

    ).

    export

    (),

  •        })

  •    })

  • }

  • function

    flyX(cartX,oriX,duration) {

  •    let animation = wx.createAnimation({

  •        duration: duration||

    400

    ,

  •        timingFunction:

    "linear"

    ,

  •    })

  •    animation.translateX(cartX-oriX).step()

  •    

    return

    animation

  • }

  • function

    flyY(cartY,oriY,duration) {

  •    let animation = wx.createAnimation({

  •        duration: duration||

    400

    ,

  •        timingFunction:

    "ease-in"

    ,

  •    })

  •    animation.translateY(cartY-oriY).step()

  •    

    return

    animation

  • }

  • HTML 部分不变。

    (二)H5 的实现

    除了小程序之外,前端日常开发更多的当然还是 H5,下面我将用 CSS3 transition 的方法来实现。

  • <!DOCTYPE html>

  • <html

    lang

    =

    "en"

    style

    =

    "

    width:

    100

    %;height:

    100

    %;

    "

    >

  • <head>

  •    

    <meta

    charset

    =

    "UTF-8"

    >

  •    

    <meta

    name

    =

    "viewport"

    content

    =

    "width=device-width"

    >

  •    

    <style>

  •        * {

  •            padding:

    0

    ;

  •            margin:

    0

    ;

  •        }

  •        

    #ball {

  •            width:

    12px

    ;

  •            height:

    12px

    ;

  •            background:

    #5EA345;

  •            border-radius:

    50

    %;

  •            position:

    fixed

    ;

  •            transition: left

    1s

    linear, top

    1s

    ease-

    in

    ;

  •        }

  •    

    </style>

  •    

    <title>

    CSS3 水平抛物线动画

    </title>

  • </head>

  • <body

    style

    =

    "

    width:

    100

    %;height:

    100

    %;

    "

    >

  •    

    <div

    id

    =

    "ball"

    ></div>

  • </body>

  • <script>

  •    

    var

    $ball = document.getElementById(

    "ball"

    );

  •    document.body.onclick =

    function

    (evt) {

  •        console.log(evt.pageX,evt.pageY)

  •        $ball.style.top = evt.pageY+

    "px"

    ;

  •        $ball.style.left = evt.pageX+

    "px"

    ;

  •        $ball.style.transition =

    "left 0s, top 0s"

    ;

  •        setTimeout(()=>{

  •            $ball.style.top = window.innerHeight+

    "px"

    ;

  •            $ball.style.left =

    "0px"

    ;

  •            $ball.style.transition =

    "left 1s linear, top 1s ease-in"

    ;

  •        },

    20

    )

  •    }

  • </script>

  • </html>

  • 还有体验链接哦:http://henry-cj.github.io/demo/js-horizontal-parabola

    至此,水平抛物线动画的实现就介绍得差不多啦,嘻嘻!!

    觉得本文对你有帮助?请分享给更多人

    关注「前端大全」,提升前端技能

    JS 实现抛物线动画