今天,网站又一次挂了,基于docker跑的mysql不知道怎么就坏了。

登上服务器,试过stop, kill, restart,都不能正确重启这个docker container。无奈之下,只要拿出重启大法

执行reboot命令,ssh断开,间隔三分钟之后重新登录。

使用docker stats {container_name} 查看,发现mysql正常了,再启动wordpress对应的container(不知道这个container为啥没能自动启动)。重新刷新网页,提示:Connection refused

以为还是mysql或者wordpress这两个container的问题,再次执行了针对这两个container的restart命令,没效果。

使用tail -f来监听nginx的access.log,然后本地和服务器上反复执行ping和curl,发现也没有新的日志。

怀疑是防火墙的问题,去vulter的后台确认了平台这边的防火墙没问题,再使用firewall-cmd查看服务器自身配置的防火墙,发现也没问题,80端口都是开着的。

继续找资料,无意间看到这篇文章:linux中某个端口拒绝远程主机连接原因及解决方法

就在服务器上执行了netstat -anp | grep 80,发现本地并没有任何程序监听此端口。

瞬间灵光一闪,明白了,重启之后Nginx还没启动。

手动执行nginx,然后刷新网页,一切正常了….

一直都是做网页开发,对于移动端的多屏适配可以说是比较有经验了,不管是用rem还是vw。不过在写游戏的时候,有点头疼了,完全不知道如何着手。

游戏里面元素的位置变化比较频繁多变,一开始是想的所有的位移都是基准屏幕(iPhone 6)的尺寸加上屏幕变化的比率。可这样,光想想就很头疼,这种不优雅的解决方式,一定不是最优解。

在网上搜索一番之后,大概明白大致的实现方案了:把页面主要元素都放到一个rootStage里面,再等比缩放这个rootStage,因为缩放是自动计算的,其它的所有运动和碰撞都可以基于rootStage的尺寸来进行,不需要考虑多屏适配。

具体实现如下:

在pixi.js里面,有一个最基础的由Application实例化过来的stage, 先取名叫做app。 这个app里面可以放置场景内容,背景色或者背景图片什么的。需要保证这个app是完全全屏的:

app = new Application({
  width: windowWidth * pixelRatio,
  height: windowHeight * pixelRatio,
  antialias: true,
  transparent: false,
  resolution: pixelRatio,
  view: canvas
})
app.renderer.backgroundColor = 0xffffff;
app.renderer.autoResize = true;
app.renderer.resize(windowWidth, windowHeight);

新增一个Container,叫做rootStage,这个rootStage的width和height,是设计稿的尺寸,像我们这边都是基于iPhone6,也就是375 x 667。

基于这个尺寸和app的尺寸,计算出一个rootStage可以以cover形式放置的尺寸。(cover尺寸是指的rootStage等比缩放,在不发生裁剪的情况下,在app里面能展现出最大的尺寸)。

基于cover尺寸和实际尺寸,计算出rootStage的scale,并调整rootStage的x和y,使得可以在app里面居中放置。

root = new Container();
root.width = rootScreen.width;
root.height = rootScreen.height;
const scale = realScreen.width / rootScreen.width
root.scale.x = scale;
root.scale.y = scale;
root.x = (windowWidth - realScreen.width) / 2;
root.y = (windowHeight - realScreen.height) / 2;
app.stage.addChild(root)

现在rootStage已经配置好,后面所有游戏内的主要交互和运动的元素都可以放到这里面,计算都是基于375 x 667的舞台计算,不需要考虑多屏幕的适配问题。

这是头一回正式的进行游戏开发,也没什么领路人,暂时只能想到这么多,如果有更好的办法,也欢迎大家告知

参考资料:

  1. 使用 pixi.js 开发微信小游戏
  2. 在pixi.js实现设备自适应和强制竖屏
  3. Phaser横竖屏适配

起因

今天在学习《HTML5+Javascript动画基础》这本书的时候,在第八章的第三节讲到如何用三个弹簧连接三个点来做拉伸运动。

在做完例子之后,就想到如果是四个点,五个点,怎么样。

就改写了一下代码,把点的数目变量化。最终的效果是能实现各个点最终的拉伸运动到平衡,可是点之间的连线不是很好看,有些是交叉的。

于是就想着能不能优化这一块。

旋转连线

前面例子里面的点,都是随机位置,所以连线不可控。所以想先从这块着手。

