Blog

Yoki


  • 首页

  • 归档

  • 搜索

动画了解一下(一)

发表于 2017-11-23

笼统的过一下动画的一些基本概念

基本概念

  • 帧:在动画过程中,每一幅静止画面即为一“帧”
  • 帧率:即每秒播放的静止画面的数量,单位是 fps(frame per second)或赫兹(Hz)
  • 帧时长:即每一幅静止画面的停留时间,单位一般是毫秒
  • 丢帧:在帧率固定的动画中,某一帧的时长远高于平均帧时长,导致其后续帧被挤压而丢失的现象

我们在显示器上看到的动画,每一帧变化都是系统绘制出来的(GPU 或者 CPU)。它的最高绘制频率受限于显示器的刷新频率(而非显卡,大多数都是 60Hz 或者 75Hz)

帧率越高,屏幕上图片闪烁感就越小,稳定性也就越高。人的眼睛不容易察觉 75Hz 以上刷新频率带来的闪烁感。

实现方式

  • js:通过定时器(setTimeout 和 setInterval)来改变元素样式,或者使用 requestAnimationFrame
  • css3:transition 和 animation
  • html5:使用 html5 提供的绘图方式(canvas/svg/webgl)

requestAnimationFrame

  • 这个 api 是浏览器用于定时循环操作的一个接口,类似于 setTimeout,主要用途是按帧对网页进行重绘
  • 目的是为了让各种网页动画效果(dom 动画,canvas 动画,svg 动画,webgl 动画)能够有一个统一的刷新机制,从而节省系统资源,提供系统性能,改善视觉效果。
  • 代码中使用这个 api,就是告诉浏览器希望执行一个动画,让浏览器在动画帧安排一次重绘。
  • 其接受一个回调作为参数,这个回调函数会在浏览器重绘之前调用,由于功效只是一次性的,所以想实现连续的动效,需要递归调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
<div id="demo" style="position:absolute; width:100px; height:100px; background:#ccc; left:0; top:0;"></div>

<script>
var demo = document.getElementById('demo');
function render(){
demo.style.left = parseInt(demo.style.left) + 1 + 'px'; //每一帧向右移动1px
}
requestAnimationFrame(function(){
render();
//当超过300px后才停止
if(parseInt(demo.style.left) <= 300) requestAnimationFrame(arguments.callee);
});
</script>
  • cancelAnimationFrame 用于取消重绘
1
2
var requestID = requestAnimationFrame(repeatOften);
cancelAnimationFrame(requestID);

使用 requestAnimationFrame 的优势如下

  • 会把每一帧中的所有 dom 操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随着显示器的刷新频率(60 或者 75Hz)
  • 在隐藏或不可见的元素中,将不会进行重绘或回流,这就意味着更少的 cpu,gpu 和内存使用量
  • 所以它是性能优化版/专为动画量身打造的 setTimeout,不同的是它不是自己指定回调函数运行的时间,而是跟着浏览器内建的刷新频率来执行回调,这就能达到浏览器所能实现动画的最佳效果

transition

  • css 中的 transition 属性允许块级元素中的属性在指定的时间内平滑的改变
1
transition:property duration timing-function delay
  • property 是过渡效果的 css 属性的名称,比如 height
  • duration 规定完成过渡效果需要多少毫秒或者秒
  • timing-function 规定速度效果的曲线(linear/ease..)
  • delay 规定过渡效果何时开始

animation

  • 跟 transition 比较,作用于元素本身而不是样式属性,可以使用关键帧的概念,应该说可以实现更自由的动画效果
1
animation:name duration timing-function delay iteration-count direction
  • name 是需要绑定到选择器的 keyframe 名称
  • duration 规定完成动画所花费的时间,以秒或者毫秒算
  • timing-function 是动画的速度曲线(line/ease..)
  • delay 规定在动画开始之前的延迟
  • iteration-count 规定动画应该播放的次数
  • animation-direction 规定是否应该轮流反向播放动画(normal/alternate)

Canvas

  • html5 新增的元素,作为页面图形绘制的容器,可用于使用 js 中的脚本来绘制图形。
  • 它可以用于绘制图形,制作照片,创建动画,甚至可以进行实时视频处理或者渲染

    它具有如下特点

  • 依赖分辨率,基于位图
  • 不支持事件处理器
  • 弱的文本渲染能力
  • 能够以.pbg 或者.jpg 格式保存结果图像
  • 最适合图像密集型的游戏,其中的许多对象会被频繁重绘

