初始化 npm 项目

yarn init

添加依赖

yarn add hapi

添加开发依赖

要在开发中使用 TypeScrip,同时至少需要有一个工具,可以一直监听项目文件的变更,并实时的将变更更新至启动的服务中,我选择使用 Nodemon,首先添加以下几个开发依赖

yarn add typescript -D
yarn add nodemon -D

接下来,我们需要为 nodehapi 安装类型定义库:

yarn add @types/node -D
yarn add @types/hapi -D

安装完成之后, package.json 文件看起来像下面这样的:

{
  "name": "hapiserver",
  "version": "0.0.1",
  "description": "API server",
  "main": "index.js",
  "author": "Your Name",
  "license": "MIT",
  "dependencies": {
    "hapi": "^18.1.0"
  },
  "devDependencies": {
    "@types/hapi": "^18.0.2",
    "@types/node": "^12.0.2",
    "nodemon": "^1.19.0",
    "typescript": "^3.4.5"
  }
}
注意:你的 dependenciesdevDependencies 配置中,版本号可能与我的不同。

配置 TypeScript

设计项目文件目录结构

在项目的根目录下,创建一个名为 src 的目录,用于包含系统的所有源代码文件,接着,创建一个名为 dist 的目录,用于保存由 typescript 编译后的 javascript 文件。

注意:文件结构并不是强制的,你可以完全按照自己的习惯和规范来进行
.
├── dist
├── node_modules
├── package.json
├── src
└── yarn.lock

tsconfig.json

TypeScript 会查询名为 tsconfig.json 的配置文件来查找项目的入口文件以及编译设置,关于它的详细使用说明,可以从 https://www.typescriptlang.org/docs/handbook/tsconfig-json.html 查阅,在这里,我们先填入以下内容:

{
  "compilerOptions": {
    "outDir": "./dist",
    "allowJs": false,
    "target": "es6",
    "sourceMap": true,
    "module": "commonjs",
    "moduleResolution": "node"
  },
  "include": ["./src/**/*"],
  "exclude": ["node_modules"]
}

tsconfig.json 文件中,我们定义了 outDir 的值为 ./dist,它告诉编译器,编译后的输出目录为 ./dist 文件夹,现在可以直接在项目根目录执行以下代码,即可编译 src 目录下的 TypeScript 代码至 dist 目录下的 JavaScript 文件了。

node_modules/typescript/bin/tsc

用 TypeScript 开发 Hapi 服务应用

src 目录下,创建一个名为 server.ts 的文件,内容如下:

import * as hapi from "hapi";

// 创建一个服务器,监听 `localhost` 上的 `8000` 商品
const server: hapi.Server = new hapi.Server({
  host: "localhost",
  port: 8000
});

// 添加路由
server.route({
  method: "GET",
  path: "/hello",
  handler: function(request, h) {
    return "Hello! TypeScript!";
  }
});

// 启动服务
async function start() {
  try {
    await server.start();
  } catch (err) {
    console.log(err);
    process.exit(1);
  }
  console.log("Server running at:", server.info.uri);
}

// 不要忘记启动服务
start();

由于我们的代码是由 TypeScript 写的,所以现在还没有办法直接运行,需要先将其编译为 JavaScript 代码之后再运行:

使用下面的命令编译代码:

node_modules/typescript/bin/tsc

编译完成之后,将得到下面这样的两个文件:

dist
├── server.js
└── server.js.map

此时,执行下面的代码,启动服务:

node dist/server.js

启动成功之后,终端将显示:

Server running at: http://localhost:8000

使用 curl 测试一下我们的服务:

$ curl -i http://localhost:8000/hello
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
cache-control: no-cache
content-length: 18
accept-ranges: bytes
Date: Fri, 17 May 2019 01:58:50 GMT
Connection: keep-alive

Hello! TypeScript!

已经启动成功了。

完成所有配置

我们总不能每改一次代码,都手工执行一次编译,再重新启动服务,可以在 package.json 中添加两个命令:

{
  ...
  "scripts": {
    "start": "./node_modules/nodemon/bin/nodemon.js -e ts  --exec \"yarn run compile\"",
    "compile": "tsc && node ./dist/server.js"
  },
  ...
}