先以某一个点为参照点,获得其他点相对于这个点的角度。

然后按照角度从小到大的去连接这些点,这样就能画出一个正常的多边形了。

大致实现代码如下:

let balls = [];
let ballNum = 6;
let firstBall = null;
while(ballNum--) {
  let ball = new Ball(20, parseColor(Math.random() * 0xffffff))
  ball.x = Math.random() * width;
  ball.y = Math.random() * height;
  balls.push(ball)

  if (!firstBall) {
    firstBall = ball
    ball.angle = 0
  } else {
    const dx = ball.x - firstBall.x,
          dy = ball.y - firstBall.y;

    ball.angle = Math.atan2(dy, dx);
  }
}

// 尝试让球连线是一个正多边形
balls = balls.sort((ballA, ballB) => {
  return ballA.angle - ballB.angle
})

这样在最后绘制连线的时候,遍历数组就能按照角度从小到大来绘制了。

效果如下:

这样是能极大的减少交叉线的情况,可还是无法完全避免。

接下来,想尝试优化这个方案,比如angle用Math.abs来取正,或者每一个点都找夹角最小的点来连线。可是结果都不行,无法避免交叉线。

基于中心点旋转

后面又想到一个思路,如果能确定多边形的中心点,那么分别计算所有点相对于中心点的夹角,就能以顺时针或者逆时针来连接这些点。

可是在网上找了半天,所有点算法里面,都是要求有一系列按某个时针顺序排列的点。

可是如果我有这些点,就已经能绘制多边形了。只好放弃

X轴两极点分割

无奈之下只好找Google,然后就发现了知乎上的一个答案挺好的:如何将平面上无序的一组点连成一个简单多边形?

具体算法描述,大家看那个答案就好,我就不赘述了。

不过在连接上链和下链的时候,其实只要保证上链是X轴降序连接,下链是X轴升序连接即可(以逆时针方向绘制)。至于X轴相同的点,不管是优先Y轴大的还是小的都可以。

实现的时候,是严格按照答案里面的算法实现的。

在判断一个点是属于上链还是下链的时候,一开始想的是基于两点确定直线的函数方程,再引入点的坐标来计算。不过后面想到,所有的点都以最左边的极点来计算斜角,然后根据角度大小来划分,视觉上更好理解。

大致代码如下:

let balls = [];
let tempBalls = [];
let ballNum = 6;
let isDragingBall = false;

while(ballNum--) {
  let ball = new Ball(10, parseColor(Math.random() * 0xffffff))
  ball.x = Math.random() * width;
  ball.y = Math.random() * height;
  tempBalls.push(ball)
}

// 让点按X轴升序排序
tempBalls = tempBalls.sort((ballA, ballB) => {
  return ballA.x - ballB.x
})

// 找X轴左右极点
let firstBall = tempBalls[0],
    lastBall = tempBalls[tempBalls.length -1];
let smallXBalls = tempBalls.filter(ball => ball.x === firstBall.x),
    bigXBalls = tempBalls.filter(ball => ball.x === lastBall.x)

// 处理左右极点有多个的情况
if (smallXBalls.length > 1) {
  smallXBalls.sort((ballA, ballB) => {
    return ballB.y - ballA.y
  })
}
if (bigXBalls.length > 1) {
  bigXBalls.sort((ballA, ballB) => {
    return ballB.y - ballA.y
  })
}

firstBall = smallXBalls[0]
lastBall = bigXBalls[0]

// 获得极点连线的角度
let splitLineAngle = Math.atan2(lastBall.y - firstBall.y, lastBall.x - firstBall.x);
let upperBalls = [],
    lowerBalls = [];

// 所有其他点跟firstBall计算角度
// 大于splitLineAngle的都是下链
// 其他是上链
tempBalls.forEach(ball => {
  if (ball === firstBall || ball === lastBall) {
    return false
  }
  let angle = Math.atan2(ball.y - firstBall.y, ball.x - firstBall.x);
  if (angle > splitLineAngle) {
    lowerBalls.push(ball)
  } else {
    upperBalls.push(ball)
  }
})

// 处理X轴相同情况的排序
lowerBalls = lowerBalls.sort((ballA, ballB) => {
  if (ballA.x !== ballB.x) {
    return ballA.x - ballB.x
  }
  return ballB.y - ballA.y
})

upperBalls = upperBalls.sort((ballA, ballB) => {
  if (ballA.x !== ballB.x) {
    return ballB.x - ballA.x
  }
  return ballB.y - ballB.x
})