SVG

  • scalable vector graphics==可缩放矢量图形,用来定义用于网络的基于矢量的图形,使用 xml 格式定义图像

    它具有如下特点

  • 不依赖分辨率,基于矢量图
  • 支持事件处理器
  • 最适合带有大型渲染区域的应用程序,如谷歌地图
  • 复杂度高会减慢渲染速度(任何过度使用 dom 的应用都不快)
  • 不适合游戏应用

svg 示例

1
2
3
4
5
6
7
8
9
10
11
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect
x="50"
y="20"
rx="20"
ry="20"
width="150"
height="150"
style="fill:red;stroke:black;stroke-width:5;opacity:0.5"
/>
</svg>
  • svg 代码以 svg 根元素开始。
  • width 和 height 设定 svg 文档的高度和宽度
  • version 指定所使用的 svg 版本
  • xmlns 定义 svg 命名空间

webgl

  • webgl 使得网页在支持 canvas 标签的浏览器中,不需要安装任何插件,便可以使用基于 OpenGL ES 2.0 的 api 在 canvas 中进行 3D 渲染。
  • webgl 由 js 控制代码,和在计算机的图形处理单元(GPU)中执行的特效代码(shader code,渲染代码)组成

常用的动画库

  • Ani.js–基于 css 动画的生命处理库
  • Dynamics.js–创建具有物理运动效果动画的 js 库
  • Animate.css–齐全的 css3 动画库
  • Three.js–快速搭建 webgl 项目

进程和线程

发表于 2017-11-09

进程(process)和线程(thread)

  • 进程和线程独立运行,并可能同时运行,多个线程能够共享单个进程的内存。
  • 进程是具有一定独立功能的程序,它是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位,换句话说进程是可以独立运行的一段程序。
  • 线程是进程得一个实体,是 CPU 调度和分派的基本单位,他是比进程更小的独立运行的基本单位,线程自己基本上不拥有系统资源。在运行时,只是暂用一些计数器、寄存器和栈。

他们之间的关系是

  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(就是通常说的主线程)
  • 资源分配给进程,同一进程的所有线程共享该进程的所有资源
  • 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的方法实现同步。
  • 处理机分给线程,即真正在处理机上运行得是线程
  • 线程是指进程内的一个执行单元,也是进程内的可调度实体

他们之间的区别

  • 调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
  • 并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
  • 拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但也可以访问隶属于进程的资源。

生动形象

  • 书面上,进程和线程基本是一个东西,只是进程可以包含若干个线程。此外就是在操作系统中的层级上,也就是粒度不同外,没有其他大的区别。在一般使用中,需要就多开几个线程。
  • 形象的说,进程就是一个项目组,每个程序员就是里面的线程,当然一个程序员也可以是一个项目组,对应的一个进程只有一个线程,而且还是主线程。公司里面的任务是分配给项目组级别的,也就是说系统资源是分配给进程的,但是干活的是程序员(线程)。

杂乱无章

发表于 2017-09-24

最近有点忙,忙得几乎都不像是一个才毕业了两个月的人。生活也足够简单,两点一线,下班后最大的愿望便是去书店看(zhuang)书(bi),偷得浮生半日闲。

在九月过半的时候,我突然发觉应有点仪式感——纪念一下曾经是学生的日子,于是总想写些什么。在学校的日子已算是一去不复返了,他们开学的时候,我总是觉得自己失去了些什么,那种感觉无以言状。

出来工作的时候,确实会觉得上学的时光是美好的,这种生活的内核是多彩,不重复。也许这是钱老说的那样,城里的人想出去,城外的人想进来。小时候总是想快些长大,想快点去各种自己喜欢的事情,那些盼呀盼呀总觉得盼不到的日子,现在突然就来临了。那些小时候喜欢的事,反倒是换了一份新鲜感。人呀,越是要自由,就越要担起更多的责任。而我们开始慢慢长大,获得了从前不曾有的自由,不论是经济上还是情感上,但是同时也有了更多的责任。

有时候会觉得这些日子乏味,枯燥,我渐渐地就开始看起了以前那些没看完的书。学会了以宽容的心态去面对生活,可能也是时光宽容了我。有时候,在早晨公交车如此多人的时候,每个人的心情都十分烦躁,鸟儿的声音也是聒噪多余的,但我总是会看到我们这一路司机在特别人多下车的地方,都会大声提醒外面有车开过来,下车注意些。每当看到这些画面,心里就会特别感激,对于生活,感觉才真正有了那种烟火气息。