现在,只需要在项目根目录下执行以下代码,即可启动一个实时编译代码并自动重新服务的开发环境了:

yarn start

它的作用是:nodemon 启动一个服务,监听文件的变更,当有任何文件变更之后,执行 yarn run compile 命令(即执行:tsc && node ./dist/server.js,以重启服务。

初始化一个应用

使用 react-native init AppName 命令初始化一个 React Native 应用

react-native init Chongai
mv Chongai chongai-app
cd chongai-app
我习惯性的使用 CamelCase 规范命名 App 名称,同时将文件夹改为 foobar-app 方式,上面那一行 mv 命令不一定都需要,我是为了保证项目的 git 地址没有大写字母

设置 git

git init
git remote add origin git@git.domain.com:namespace/project-name.git
git push -u origin master

安装 PrettierESLint

在本地项目中安装开发依赖

yarn add -D eslint prettier
yarn add -D eslint-config-airbnb
npx install-peerdeps --dev eslint-config-airbnb
yarn add -D eslint-config-prettier eslint-plugin-prettier

添加 .eslintrc.json 文件:

{
  "extends": ["airbnb", "prettier"],
  "plugins": ["prettier"],
  "rules": {
    "prettier/prettier": ["error"]
  }
}

添加 .prettierrc

{
  "printWidth": 100,
  "singleQuote": true
}

配置编辑器

我使用的是 VSCode:

分别安装 ESLintPrettier 插件。

设置配置项目:

{
  ...
  "editor.formatOnSave": true
  ...
}

安装常用库

yarn add react-native-gesture-handler
react-native link react-native-gesture-handler

yarn add react-navigation

yarn add react-native-video
react-native link react-native-video

yarn add react-native-camera
react-native link react-native-camera

yarn add react-native-device-info
react-native link react-native-device-info

yarn add react-native-image-picker
react-native link react-native-image-picker

早期的身份证号码称之为社会保障号,为15位,1999年开始,更名为公众民份证号码,即第二代身份证号码,为18位,且终身不变。

公民身份证号码是特征组合码,由前17位数字本体码和最后一位数字校验码组成,排列顺序从左至右依次为六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。

地址码: 表示编码对象常住户口所在县(市、旗、区)的行政区划代码。对于新生儿,该地址码为户口登记地行政区划代码。需要没说明的是,随着行政区划的调整,同一个地方进行户口登记的可能存在地址码不一致的情况。行政区划代码按GB/T2260的规定执行。

出生日期码:表示编码对象出生的年、月、日,年、月、日代码之间不用分隔符,格式为YYYYMMDD,如19880328。按GB/T 7408的规定执行。原15位身份证号码中出生日期码还有对百岁老人特定的标识,其中999、998、997、996分配给百岁老人。

顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。

校验码: 根据本体码,通过采用ISO 7064:1983,MOD 11-2校验码系统计算出校验码。算法可参考下文。前面有提到数字校验码,我们知道校验码也有X的,实质上为罗马字符X,相当于10。

15 位与 18 位身份证号码的差异

  • 出生日期码:15位身份证号码中出生日期码为6位,其中年份代码仅有2位,如590328,代表1959年生。
  • 校验码:15位身份证号码中无校验位。

校验码算法

将本体码各位数字乘以对应加权因子并求和,除以11得到余数,根据余数通过校验码对照表查得校验码。

加权因子如下:

位置序号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17
-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-
加权因子 | 7 | 9 | 10 | 5 | 8 | 4 | 2 | 1 | 6 | 3 | 7 | 9 | 10 | 5 | 8 | 4 | 2

校验码:

余数 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
-|-|-|-|-|-|-|-|-|-|-|-
校验码 | 1 | 0 | X | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2

比如本体码为 11010519491231002

  1. 各位数与对应加权因子乘积求和:

    1 * 7 + 1 * 9 + 0 + 10 + ... + 0 * 2 = 167
  2. 对求和进行除 11 得余数 167 % 11 = 2
  3. 根据余数 2 对照校验码得 X

因此完整身份证号码为: 11010519491231002X

默认参数


```
var  link  =  function(height  =  50,  color  =  'red',  url  =  'http://azat.co')  {
...
}
```

箭头函数

var fn = arg1 => arg1 * 2

多行字符串

var text = `this is
 text

模板表达式


```
var  name  =  `Your  name  is  ${first}  ${last}`
```

Promise

new Promise((resolve, reject) => {
    if (success) {
        resolve(true)
    }
    else {
        reject('error')
    }
}

块级作用的 let 和 const

let writableVariable = 1
const constantVariable = 2

class Obj {
    constructor (props) {
      this.props = props
    }

    log () {
        console.log(this.props)
    }
}

模块化

export const edit = () => console.log('edit')
import { edit } from './edit.js'

拆包表达式


    ```
    let obj = { name: '', slug: '' }
    let { name,: title  slug } = obj
```

改进的对象表达式

首先,一个简单的JavaScript时间线,不了解历史的人也无法创造历史。

  1. 1995年:JavaScript以LiveScript之名诞生
  2. 1997年:ECMAScript标准确立
  3. 1999年:ES3发布,IE5非常生气
  4. 2000年-2005年:XMLHttpRequest,熟知为AJAX,在如Outlook Web Access(2002)、Oddpost(2002)、Gmail(2004)、Google Maps(2005)中得到了广泛的应用
  5. 2009年:ES5发布(这是我们目前用的最多的版本),带来了forEach / Object.keys / Object.create(特地为Douglas Crockford所造,JSON标准创建者) ,还有JSON标准。

历史课上完了,我们回来讲编程。

ES6中还有很多你可能都用不上(至少现在用不上)的可圈可点的特性,以下无特定顺序:

  1. Math / Number / String / Array / Object中新的方法
  2. 二进制和八进制数据类型
  3. 自动展开多余参数
  4. For of循环(又见面了CoffeeScript)
  5. Symbols
  6. 尾部调用优化
  7. generator
  8. 更新的数据结构(如MapSet

如果按面向对象的思路去讲 JavaScript 的 new,还是很难去理解,我们可以从另一个方向去理解一下它。

你这些人类

我是一名程序员,也是一个人,我可能:

  • 有一个响亮亮的名称
  • 在某一天出生
  • 是个男人
  • 我能行走
  • 我还能跑步
  • 还能跳跃
  • 能说话
  • 我还能写代码

那么,在 JavaScript 中,我们可能像下面这样表达我:

const me = {
  name: '大胡子农同工潘半仙',
  birth: '1988-08-08',
  sex: 'male',
  walk: function (speed, direction, duration) {
    // 以 speed 的速度向 direction 方向行走 duration 长的时间
  },
  run: function (speed, direction, duration) {
    // 像跑步一样,速度
  },
  jump: function (high, direction, angle) {
    // 以 angle 角度向 direction 方向跳 high 高
  },
  speak: function (letters) {
    // 说出 letters 这些词
  },
  coding: function (language, line) {
    // 写程序呢
  }
}

你们这些人类

当然,这个世界上不可能只有我一个程序员,更不可能只有我一个人,就像我们这个小公司,就有七八百人,似乎所有这些人的数据都保存在数据库里面:

namesexbirth
潘韬male1988-08-08
高超male1985-08-09
春雨male1999-08-08

我们从数据库中查询出上面这几条记录,在 JavaScript 可能表示为一个二维数据,然后要创建出这三个人来,可能是下面这样的:

const people = DB.query()
// people = [['潘韬', 'male', '1988-08-08'], [...], [...]]
for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    name,
    sex,
    birth,
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
  }
}

重复的资源占用

上面大家已经发现,像上面这样去创建三个对象, walkrunjumpspeakcoding 这五件能做的事情(方法),其实做法都一样,但是我们却重复的去描述该怎么做了,其实就占用了很多资源,所以,我们可能会像下面这样改进一下:

const walk = function walk () {}
const run = function run () {}
const jump = function jump () {}
const speak = function speak () {}
const coding = function coding () {}

for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    name,
    sex,
    birth,
    walk,
    run,
    jump,
    speak,
    coding
  }
}

不同的人共用相同的资源(方法)

但是这个世界不止有人类

对,人类相比于这个世界上的其它生物来讲,数量根本就值得一提,如果像上面这样,可能各种不同物种能做的事情都会要定义出不同的函数,蠕动肯定不是人类会去做的事情,但很多别的生物会做,那么为了代码管理方便,我们把人能做的所有事情都放在一个对象里面,这样就相当于有了一个命名空间了,不会再跟别的物种相冲突:

const whatPeopleCanDo = {
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}
for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    name,
    sex,
    birth,
    ...whatPeopleCanDo
  }
}

原型

但是,有的人可能我们并不知道他的 sex 信息是多少,有的也有可能不知道 birth 是多少,但是我们希望在创建这个人的时候,能给不知道的数据一些初始数据,所以, whatPeopleCanDo 并不能完全的表达出一个人,我们再改进:

const peopleLike = {
    name: '',
    sex: 'unknown',
    birth: '',
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}
for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    ...peopleLike,
    name: name || peopleLike.name,
    sex: sex || peopleLike.sex,
    birth: birth || peopleLike.birth
  }
}

这样一来,我们就可以为不知道的属性加一些默认值,我们称 peopleLike 这个东东就为原型,它表示了像人类这样的物种有哪些属性,能干什么事情。

原型链

虽然上面已经比最开始的版本好得多了,但是还是能有很大的改进空间,我们现在像下面这样改一下:

const peoplePrototype = {
    name: '',
    sex: 'unknown',
    birth: '',
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}
for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    name: name || peoplePrototype.name,
    sex: sex || peoplePrototype.sex,
    birth: birth || peoplePrototype.birth,
    __proto__: peoplePrototype
  }
}

我们不再把人类原型里面的所有方法都绑定到某个人身上,而是像上面这样,用一个特殊的字段 __proto__ 来指定:我的原型是 peoplePrototype 这个对象,同时,我们还制定了一个规则:如果你想请求我的某个方法,在我自己身上没有,那就去我的原型上面找吧,如果我的原型上面没有,那就去我的原型的原型上面去找,直到某个位置,没有更上层的原型为止

像上面这样创建的 people 对象,有自己的属性,但是当我们去访问 people.speak() 方法的时候,其实访问的是 people.__proto__.speak(),这是我们的规则。

更优雅的创建新新人类

我们总不能在需要创建新人的时候,都像上面这样,自己去写一个对象,然后再手工指定它的原型是什么,所以,我们可以创建一个函数,专门用来生成人类的:

const peoplePrototype = {
    name: '',
    sex: 'unknown',
    birth: '',
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}
const makePeople = function makePeople(name, sex, birth) {
  let people = {}
  people.name = name || peoplePrototype.name
  people.sex = sex || peoplePrototype.sex
  people.birth = birth || peoplePrototype.birth
  people.__proto__ = peoplePrototype
  return people
}

people = people.map(makePeople)

现在这样我们只需要引入 makePeople 这个函数就可以随时随地创建新人了。

更优雅一点的改进

显然,上面这样并不是最好的办法,定义了一个原型,又定义了一个原型对象,我们可以把这两个合并到一起,所以,就可以有下面这样的实现了:

const People = function People (name, sex, birth) {
  let people = {}
  people.name = name || People.prototype.name
  people.sex = sex || People.prototype.sex
  people.birth = birth || People.prototype.birth
  people.__proto__ = People.prototype
  return people
}

People.prototype = {
    name: '',
    sex: 'unknown',
    birth: '',
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}

我们直接把创建人类的那个函数叫作 People,这个函数有一个属性叫 prototype,它表示用我这个函数创建的对象的原型是什么,这个函数做的事情还是以前那些事儿,创建临时对象,设置对象的属性,绑定一下原型,然后返回。

神奇的 this

我们除了人,还有别的动物,比如 TigerFish等,按上面的方式,在 Tiger() 或者 Fish() 函数里面都会建立不同的 tiger 或者 fish 名称的临时对象,这样太麻烦,我们把这种函数创建出来的对象,都可以统一叫作“这个对象” ,也就是 this object,不在关心是人是鬼,统一把所有的临时对象都叫 thisObject 或者更简单的就叫作:这个,即 this

const People = function People (name, sex, birth) {
  let this = {}
  this.name = name || People.prototype.name
  this.sex = sex || People.prototype.sex
  this.birth = birth || People.prototype.birth
  this.__proto__ = People.prototype
  return this
}

当然,上面的这一段代码是有问题的,只是假想一样,这样是不是可行。

new

到现在为止,我们发现了整个代码的演变,是时候引出这个 new 了,它来干什么呢?它后面接一个类似上面这种 People 的函数,表示我需要创建一个 People 的实例,它的发明就是为了解决上面这些所有重复的事情,有了 new 之后,我们不需要再每一次定义一个临时对象,在 new 的上下文关系中,会在 People 函数体内自动为创建一个临时变量 this,这个就表示即将被创建出来的对象。同时,对于使用 new 创建的实例,会自动的绑定到创建函数的 prototype 作为原型,还会自动为 People 创建一个 constructor 函数,表示这个原型的创建函数是什么,所以,我们可以改成下面这样的了:

const People = function People (name, sex, birth) {
  this.name = name || People.prototype.name
  this.sex = sex || People.prototype.sex
  this.birth = birth || People.prototype.birth
}

People.prototype.name = ''
People.prototype.sex = 'unknown'
People.prototype.birth = ''
People.prototype.walk = function () {}
People.prototype.run = function () {}
People.prototype.jump = function () {}
People.prototype.speak = function () {}
People.prototype.coding = function () {}

people = people.map(p => new People(...p))

总结

new 到底干了什么?当 new People() 的时候

  1. 创建临时变量 this,并将 this 绑定到 People 函数体里
  2. 执行 People.prototype.constructor = People
  3. 执行 this.__proto__ = People.prototype
  4. 执行 People 函数体中的自定义
  5. 返回新创建的对象

已经入了人生中第一台胶片相机,GSW690 II,是一台 6x9 画幅的120相机,现在主要还是拍黒白的,不过,我也确实拍得不怎么样,但是这个过程还是很不错的,第一次去了北京东方明珠图片社,做了胶片的冲洗、底扫,同时,还直接给第一卷8张冲洗了15寸的照片,不过说实话,冲洗质量真的不怎么样。

华威西里公交站

华威西里公交站

华威西里公交站

华威西里公交站

中国尊

中国尊

潘家园十字路口

潘家园十字路口

金帝广场外的早餐亭

金帝广场外的早餐亭

中国尊

中国尊

中国尊

中国尊

中国尊

中国尊

第一郑基本上就是在试快门了,手机上面装了一个测光App提供测光,还是有很多问题,旁轴机还没有适应,以后慢慢练习吧,就是成本有点高,摄影这个坑总是越跳越深的,这不开始想着要自己冲洗胶片了,查找了一些资料,似乎也不是很难,记录一下学习笔记,往后如果自己冲洗成功了,再来一篇详细点的冲洗笔记。

需要的工具

  • 黑白胶片 Black & white film
  • 135胶片盒开盖器 Cassette end cap remover
  • 剪刀 Scissors
  • 显影罐 Developing tank
  • Spiral
  • Film clips or pegs
  • 计时器 Timer
  • Large measuring cylinders * 3
  • Small measuring cylinder
  • 吸管 Pipette
  • 温度计 Thermometer
  • Squeegee
  • 塑胶手套 Rubber gloves
  • 护目镜 Safety glasses

需要的药水

  • Developer: This makes the image appear on the film (ILFOTEC DD-X)
  • Stop bath: Brings development to an end (ILFOSTOP)
  • Fixer: Makes the developed image permanent
  • Wetting agent: Helps the film dry quickly and evenly

实体店

北京

  • 今日捷成图片社

    • 地址:海淀区羊坊店西路20号
    • 电话:010-51650999
  • 东方明珠图片社

    • 地址:西城区宣武门内大街,西单地铁站下向南
    • 电话:010-66062820
    • 评价:普通彩色负片、黑白就不用往这送了,反转片冲洗还可以
  • Spring Cameras

    • 地址:东城区车辇店胡同52号(近北锣鼓巷)
    • 电话:010-67292366
  • 盛世九州图片社

    • 地址:西城区 官园胡同甲1-1号(近梅兰芳大剧院)
    • 电话:010-66517144
    • 评价:大众评价不太好的一个店

哈尔滨

  • 影友图片社(安发街店)

    • 地址:道里区安发街31号(近安固街)
    • 电话:0451-84548543

广州

  • GZnow青年影像

    • 地址:广州市天河路365号天俊阁1603
    • 电话:020-85165985
  • 八景摄影冲印(华师店)

    • 地址:天河区 华师科技大楼155-156号(地铁华师站E出口)
    • 电话:020-85213937
  • 八景摄影冲印(总店)

    • 地址:海珠区 赤岗西路236-238号金帆大厦1楼(近珠影公交站)
    • 电话:020-84204605 020-89885362

青岛

  • 天堂鸟图片社

    • 地址:市北区延安路174号

淘宝冲扫查询:

我一直玩户外都有拍视频记录的习惯,最近半年一直想着拍VLog(当然以前的只是记录),但是直到现在才真正的试着先计划分镜再拍,第一个,下面任何一个链接都可以看,如果想看4K的,选择 Youtube,国内打不开的话,自己想办法。

下一次还需要注意,我应该再加上下面这些分镜:

  • 起床
  • 收拾装备
  • 关灯出门
  • 电梯间
  • 步行前往地铁站
  • 进入地铁站(展示地铁站名称)
  • 安检
  • 手机进地铁
  • 地铁车辆驶入
  • 进地铁
  • 出地铁
  • 上大巴

前面这些总共不超过36秒,大概3秒一个分镜

大巴到达目地地的时候,下车过程以及刚进山的过程必须得有。

在拍摄过程中,摄像机位写好后,还是没有演好,唉,得努力练习演技了。

最近在做一个新的基于 Flutter 应用,创建应用的命令是:

flutter create -i swift -a kotlin app

依赖了自己开发的 wechat 插件,之后执行 flutter run 的时候,就一直报下面这个错误了:

code's output:
    === BUILD TARGET Runner OF PROJECT Runner WITH CONFIGURATION Debug ===
    While building module 'wechat' imported from /Users/pantao/workspace/gitee/onmr/chain-app/ios/Runner/GeneratedPluginRegistrant.m:6:
    In file included from <module-includes>:1:
    In file included from /Users/pantao/workspace/gitee/onmr/chain-app/build/ios/Debug-iphonesimulator/wechat/wechat.framework/Headers/wechat-umbrella.h:13:
    /Users/pantao/workspace/gitee/onmr/chain-app/build/ios/Debug-iphonesimulator/wechat/wechat.framework/Headers/WechatPlugin.h:2:10: error: include of non-modular header inside framework module 'wechat.WechatPlugin':
    '/Users/pantao/workspace/gitee/onmr/chain-app/ios/Pods/Headers/Public/WechatOpenSDK/WXApi.h' [-Werror,-Wnon-modular-include-in-framework-module]
    #include "WXApi.h"
             ^
    1 error generated.
    /Users/pantao/workspace/gitee/onmr/chain-app/ios/Runner/GeneratedPluginRegistrant.m:6:9: fatal error: could not build module 'wechat'
    #import <wechat/WechatPlugin.h>
            ^
    2 errors generated.

最简单的解决办法:

TARGETS -> Build Settings -> 搜索 Allow Non -> 将 Allow Non-modular includes in Framework Modules 设置为 YES

WX20181224-200614@2x.png

就像 React Native 一样,在 Flutter 应用中,如果需要调用第三方库的方法或者有一些功能需要使用原生的开发来提供,使用 Flutter Plugin 是一种不错的方式,它本质上就是一个 Dart Package,但与其它的 package 不同点在于,Flutter 插件中一般都存在两个特殊的文件夹:androidios,如果需要编写Java、Kotlin或者 Object-C 以及 Swift 代码,我们就需要在这两个文件夹项目中进行,然后通过相应的方法将原生代码中开发的方法映射到 dart 中。

阅读剩余部分

一直都在自己的 React Native 应用中使用 Redux,其实更大情况下也是使用它来管理应用的会话状态以及当前登录的用户信息等等简单的数据,很好用,自从 Google 发布 Flutter 之后,就一直想着拿它来做点啥,准备拿一个新项目开刀,先研究下怎么把以前在 React Native 中需要用到的一些技术在 Flutter 找到对应的实现方法,本文记录下 Flutter + Redux + Redux Persist 的实现。

项目地址:https://github.com/pantao/flutter-redux-demo-app

阅读剩余部分

改 repo 仓库

cd ~/.cocoapods/repos
pod repo remove master
git clone https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git master

完成上面步骤之后,进入项目的 ios/Podfile 文件,在文件第一行前插入下面这一行:

source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'

最近创建旅行星球的星球点亮页,需要在整个屏幕中创建一个可操作并且遵行自然规则的3D地球,哦,不对,星球 —— Travo Planet,本文记录下整个开发过程。

安装 threejs

yarn add three

创建 Planet 类:

import THREE from 'three'

const defaultAspect = {
  width: window.innerWidth,
  height: window.innerHeight
}

const defaultOptions = {
  aspect: {
    ...defaultAspect
  }
}

export default class Planet {
  constructor (container, options = { ...defaultOptions }) {
    this.container = container
    this.options = {
      ...defaultOptions,
      ...options
    }

    this.init(container, options)
  }

init (container, options) {} 
}

之后的所有开发都将在这个文件中进行,要基于 ThreeJS 展示什么,我们至少需要三样东西:一个 scene,一个 camera 以及一个 renderer

  init (container, options) {
    if (this.inited) {
      return this
    }

    const {
      aspect
    } = options

    const scene = this.scene = new THREE.Scene()
    const camera = this.camera = new THREE.PerspectiveCamera(45, aspect.width / aspect.height, 0.01, 1000)
    camera.position.z = 1.5

    const renderer = this.renderer = new THREE.WebGLRenderer()
    renderer.setSize(aspect.width, aspect.height)
  }

scene 的作用用来跟踪与展示我们需要展示的对象们,接下来添加光效,如下图所示,分别为:Ambient lightDirectional light 以及 ·Ambient & Directional light`:

sphere-earth-light.jpg

scene.add(new THREE.AmbientLight(0x333333))
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.position.set(5, 3, 5)
scene.add(directionalLight)

安装极光插件

yarn add jpush-react-native jcore-react-native

链接插件

react-native link
# rnpm-install info Linking jcore-react-native ios dependency
# rnpm-install info Platform 'ios' module jcore-react-native has been successfully linked
# ? Input the appKey for JPush xxxxxxxxx
# patching android/settings.gradle...
# patching android/**/AndroidManifest.xml...
# patching android/**/build.gradle...
# patching ios/**/AppDelegate.m...
# done!
# rnpm-install info Linking jpush-react-native ios dependency
# rnpm-install info Platform 'ios' module jpush-react-native has been successfully linked
# rnpm-install info Platform 'android' module jpush-react-native is already linked

这一步操作会做下面这些操作:

android/settings.gradle 文件中添加下面这几行:

include ':jcore-react-native'
project(':jcore-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jcore-react-native/android')

include ':jpush-react-native'
project(':jpush-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/jpush-react-native/android')

android/app/build.gradle 文件里面添加

android {
  ...
  defaultConfig {
    manifestPlaceholders = [
       JPUSH_APPKEY: "xxxxxxxx",
       APP_CHANNEL : "default"
     ]
  }
  ...
}

android/app/src/main/AndroidManifest.xmlapplicaiton 中添加两个 meta-data

<meta-data android:name="JPUSH_APPKEY" android:value="${JPUSH_APPKEY}" />
<meta-data  android:name="JPUSH_CHANNEL" android:value="${APP_CHANNEL}" />

ios/AppName/AppDelegate.m 中添加:

- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(NSInteger))completionHandler
{
  NSDictionary * userInfo = notification.request.content.userInfo;
  if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    [JPUSHService handleRemoteNotification:userInfo];
    [[NSNotificationCenter defaultCenter] postNotificationName:kJPFDidReceiveRemoteNotification object:userInfo];
  }

  completionHandler(UNNotificationPresentationOptionAlert);
}