// 逆时针连接所有的点
balls = [firstBall].concat(lowerBalls, [lastBall], upperBalls)

balls = balls.map((ball, i) => {
  ball.text = i + 1;
  return ball
})

最终返回的balls,就是按逆时针排序的多边形的点了。

效果如下:

最终效果图

各个球的内部状态如下:

各个球的状态

有兴趣的,欢迎看看在github上面查看完整源代码

起因

最近在做一个手机站点,服务器用的Google Cloud,但是用户多是国内的。所以在提升页面加载和响应速度这块,是很重视的。

一开始是想使用manifest.json来做Application Cache, 不过后面发现Google和FireFox都有提到打算废弃这个标准并建议大家改用Service Worker。

可是我要做到站点是文章居多的偏新闻性质的,而且交互也不多,用Service Worker来做PWA,感觉有点大材小用。所以想找一些更好实践,而且对于css和js资源的缓存和快速加载有意义的方案。

prefetch和preload就出现在眼前了。

关于prefetch和preload

具体的技术介绍,这里就不详述,大家可以看看MDN的这篇文章

这里说一下preload和prefetch的区别:

preload是通知浏览器尽快的去加载当前页面现在或者将来需要的一些资源文件(css,js,image,viedo,audio,fonts等等),这个加载跟页面解析是并行的,不会阻塞页面本身的加载。

prefetch是一种预测,预测访问当前页面A的用户很可能会访问页面B,所以在页面A提前告知浏览器去加载页面B的资源。这个提前加载的优先级最低,不会干扰页面的正常资源的加载解析,也不会干扰preload。

兼容性

preload在pc上,chrome和firefox支持比较好, eage是还在开发状态,safari的最新版本也已经支持了。在手机端的支持就不是很好,Andriod 需要5之后的webview才支持,国内的UC,QQ和Baidu都不支持;iOS是最新的11.3开始支持

具体参看:caniuse preload

prefetch的兼容性,相对好点,除了MacOS和iOS上的safari不支持之外,其他浏览器都支持了。

具体参看:caniuse prefetch

另外,preload和prefetch都是申明性质的,所以就算不支持,也不会影响现有页面的任何功能

关于站点的webpack配置

为了方便js和css资源的缓存,在nginx上是配置了很长的过期时间,所以对应的,css和js的编译后的文件名都做了hash后缀。这样,线上的更新,都是文件的替换而不是刷新缓存。

为了方便的实现html里面css和js资源文件能在编译后自动更新路径,使用了HtmlWebpackPlugin来实现html的编译和输出

而在HtmlWebpackPlugin本身也有插件机制,其中一个由GoogleChromeLabs开发的插件PreloadWebpackPlugin,就能方便的给编译的html文件添加相关资源的preload和pretch

具体实现(针对多页面多Entry情况)

如果你是一个单页面应用,那么一切都很简单,就不多说了。这里主要说一下在处理多页面的输出时的注意点

首先,说一下我的几个module的版本:

"webpack": "^4.10.2",
"html-webpack-plugin": "^3.2.0",
"preload-webpack-plugin": "^3.0.0-alpha.3",

PreloadWebpackPlugin,我一开始用的是正式版的v2.3.0,可是我的webpack是4.x版本,有点不兼容。按照官方issues上的解决方案,改为安装了preload-webpack-plugin@latest,然后才可以的。

HtmlWebpackPlugin多页面配置

首先,你需要配置好HtmlWebpackPlugin,不同的页面使用不同的template,编译到不同的filename,同时还要指定哪些chunks需要加载到页面里面。

我的站点是目前包括四个页面index.html, book.html, content.html和search.html,对应配置如下:

    new HtmlWebpackPlugin({
      title: 'index',
      template: './src/pages/index/index.html',
      filename: 'index.html',
      chunks: ['index']
    }),
    new HtmlWebpackPlugin({
      title: 'book',
      template: './src/pages/book/book.html',
      filename: 'book.html',
      chunks: ['book']
    }),
    new HtmlWebpackPlugin({
      title: 'content',
      template: './src/pages/content/content.html',
      filename: 'content.html',
      chunks: ['content']
    }),
    new HtmlWebpackPlugin({
      title: 'search',
      template: './src/pages/search/search.html',
      filename: 'search.html',
      chunks: ['search']
    }),