看到很多人工作之后发的朋友圈,千篇一律的累,单调,全然没有了上学时候的激情。我曾经也迷茫,但是周遭厉害的人都很多,容不得我没有激情。当在这二十几岁的年纪里,还有很多我们不曾到达的领域,褪掉了激情,再无可能去攀登——最近在《人性的弱点》里体会到,也算是满满的鸡汤文,但是与卡耐基对话,就有种想要变得更好的冲动。

不管如何,失败一些,成功一些,本就是一件很好的事。也庆幸在大学里谈过恋爱,懂得了如何去珍惜,分开的时候会对感情这件事极其失望,当然现在是以感恩面对这些,过往不究。有时候夜深了在黄埔大道上面走着,灯影明灭,总是会看见一两对小情侣在打闹,或是依偎,我会觉得特别有生气,也会想起大学时候谈恋爱的时光。现在一个人了,虽无拘束,但心情总有波澜。

思绪很杂,文字也随之杂乱无章。

Generator函数

发表于 2017-05-23

Generator 函数

generator 是生成器。generator 可以看成是一个取号机,你拿一张票去向机器请求一个号码,你接受了你的号码之后,机器不会自动为你提供下一个。换句话说,取票机“暂停”直到另外一个人来请求另一个号码,此时它才会向后进行。

generator 函数的创建
  • ES6 中用function*来创建生成器函数
  • 当想要 generator 产生一个值之后暂停执行,需要使用到yield(生成)关键字,有点类似于 return 关键字(都可以返回一个值),只是 yield 返回值之后 generator 函数会暂停运行
1
2
3
4
5
6
function* ticketGenerate() {
//创建一个取号机
yield 1;
yield 2;
yield 3;
}
  • yield*是委派至另一个生成器函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}

function* generator(i) {
yield i;
yield* anotherGenerator(i);
yield i + 10;
}

var gen = generator(10);

console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
generator 函数的使用
  • ticketGenerate()不会马上执行它的主体,而是会返回一个这个生成器函数的迭代器对象
  • 当这个迭代器对象的 next 方法被调用时候,生成器函数的主体会执行到第一个 yield 表达式,该表达式定义了迭代器对象返回的值。
  • next()返回一个对象:{value:1,done:false},value 属性是产出的值,done 属性是表示生成器是否已经产出了它最后的值。
1
2
const ticket = ticketGenerate(); //返回一个生成器对象
ticket.next(); //{value: 1, done: false}
影响 generator 的状态
  • next()的另一个妙用是可以给它传递值,它会被视为 generator 中的 yield 语句的结果对待。
  • 因此 next 是在 generator 运行过程中向其传递信息的方式。我们可以借此来重置取号机,将号码变为 0。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* ticketGenerator() {
for (var i = 0; true; i++) {
var reset = yield i;
if (reset) {
i = -1;
}
}
}
var takeANumber = ticketGenerator();
console.log(takeANumber.next().value); //0
console.log(takeANumber.next().value); //1
console.log(takeANumber.next().value); //2
console.log(takeANumber.next(true).value); //0
console.log(takeANumber.next().value); //1

js里的异常

发表于 2017-05-18

throw

用来抛出一个异常,throw expression

  • throw 12 //抛出数值 12 的异常
  • throw ‘err’ //抛出字符串’err’的异常
  • throw {name:’异常的名字’,message:’异常的信息’,stack:’一般还有个函数栈’} //抛出对象的异常,一般这个对象包含名字,信息,栈。可以试一下 throw new Error(‘123’)

也可以创建自定义错误

1
2
3
4
5
function UserException(message) {
this.name = "UserException";
this.message = message;
}
throw new UserException("用户消息错误");

try..catch 大法

配合 throw 能找出自己程序的异常

1
2
3
4
5
6
7
8
9
10
11
12
function printValue(value) {
if (value == 1) {
throw new Error("值是1错误");
} else {
console.log(value);
}
}
try {
printValue(1);
} catch (e) {
console.log(e.message, e.name, e.stack);
}

使用promise

发表于 2017-05-17

Promise

Promise 作为 ES6 的特性,可以很好地创建异步工作流

  • Promise 是构造函数
  • Promise 只有三种状态(pending,resolve,reject),pending 是刚创建 Promise 实例的状态,然后一旦转为 resolve 或者 reject,就不可变更

创建 Promise

以封装 xhr 为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getURL(url) {
return new Promise((resolve, reject) => {
//Promise构造函数接受一个函数,这个函数接受两个参数
let xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = function() {
if (xhr.status == 200) {
resolve(JSON.parse(xhr.responseText)); //如果成功响应,那么把这个值填充到resolve
} else {
reject(new Error(xhr.statusText)); //把该错误对象填充到reject里面
}
};
xhr.onError = function() {
reject(new Error(xhr.statusText)); //把该错误对象填充到reject里面
};
xhr.send(null);
});
}
  • reject 必须是接受一个错误对象实例,比如new Error('12')里面是什么类型的都可以