- (void)jpushNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler
{
  NSDictionary * userInfo = response.notification.request.content.userInfo;
  if ([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
    [JPUSHService handleRemoteNotification:userInfo];
    [[NSNotificationCenter defaultCenter] postNotificationName:kJPFOpenNotification object:userInfo];
  }

  completionHandler();
}

手动配置

IOS

在 iOS 工程中设置 TARGETS-> BUILD Phases -> LinkBinary with Libraries 找到 UserNotifications.frameworkstatus 设为 optional

在 iOS 工程中如果找不到头文件可能要在 TARGETS-> BUILD SETTINGS -> Search Paths -> Header Search Paths 添加如下路径:

$(SRCROOT)/../node_modules/jpush-react-native/ios/RCTJPushModule

在 xcode8 之后需要点开推送选项: TARGETS -> Capabilities -> Push Notification 设为 on 状态

删除原有 boost

sudo rm –rf /usr/local/include/boost
sudo rm –rf /usr/local/lib/libboost_*

brew uninstall boost

重新从官网下载 Boost

cd ./node_modules/react-native/third-party

查看 boost 版本号,从官方网站上面下载 boost https://www.boost.org/users/download/

可以下载适合你的版本。

替换 .rncache 目录下面的 boost

将下载的 boost 包复制至 .rncache 目录下,解压该文件,

cd ./boost_1_63_0
./booststrap.sh
sudo ./b2 install

再次尝试应该是可以了

react-native run-ios
如果还不行的话,可以试试直接将 /opt/local/include/boost 复制至 ./node_modules/react-native/third-party 目录下。

_GFX2906.jpg

往年的这个时候都会去北京城的各个红叶黄叶区去拍拍照,今年没有去什么人多的地方,只是在现在住的地方周边转了一下,那天下了点小雨,一个人背着相机瞎转,秋天伴着细雨落下来,安静的躺在路上,水泥就在脚下,它们已经无法归根……

阅读剩余部分

# 获取 Android 默认的 debug.keystore 指纹

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

# 获取生产环境使用的 keystore 指纹

keytool -list -v -keystore /path/to/NAME.keystore -alias ALIAS -storepass PASS -keypass PASS

上周末,带着 GFX 50S 去了躺内蒙,拍了几张人像,感觉全幅的细节跟中幅真的是没法比,也没怎么后期,就直接手机上面套了几个滤镜就直接上图了吧,细节基本上全部丢失。

IMG_20180911_005304.jpg

IMG_20180911_005343.jpg

IMG_20180911_005430.jpg

IMG_20180911_005130.jpg

IMG_20180911_004859.jpg

IMG_20180911_004746.jpg

IMG_20180911_004658.jpg

IMG_20180911_004513.jpg

IMG_20180911_004331.jpg

IMG_20180911_004235.jpg

IMG_20180911_004109.jpg

IMG_20180911_004010.jpg

IMG_20180911_003936.jpg

IMG_20180911_003805.jpg

IMG_20180911_003636.jpg

IMG_20180911_003607.jpg

IMG_20180911_003119.jpg

IMG_20180911_002815.jpg

IMG_20180911_002715.jpg

IMG_20180911_002546.jpg

IMG_20180911_002420.jpg

IMG_20180911_001735.jpg

IMG_20180911_001539.jpg

IMG_20180911_001047.jpg

IMG_20180911_002319.jpg

Polarr 转了几张黑白(其实机身直出黑白感觉更好):

_DSF0559_polarr_2.jpg

_DSF0561_polarr_1.jpg

佳能全出了,换了一个 Fujifilm GFX 机身 + 63mm/F2.8 镜头,这里记录一下。


更新几张样片,感觉得慢慢掌握了,就这一次上传原图了,五张图,100多M,以后得小小图了。

南新园小区楼

_DSF0013.jpg

_DSF0022.jpg

_DSF0043.jpg

_DSF0047.jpg