完成这一步之后,编译,dist目录应该有对应的四个文件,里面的资源也能正确加载。接下来就是配置preload了

preload配置

PreloadWebpackPlugin这个插件更适用于单页面应用,对于多页面应用,在配置上比较繁琐一点。

首先,需要使用excludeHtmlNames来反向排除所有页面,只关联你要配置的那个页面,

再然后,使用include选项来明确指定哪些资源要被preload

最后,每一个页面对应添加一个PreloadWebpackPlugin的实例

还有一点需要补充说明一下,在webpack配置的plugins里面,PreloadWebpackPlugin的配置需要在HtmlWebpackPlugin之后,不然PreloadWebpackPlugin找不到HtmlWebpackPlugin提供的hooks,会报错。

完成上面这一步之后的配置如下:

    new HtmlWebpackPlugin({
      title: 'index',
      template: './src/pages/index/index.html',
      filename: 'index.html',
      chunks: ['index']
    }),
    new HtmlWebpackPlugin({
      title: 'book',
      template: './src/pages/book/book.html',
      filename: 'book.html',
      chunks: ['book']
    }),
    new HtmlWebpackPlugin({
      title: 'content',
      template: './src/pages/content/content.html',
      filename: 'content.html',
      chunks: ['content']
    }),
    new HtmlWebpackPlugin({
      title: 'search',
      template: './src/pages/search/search.html',
      filename: 'search.html',
      chunks: ['search']
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      excludeHtmlNames: ['book.html', 'content.html', 'search.html'],
      include: ['index']
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      excludeHtmlNames: ['index.html', 'content.html', 'search.html'],
      include: ['book']
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      excludeHtmlNames: ['index.html', 'book.html', 'search.html'],
      include: ['content']
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      excludeHtmlNames: ['index.html', 'book.html', 'content.html'],
      include: ['search']
    }),

可以看到,在excludeHtmlNames的配置上比较繁琐,如果页面多的话,最好写个函数来实现过滤。

prefetch的配置

一个PreloadWebpackPlugin实例同时只能干一件事情,根据rel字段区分,要么是preload,要么是prefetch。所以,还得新增PreloadWebpackPlugin的实例。

不过不着急,先想好如何配置prefetch。在我的这个站点里面,在index页面,大部分的链接都是指向的book页面,所以可以在index页面prefetch book页面的资源。同样的还有,在book页面prefetch content页面的资源。

定好之后,就是配置了,跟preload的配置比较类似,需要区分的就是rel和include这两个选项

rel需要改成prefetch,这不用多说。include,这个时候需要改成你打算prefetch的页面的资源,而不是当前页面的资源。

最终的配置如下:

    new HtmlWebpackPlugin({
      title: 'index',
      template: './src/pages/index/index.html',
      filename: 'index.html',
      chunks: ['index']
    }),
    new HtmlWebpackPlugin({
      title: 'book',
      template: './src/pages/book/book.html',
      filename: 'book.html',
      chunks: ['book']
    }),
    new HtmlWebpackPlugin({
      title: 'content',
      template: './src/pages/content/content.html',
      filename: 'content.html',
      chunks: ['content']
    }),
    new HtmlWebpackPlugin({
      title: 'search',
      template: './src/pages/search/search.html',
      filename: 'search.html',
      chunks: ['search']
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      excludeHtmlNames: ['book.html', 'content.html', 'search.html'],
      include: ['index']
    }),
    new PreloadWebpackPlugin({
      rel: 'prefetch',
      excludeHtmlNames: ['book.html', 'content.html', 'search.html'],
      include: ['book']
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      excludeHtmlNames: ['index.html', 'content.html', 'search.html'],
      include: ['book']
    }),
    new PreloadWebpackPlugin({
      rel: 'prefetch',
      excludeHtmlNames: ['index.html', 'content.html', 'search.html'],
      include: ['content']
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      excludeHtmlNames: ['index.html', 'book.html', 'search.html'],
      include: ['content']
    }),
    new PreloadWebpackPlugin({
      rel: 'preload',
      excludeHtmlNames: ['index.html', 'book.html', 'content.html'],
      include: ['search']
    }),

以上,就能顺利的实现,在webpack编译过程中,在html页面里面自动配置上preload和prefetch。希望能帮到大家

问题

今天公司运营要做拿之前的一个活动翻新重开一次,调整了分享文案,结果发出去之后,在自己这边是这样的:

但是在其他人看是这样的:

尝试解决

一开始以为是图片本身的问题,尝试过png 转 jpg, 减小图片质量来减少体积,减少图片尺寸等等方法,都还是没法让其他人可见。

然后尝试着把分享配置回滚为上周的,结果是正常的:

想着是不是上周的图片是好的。

就又把上周的图片搬过来用,可又不可见。

最终解决

用微信开发者工具来debug整个分享流程也都是没问题的。

这个时候有点懵了,Google了一下,发现有人说可能是分享文案导致的。

综合前面问题排查的情况,觉得很有可能,因为我们的分享文案里面是有现金红包这样的词汇的。

让运营同学调整了一下分享文案,再重试,可以了:

看来的确是分享文案的锅。后面又去找微信官方的说明,几番转折,找到了官方的一些说明:

微信外部链接内容管理规范

朋友圈管理常见问题

而在微信JS-SDK说明文档的分享接口部分,也做了一个说明:

请注意不要有诱导分享等违规行为,对于诱导分享行为将永久回收公众号接口权限,详细规则请查看:朋友圈管理常见问题

算是官方比较正式的说明了。

希望上述内容对大家能有帮助。

更新:2018/06/08

发现一个小的手段去规避这个问题,就是在敏感词之间填充一些字符,比如空格。这样微信就不会屏蔽图片了

参考链接:

微信 JS-SDK 分享图片不显示

最近在看Google的机器学习速成课程MLCC

中午的时候在看关于pandas这个数据分析库的介绍

中间有一个用直方图显示数据集的代码:

import pandas as pd
california_housing_dataframe = pd.read_csv("https://storage.googleapis.com/mledu-datasets/california_housing_train.csv", sep=",")
california_housing_dataframe.hist('housing_median_age')

在这个介绍里面可以执行代码显示出好看的直方图出来。

我这边想本地也能执行出这样的效果,就依次安装了pandasmatplotlib,然后执行上述代码

结果只返回了一句:

array([[<matplotlib.axes._subplots.AxesSubplot object at 0x1072c14e0>]],
dtype=object)

按照matplotlib官网对于macosx下图形不显示的一个修复指南折腾了一个下午,还是不显示

最后偶然在stackoverflow上看到一个方案

在代码前方加上matplotlib.pyplot的引用:

import matplotlib.pyplot as plt

再在执行hist完成之后加上:

plt.show()

最终执行,就会新出一个图形弹窗显示直方图:

同时底下也有人对这个情况做出了解释:

Inside a GUI such as an IPython notebook, you may not need to call plt.show() depending on how the GUI is configured. If you run the Python script from the command line, then you would have to call plt.show()

有一些GUI的环境里面执行,会自动的去展示相关的结果,但是在命令行里面,就需要手动的调用plt.show()

使用es6学canvas游戏开发

最新在看一本书《HTML5+JavaScript动画基础》, 基于这本书来学习Canvas游戏制作的。书很不错,介绍了很多游戏的基础概念。不过书里面的代码都是基于ES5编写的,现在是2018年了,所以打算边看边改写书里面的代码为ES6版本的。

前天晚上在做ch03/01-rotate-to-mouse.html这个例子,当时的代码大致如下:

    import {captureMouse} from '../include/utils.js'
    import Arrow from './classes/arrow.js'
    window.onload = () => {
      const canvas = document.getElementById('canvas')
      const context = canvas.getContext('2d')
      let mouse = captureMouse(canvas)
      let arrow = new Arrow()

      (function drawFrame () {
        window.requestAnimationFrame(drawFrame, canvas)
        context.clearRect(0, 0, canvas.width, canvas.height)
        const dx = mouse.x - arrow.x
        const dy = mouse.y - arrow.y
        arrow.rotation = Math.atan2(dy, dx)
        arrow.draw(context)
      }())
    };

诡异的问题

上面看着是没有任何问题的,可是执行的时候,一直报错:

Uncaught ReferenceError: arrow is not defined

当时就有点懵,没道理啊,drawFrame跟arrow的定义在同一个作用域,那drawFrame函数内部作用域里面肯定是能访问到外部作用域的arrow的,怎么可能没定义

当时有点怀疑自己是不是ES6没学好,这种IIFE的自执行函数难到在严格模式下有什么特殊的行为?

