结束语

全文完结,感谢你的阅读,希望整个系列的文章对你今后的学习有所帮助。

如果你想系统学习 React + Redux
技术栈的所有内容,请点我前往

2 赞 1 收藏
评论

澳门微尼斯人手机版 1

JS 实现抛物线动画

2018/02/28 · JavaScript
· 动画

原文出处: henry_chen   

在做小程序的项目中,需要在添加购物车的时候,增加抛物线小球动画。先给大家看下效果图(其实已经是实现后的效果了,顺便给自己公司打广告了哈哈)

澳门微尼斯人手机版 2

chrome-render原理与实践

澳门微尼斯人手机版,关于资源加载的选择

React 虽然是个
library,但是它的生态圈非常的丰富,会有非常多的可扩展框架或类库可以加入使用,但是千万别太快的加入这些扩展方案。并且每次新加入一个模块的时候,要在团队里面确认每个人都是清楚了解的。特别是对于
Redux 本身的一些生态扩展,会有非常多的一些小模块,比如下面这些:

  • 在大家还没开始写 action creatorsreducers 之前,就不要添加
    redux-actions
  • 在大家还没写出第一个自己的 form 表单和表单验证的时候,就不要加入
    redux-form
  • 在大家还没开始写自己的 selectors 之前,就不要加入
    reselect
  • 在大家还么开始写第一个高阶组件 HOC 之前,就不要加入
    recompose
  • …..

同时,关注一些大牛的最佳实践,并且建立你自己的最佳实践。不过得确保团队中其他小伙伴也能理解。定义清晰的命名规则和目录结构,并且在项目做一些升级的时候得把这些约定提前讨论清楚。

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 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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 部分不变

如何操控HeadlessChrome

既然HeadlessChrome是以无界面模式运行的,那要怎么控制它和它交互?
chrome提供了远程控制接口,目前可以通过chrome-remote-interface来用js代码向chrome发送命令进行交互。在启动chrome的时候要开启远程控制接口,然后通过
chrome-remote-interface
连接到chrome后再通过协议控制chrome。具体操作见文档:

  • 以headless模式和远程控制模式启动chrome
  • 连接到远程chrome控制它
  • 控制chrome时支持哪些操作具体怎么用

保证测试用例简单、最小颗粒度

否则的话你需要为此付出很高的维护成本。

确认每个组件是否都有执行过单元测试,确认每个 props 和 callbacks
都在集成测试的时候传递给了对应的子组件。

为了保证组件测试用例的小颗粒度和简单化,你需要熟悉一下
selectorsEnzyme 提供了丰富的
selector 去深入组件树。

另外,建议使用 sinon 来测试 callbacks
回调函数,不要在组件中测试业务逻辑,这真不是个好注意。而是应该将业务逻辑从组件中解耦并对其进行测试。

最后,Facebook 出品的 Jest
也能在初期帮助我们更加轻量的执行测试,你可以非常简单就设置好 snapshot
test,这样当组件的输出改变的话测试用例会自动的报出失败的结果,并且能够获取到错误信息。

(一)小程序的实现

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 }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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>

1
2
3
  <view animation="{{animationY}}" style="position:fixed;top:{{ballY}}px;" hidden="{{!showBall}}">
    <view class="ball" animation="{{animationX}}" style="position:fixed;left:{{ballX}}px;"/>
  </view>

总结

大家可能会说这个很像prerender.io,没错思路是一样的,chrome-render的优势在于:

  • chrome-render开源可自己部署,prerender要收费是商业产品
  • prerender基于已经停止维护的phantomjs

本文中所提到的相关项目都是开源的并且有详细的使用文档,它们的文档链接如下:

  • chrome-render
  • chrome-runner
  • koa-seo
  • koa-chrome-render

喜欢的给个star,希望大家和我一起来改进它们让它们更强大。

1 赞 收藏
评论

澳门微尼斯人手机版 1

发表评论

电子邮件地址不会被公开。 必填项已用*标注