使用 Promise

使用上面封装 xhr 的 getURL 的函数,该函数返回一个 Promise 实例

  • Promise 的实例方法有两个:then 和 catch,这个两个方法都返回新的 Promise 对象
  • then 有两个参数。then(onfilled,onrejected),第一个是参数值 resolve 来填充,第二个是 reject 来填充
  • catch 实则是 then(undefined,onrejected)的别名,但是一般情况下都是用这个
  • then 的第二个参数 检测不了自身 Promise 对象 onfilled 出了异常,而 catch 是基于对上次的 then 产生的 promise 对象,看这里
1
2
3
4
5
6
7
8
9
let url1 = "http://azu.github.io/promises-book/json/comment.json",
url2 = "http://azu.github.io/promises-book/json/people.json";
getURL(url1)
.then(val => {
console.log(val); //这里是onfilled填充
})
.catch(error => {
console.log(error); //这里是onrejected填充
});
Promise chain 获取上次 promise 的返回值

由于 Promise 链式调用的时候,每个 Promise 都是独立的。但是可以通过 return 值来传给下一个 Promise,因为 return 了值是填充了当前 Promise 对象的 resolve

1
2
3
4
5
6
7
promise
.then(val => {
return val;
})
.then(val => {
console.log(val);
});
Promise 静态方法
  • Promise.resolve(42) === new Promise((resolve,reject)=>resolve(42)) ,是它的语法糖
  • Promise.reject(new Error(42)) === new Promise((resolve,reject)=>reject(new Error(42))) 是它的语法糖
  • 如果有多个 Promise 同时需要判断才能进行下一步,可以用 Promise.all([promise1,promise2])。这个 then 方法返回的参数也是一个数组,跟 promise1 和 promise2 的顺序一样。需要注意的是,all 是并行执行的。
  • Promise.race([promise1,promise2]) 则和数组的 some 方法很像,只要有一个 promise 对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。
1
2
3
4
5
6
promise.all([getURL(url1), getURL(url2)]).then(function(result) {
console.log(result); //[getURL(url1)的结果,getURL(url2)的结果]
});
promise.race([getURL(url1), getURL(url2)]).then(function(value) {
console.log(value); //一旦这两个promise对象有一个完成了onfilled或onrejected状态,那么就会进行后续的then,所以这里then的参数只有一个value。但不并不会影响其他promise对象的进行,只是它们的结果值不会出现在then的里面
});

模板字符串的替换

发表于 2017-05-11

简单介绍

看到 vue 模板里都是用包含变量,然后替换成变量,觉得比字符串拼接实在是好太多了

  • es6 的字符串模板用起来实在是舒服,所以简单用实现一下
1
2
3
4
// 首先 str.replace(regexp|substr, newSubStr|function) ,replace 可以传递一个函数
str.replace(reg,function(match,key)=>{})
//正则匹配{{内容}},应该是/\{\{(.*?)\}\}/,用\转义{和}
// /\{\{(.*?)\}\}/g;

正则表达

  • .*?是正则的固定搭配用法,就是表示非贪婪模式,尽可能匹配少的意思
  • .*则表示贪婪模式
1
2
3
4
5
6
7
8
9
源字符串:aa<div>test1</div>bb<div>test2</div>cc

正则表达式一:<div>.*</div>

匹配结果一:<div>test1</div>bb<div>test2</div>(从头部匹配到尾部,从第一个div到最后的那个</div>)

正则表达式二:<div>.*?</div>

匹配结果二:<div>test1</div>(这里指的是一次匹配结果,不使用\/g,所以没包括<div>test2</div>)

简单实现

  • 所以有上面的简单铺垫,可以简单写出
1
2
3
4
5
6
7
8
9
10
function render(template, context) {
return template.replace(/\{\{(.*?)\}\}/g, (match, key) => {
console.log(match, key); //match是{{name }},key是name
return context[key.trim()];
});
}
//可以使用trim去掉前后空格
const template = "我是{{name }},现在{{age }}岁";
const context = { name: "yoki", age: "20" };
console.log(render(template, context));

函数节流和防抖

发表于 2017-04-10

节流(throttle)