为了证实自己的猜测,改写了一下代码:

    import {captureMouse} from '../include/utils.js'
    import Arrow from './classes/arrow.js'
    window.onload = () => {
      const canvas = document.getElementById('canvas')
      const context = canvas.getContext('2d')
      let mouse = captureMouse(canvas)
      let arrow = new Arrow()

      function drawFrame () {
        window.requestAnimationFrame(drawFrame, canvas)
        context.clearRect(0, 0, canvas.width, canvas.height)
        const dx = mouse.x - arrow.x
        const dy = mouse.y - arrow.y
        arrow.rotation = Math.atan2(dy, dx)
        arrow.draw(context)
      }
      drawFrame()
    };

果然,代码顺利运行了。

然后我就以es6, scope, function, iife, variable, let这几个关键词苦苦Google,查了一堆网页,可就是没找到到底这个作用域是怎么影响的

为看缩小问题范围,于是重新写了一个demo:

    function foo(){
      var a = 1
      let b = 2
      (function bar() {
          console.log(a)
          console.log(b)
      }())
    }
    console.log(foo());

结果a能被正确log出来,b还是not defined

这下更证明自己的猜想了。可是其中的原理还是不懂

stackoverflow上的大牛

无奈之下,在stackoverflow上面发起了一个问题:a variable defined with let is not defined in a same scope IIFE

很快就有一个热心的大牛T.J. Crowder帮忙给编辑了一下问题,修复了一些语法上的错误,优化了代码展示。

然后这位大牛又顺便给解答了问题

答案跟我一直猜想的方向完全不一致,一切都是ASI(Automatic Semicolon Insertion)导致的。demo里面的代码,在实际被解析的时候,是大致长这样的:

    function foo(){
      var a = 1
      let b = 2(function bar() {
        console.log(a)
        console.log(b)
      }());
    }
    console.log(foo());

b的赋值和iife的执行连接到一起了,这也就是为什么函数体内b是not defined的原因,iife的执行先于b的定义

深入理解

关于ASI,是有了解的,不过这个知识点在我脑海里是跟代码压缩绑定在一起。初学js的时候,遇到过分号缺失导致的压缩代码执行错误,所以在js文件里面写代码的时候,会很注意这方面。

而这次是在html的script标签里面写代码,想着这些代码又不会被手动压缩,自然就没想过ASI的问题。

可实际上,任何js代码在解析执行的时候,都会在必要的时候经由解析器执行ASI来”补全分号”

总结

只要你在写js, 不管是在js文件里面还是script标签里面,分号都是一个值得严肃对待的事情

关于ASI的详细描述,可以参看以下两篇文章(我也是刚刚看的):

备胎的自我修养——趣谈 JavaScript 中的 ASI (Automatic Semicolon Insertion)

JavaScript ASI 机制详解

重构



最新在对之前开发的一个美食类的小程序《今天吃什么星人》做重构。这个是我做的头一款小程序,基于labrador框架来开发的。小程序开发上架之后,就没再动过。后面大家发现这款小程序慢慢有点人气,打算拿出来再优化优化看看

大家重新设计了首页和美食页,然后轮到我这边来开发。

从初次开发到这次打算改版,已经又进行过三个小程序的开发,也算积累了不少经验。labrador, mpvue, wepy 这三款比较流行的框架都有接触和使用,其中最喜欢的算是mpvue了。

这次的改版,改动没多大,主要在UI上,所以是没打算做架构上的大改动。可是重构的时候遇到一些编译上的问题解决不了,而labrador已经被开发者放弃,好几个月没更新了。再想想这个项目后续可能还会有不少的改动。最后还是决定使用mpvue来改写一遍。

重构的时候,考虑到需要参照着之前的代码来改,就没用git checkout --orphan这样的方式新建分支,而是单独新建了一个同级文件夹来放置新代码。这样新旧代码可以同时参看,也可以使用微信开发者工具同时预览效果对比

代码提交

很快,整个项目重构完成了。这个时候,想往之前的项目里面新建一个new分支,把当前的新版的master分支提交过去。

一开始执行的git push;git push --tags, 提示No configured push destination

这才想起来,忘记配置项目的远程分支了:git remote add origin git@git.coding.net:xxxxx.git

然后再执行git push,提示被reject了。

直接git push,默认是推送到origin的master分支,这两个版本的分支进度不一样,所以被拒绝了。

调整命令,改成git push origin new:master。意思是推送本地的master分支到origin端的new分支

推送成功,到coding的后台一看,也有新分支了。代码算是初步提交成功