函数节流是指一定时间内只跑一次函数,就像是技能 cd,你再怎么按它一段时间内只能触发一次。

  • 主要思想就是设置一个守卫(flag),如果 setTimeout 已经执行过了就不会创建新的 setTimeout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let scrollFun = function(time) {
let canRun = true; //初始化守卫变量
return function() {
if (canRun) {
canRun = false;
setTimeout(() => {
console.log("throttle");
//3s后变量才为true,才可以继续执行if条件
canRun = true;
}, time);
}
};
};
//3s执行一次
window.scroll = scrollFun(3000);
  • 适用于窗口 resize

防抖(debounce)

函数防抖呢,就是频繁触发后,一旦空闲下来才开始执行这个函数。就像公交车司机,如果有人陆续刷卡,司机一直停车等待,直到所有人上来了才回开车。

  • 主要思想就是如果频繁触发,就取消上一个,重新创建一个规定时长的 setTimeout
1
2
3
4
5
6
7
8
9
10
11
12
let debounce = function(time) {
let setTimeId = null;
return function() {
//取消上一个的
clearTimeout(setTimeId);
setTimeId = setTimeout(() => {
console.log("debounce");
}, time);
};
};
//直到没有输入后,3s后才执行
$(el).input(dubounce(3000));
  • 适用于输入框输入持续监听等

使用nvm

发表于 2017-03-10

Node Version Manager(node版本管理)

使用nvm安装和管理node

node版本更新很快,为了能在版本之间迅速切换,我们要使用nvm来安装和管理。

用nvm下载了相应的node也会下载相应的npm进行管理

安装nvm

windows

我们可以从Github下载

默认安装路径是C:\Users\Administrator\AppData\Roaming\nvm,存放我们下载的各个版本的node

验证安装,打开控制台,输入nvm,看是否输出所有命令

centos7
  • 下载

    1
    2
    #首先确保centos已安装git,以确保之后nvm本身的升级
    curl https://raw.githubusercontent.com/creationix/nvm/v0.30.2/install.sh | bash
  • 重启终端

  • 升级nvm
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@localhost .nvm]# nvm --version
    0.30.2
    [root@localhost .nvm]# git fetch -p
    [root@localhost .nvm]# git rev-list --tags --max-count=1
    0a95e77000515c1156be593642dd4e452f2f098e
    [root@localhost .nvm]# git describe --tags 0a95e77000515c1156be593642dd4e452f2f098e
    v0.33.2
    [root@localhost .nvm]# git describe --abbrev=0 --tags
    v0.33.2
    [root@localhost .nvm]# git checkout $(git describe --tags `git rev-list --tags --max-count=1`)
    之前的 HEAD 位置是 7f3145b... [New] add support for `$NVM_DIR/default-packages` file
    HEAD 目前位于 0a95e77... v0.33.2
    [root@localhost .nvm]# source ~/.nvm/nvm.sh
    [root@localhost .nvm]# nvm --version
    0.33.2

命令

  • nvm list #查看本地所有node版本
  • nvm install 4.2.2 #安装 4.2.2 版本
  • nvm use 4.2.2 #切换至 4.2.2 版本
  • nvm uninstall 4.2.2 #卸载4.2.2 版本

镜像

nvm默认的下载地址是http://nodejs.org/dist/,这是国外的服务器,在国内下载速度很慢。

  • 需要把下列代码复制到你nvm的安装路径下的setting.txt
    1
    2
    node_mirror: https://npm.taobao.org/mirrors/node/
    npm_mirror: https://npm.taobao.org/mirrors/npm/

工作中常用git命令

发表于 2017-02-13

基础命令

  • git clone 克隆仓库

  • git checkout 切换分支

  • git add 添加文件

  • git commit 提交改动

  • git push 推送本地分支到远端

  • git merge 把某个分支的代码合并到当前分支

  • git pull = git fetch + git merge 把远端分支更新到本地

  • git status 当前分支状态

我一般习惯配置别名 alias,比如 merge 变成 mg,这样少打几个字母

常用命令组合

1.丢掉本地未提交改动

git checkout -f

2.丢掉本地已提交但未推送到远端的提交

git reset –hard origin/xxx

测试和发布

内部使用 gitlab

  • master 主分支:用于外网线上发布,永远保持跟线上一致
  • develop 分支:用于发布测试环境

日常开发说明

详情参见git flow

  • 日常开发从 master 拉一个分支 feature-xxx 分支到本地
  • 要发布测试环境,直接 mg 到 develop,然后发布
  • 要发布线上,直接 mg 到 master,然后发布
  • 日常紧急 bug 修复,可拉一个 hotfix 分支
1…789
Yoki

Yoki

云在青天水在瓶

82 日志
21 标签
© 2019 Yoki
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4