后续优化

接下来,执行git branch -u origin/new 使得本地的master分支能跟踪origin端的new分支,这样pull的时候,能顺利拉取。

完成之后,再尝试了一次git push,发现还是reject,原来是我的全局git配置里面配置了git.default 为 current。其作用是设定git push的默认行为是把当前分支推送到origin端同名的分支。所以在当前场景下,就会被reject

但是我又不想修改全局的配置。好在git是可以按项目来单独配置并覆盖全局配置的

执行git config push.default upstream,这样就使得git push的默认行为是推送到跟踪的远端分支

再执行git push,提示Everything up-to-date,成功!

参考资料:

  1. https://stackoverflow.com/questions/948354/default-behavior-of-git-push-without-a-branch-specified
  2. https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E8%BF%9C%E7%A8%8B%E5%88%86%E6%94%AF
  3. https://gist.github.com/seanbuscay/5877413

动效对于页面状态的转移是很必要的,大部分时候的动效可以用css+js来实现,不过有些时候,因为产品上的需要,就需要使用按帧播放的动画来实现。其中比较常见的就是gif动图。

就比如我这边最近在项目里面用到的一个动图,原始图如下:

图片体积1.4MB,这么大的图片,肯定是不适合用来做动图。所以需要针对这张图做一些处理。

这里就要说到一个压缩gif图很专业的网站ezgif

经过裁剪,隔帧抽取,糢糊三步之后,最终的动图如下:

184kb

完成上述三步之后,出来的动图是184kb,基本上已经无法优化了。

不过如果网站的条件允许的话,还是有方法优化的。这就是gif to video

gif图本质上就是一个帧动画,里面存储了N张图片,然后按一定的顺序播放。它的存储是比较死板的,哪怕前后两帧的图片非常相似甚至是一模一样,它都会按两张图片来存储。在video里面就不一样了,视频格式会对所存储的帧画面做各种各样的优化。

还是以上面的动图为例,把最终效果的gif图转换为mp4格式:

转换后的视频体积是79kb,比gif图少了58.66%,压缩效果还是很明显的

问题

今天在重构之前写的一个小程序页面,在修改scroll-view组件之后,发现之前的scroll-into-view的跳转失效了

之前的页面是一直正常的,所以官方的关于scroll-view的一些tips是不会关联了

tip: 请勿在 scroll-view 中使用 textarea、map、canvas、video 组件
tip: scroll-into-view 的优先级高于 scroll-top
tip: 在滚动 scroll-view 时会阻止页面回弹,所以在 scroll-view 中滚动,是无法触发 onPullDownRefresh
tip: 若要使用下拉刷新,请使用页面的滚动,而不是 scroll-view ,这样也能通过点击顶部状态栏回到页面顶部

再仔细看官方的说明,发现有这么一句

使用竖向滚动时,需要给<scroll-view/>一个固定高度,通过 WXSS 设置 height。

当前我的页面的scroll-view是能滚动的,不过说不定问题可能出在这里,因为高度设定的100%

先尝试着手动把scroll-view改成一个比较小的高度,再试试调整scroll-into-view的值,发现可以了,那问题就出在height上了。

原因

在旧版本小程序里面navBar是系统自带的,设置height: 100%是能正确展示。

而新版本里面,在app.json里面配置了navigationStyle: 'custom',自己实现了一个自定义样式的导航栏。在调整样式的时候,会让内容区域margin-top: 128px;,这样就不会跟自定义的navBar冲突。

问题应该就出在这了。

scroll-view的父级只有margin-top,没有一个固定的高度,使得scroll-view的height: 100%失效,只要能重新计算出一个准确的高度,那问题就能完美解决。

解决

本来有想过调整scroll-view的父级,不过会是一个比较罗嗦的方法,所以想试试一些css里面的新技术:

scroll-view {
  height: calc(100vh - 128px);
}

基于calc()vh来动态计算scroll-view的高度

试了下效果,正确运行!

为了保证在各个设备和系统下能正确工作,查了一下caniuse:

vh

calc

可以看到这两个特性在iOS和Andriod下都是早早就支持了,可以安心使用的

总结

scroll-view需要一个明确的高度,这个高度的实现方式有两种:

  • 自身的css样式里面设置了明确的高度,vh, px, rem, em等等都可以
  • 自身的css样式里面height: 100%;同时直接父级有一个明确的高度