HTML5 重力感应deviceorientation与orientationchange

一、前言

随着智能手机越来越普及,而一般手机都开始提供了重力感应和重力加速的接口,我们在制作HTML5页面的过程中,可以利用deviceorientation(检测设备方向事件)、orientationchange(屏幕旋转事件),来做指南针、摇一摇、地图等这些应用。

二、主要内容

1、orientationchange 事件在屏幕发生翻转时触发,window.orientation可获得设备的方向,一共有三个值0:竖直, 90:右旋, -90:左旋
2、deviceorientation 提供设备的物理方向信息,表示为一系列本地坐标系的旋角
3、MozOrientation (firefox专用) 事件中可获得三个值z,x,y,分别代表垂直加速度,左右的倾斜角度,前后的倾斜角度(取值范围:-1~1)

三、用法简介

要接收设备方向变化信息,需注册监听deviceorientation事件:

注册一个deviceorientation事件的接收器:

1
window.addEventListener("deviceorientation", handleOrientation, true);

注册完事件监听处理函数后(对应这个例子中的handleOrientation),监听函数会定期地接收到最新的设备方向数据。

方向事件对象中包含四个值Orientation and motion data explained

DeviceOrientationEvent.absolute

DeviceOrientationEvent.alpha //表示设备沿z轴上的旋转角度,范围为0~360

DeviceOrientationEvent.beta //表示设备在x轴上的旋转角度,范围为-180~180。它描述的是设备由前向后旋转的情况

DeviceOrientationEvent.gamma //示设备在y轴上的旋转角度,范围为-90~90。它描述的是设备由左向右旋转的情况

手机中的方位轴:

deviceorientation
deviceorientation
deviceorientation
deviceorientation

下面是一个事件处理函数的例子:

1
2
3
4
5
6
7
8
function handleOrientation(orientData) {
var absolute = orientData.absolute;
var alpha = orientData.alpha;
var beta = orientData.beta;
var gamma = orientData.gamma;

// Do stuff with the new orientation data
}

判断屏幕是否旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function orientationChange() {
switch (window.orientation) {  
case 0:
alert("肖像模式 0,screen-width: " + screen.width + "; screen-height:" + screen.height);
break;  
case -90:
alert("左旋 -90,screen-width: " + screen.width + "; screen-height:" + screen.height);
break;  
case 90:
alert("右旋 90,screen-width: " + screen.width + "; screen-height:" + screen.height);
break;  
case 180:
  alert("风景模式 180,screen-width: " + screen.width + "; screen-height:" + screen.height);  
break;
};
};

参考资料:
W3C DeviceOrientation事件规范
检测设备方向
HTML5 控制裝置陀螺儀 ( 三軸 )
html5重力感应事件之DeviceMotionEvent
移动终端学习2:触屏原生js事件及重力感应
移动开发事件
移动端资源集合
html5实现微信摇一摇功能
HTML5摇一摇(上)—如何判断设备摇动
陀螺仪的基础知识

CSS3D-原理篇

随着浏览器的不断进步与更新,许多的新特性也崭露头角,CSS3D酷炫玩法已经可以很好的运用到H5页面。接下来,本文将从CSS3D变换相关属性transform-style: preserve-3dperspective开篇讲解,主要内容包括:

transform-style: preserve-3d 三维效果
perspective and perspective-origin 3D视距,透视/景深效果
perspective-origin 透视角度
backface-visibility 决定元素旋转背面是否可见

理解3

transform-style创建3D空间

CSS3 实现 3D 的效果,最主要的就是设置 transform-style 属性,指定嵌套元素如何在3D空间中呈现。其主要有两个属性值:flat和preserve-3d。

1
2
3
4
5
// 语法:
transform-style: flat|preserve-3d;

transform-style: flat; // 默认值,表示所有子元素在2D平面呈现
transform-style: preserve-3d; // 子元素将在3D空间中呈现

其中flat值为默认值,表示所有子元素在2D平面呈现。preserve-3d表示所有子元素在3D空间中呈现。

可以理解为,对一个元素设置了transform-style的值为flat,则该元素的子元素都将被平展到该元素的2D平面中进行呈现。沿着X轴或Y轴方向旋转该元素将导致位于正或负Z轴位置的子元素显示在该元素的平面上,而不是它的前面或者后面。而对一个元素设置了transform-style的值为preserve-3d,它表示不执行平展操作,它的子元素就可以相对于父元素所在的平面,进行 3D 变形操作。

注意:transform-style属性需要设置在父元素中(且只对第一级有效),为了加深一下对transform-style属性,以及对比transform-style: flattransform-style: preserve-3d的区别,请浏览: demo

透视投影perspective属性

perspective 属性定义 3D 元素距视图的距离,以像素计。该属性允许您改变 3D 元素查看 3D 元素的视图。

简单来说,当元素没有设置perspective时,也就是当 perspective:none/0时所有后代元素被压缩在同一个二维平面上,不存在景深的效果。perspective 为一个元素设置三维透视的距离,仅作用于元素的后代,而不是其元素本身。

1
2
// 语法:
perspective:none | <length> //不能为负值

perspective属性包括两个属性:none和具有单位的长度值。其中perspective默认值为none,当其值为0或none时,没有3D效果,元素会平展堆砌在一起。另一个值<length>接受一个长度单位大于0的值,且其单位不能为百分比值。当<length>值越大,则焦距越大,看到3D物体效果越弱越小。反之,此值越小,则焦距越小,看到3D物体效果越强越大。

为了更好的理解perspective属性,可以结合translateZ属性值,把perspective的值简单的理解为人的眼睛到显示器(平面)的距离,而translateZ就是3D物体距离源点的距离,原理图如下translateZ方法辅助理解perspective视角演示

理解perspective

其中,焦点到z=0平面的距离是perspective的值,通过以上可以简单总结出以下结论:

  • perspective取值为0、none或不设置,不会构建3D空间,字元素会平铺展示。
  • perspective 值越小,translateZ值对于3D视觉效果影响更强烈,越大时,translateZ值对于视觉的影响更加细微(具体取值推荐在500px到1000px的时候视觉上更加合理)
  • 当元素的translateZ值使得元素在透视点之后,元素则不会被捕捉到,出现在视野之外

ps: transform: perspective(<length>)也可以设置3D 元素距视图的距离,而perspective()仅对元素本身开启透视投影。具体请参考:perspective属性的两种书写舞台多元素下的perspective两种书写对比

通过,两个参考例子,我们可以看到单个元素,设置透视,表现形式是一样的,而多个元素设置透视,表形式不一样,其由于perspective属性,不针对其本身,是所有的字元素,而transform: perspective()仅对元素本身开启透视投影。

透视角度perspective-origin属性

perspective-origin属性是3D变形中另一个重要属性,主要用来决定perspective属性的源点角度。它实际上设置了X轴和Y轴位置,在该位置观看者好像在观看该元素的子元素。

perspective-origin属性使用语法:

1
perspective-origin:[<percentage> | <length> | left | center | right | top | bottom] | [[<percentage> | <length> | left | center | right] && [<percentage> | <length> | top | center | bottom]]

该属性默认值为“50% 50%”(也就是center),其可设置为一个值,也可设置为两个长度值:

  • 第一个长度值指定相对于元素的包含框的X轴上的位置。它可以是长度值(以受支持的长度单位表示)、百分比或以下三个关键词之一:left(表示在包含框的X轴方向长度的0%),center(表示中间点),或right(表示长度的100%)。
  • 第二个长度值指定相对于元素的包含框的Y轴上的位置。它可以是长度值、百分比或以下三个关键词之一:top(表示在包含框的Y轴方向长度的0%),center(表示中间点),或bottom(表示长度的100%)。

注意,为了指转换子元素变形的深度,perspective-origin属性必须定义父元素上。通常perspective-origin属性本身不做任何事情,它必须被定义在设置了perspective属性的元素上。换句话说,perspective-origin属性需要与perspective属性结合起来使用,以便将视点移至元素的中心以外位置,如下图所示:
理解perspective-origin

往往看一样东西不可能一直都在中心位置看,想换个角度,换个位置一看究竟,这个时候就离不开perspective-origin这个属性,下面来自于W3C官网的图可以简单阐述这一观点:
理解perspective-origin

backface-visibility决定元素旋转背面可见

backface-visibility 属性决定元素旋转背面是否可见。对于未旋转的元素,该元素的正面面向观看者。当其Y轴旋转约180度时会导致元素的背面面对观众。

backface-visibility 语法:

1
2
3
4
backface-visibility: visible | hidden 

// visible:为backface-visibility的默认值,表示反面可见
// hidden:表示反面不可见

backface-visibility属性可用于隐藏内容的背面。默认情况下,背面可见,意味着即使在翻转后,旋转的内容仍然可见。但当backface-visibility设置为hidden时,旋转后内容将隐藏,因为旋转后正面将不再可见。设置backface-visibility属性可有助于模拟多面的对象,例如常见翻纸牌效果,通常将backface-visibility设置为hidden,实现只有正面可见。

CSS3D-原理演示

参考资料:

CSS 3D应该注意的事项
天猫HTML5互动技术实践
H5打造3d场景不完全攻略(一): H5 3d表现形式
H5打造3d场景不完全攻略(二): Amazing CSS3D
前端黑科技——纯clip-path打造的3D模型渲染器
CSS3 3D transforms系列教程-3D旋转木马
3d transform的坐标空间及位置

H5全景-CSS3d篇

perspective 属性定义 3D 元素距视图的距离,以像素计。该属性允许您改变 3D 元素查看 3D 元素的视图。

现在说全景会不会有点迟=。= https://zhuanlan.zhihu.com/p/22761057#!

CSS 3D应该注意的事项 http://www.w3cplus.com/css3/things-watch-working-css-3d.html

天猫HTML5互动技术实践 https://yq.aliyun.com/articles/59052

H5打造3d场景不完全攻略(一): H5 3d表现形式 https://segmentfault.com/a/1190000006188118

H5打造3d场景不完全攻略(二): Amazing CSS3D https://segmentfault.com/a/1190000007262754

前端黑科技——纯clip-path打造的3D模型渲染器 http://www.tuicool.com/articles/ZfQzy2v

CSS3 3D transforms系列教程-3D旋转木马 http://www.htmleaf.com/ziliaoku/qianduanjiaocheng/201502061338.html

参考资料:
A Look at Some CSS Methodologies
5 Standardized Methods for Writing CSS
Are You Using CSS3 Appropriately?
A List of CSS Styles Guides for Inspiration

斜率计算:反正切函数 Math.atan() 与 Math.atan2() 的区别

我们可以使用正切Math.tan()操作将角度转变为斜率,那么怎样利用斜率来转换为角度呢?可以利用斜率的反正切函数将它转换为相应的角度。Math.atan()Math.atan2()两个函数可以计算反正切,接下来分析一下具体用法:

一、Math.atan()

Math.atan()接受一个参数:用法如下:

1
2
angel = Math.atan(slope)  //slope值计算为y/x (斜率比值无法判断 y、x方向,如-1/-1, 1/-1等情况)
//angel为一个角度的弧度值,`slope`为直线的斜率,是一个数字,这个数字可以是负的无穷大到正无穷大之间的任何一个值(tan的取值范围)

不过,利用它进行计算比较复杂。因为它的周期性,一个数字的反正切值不止一个。例如atan(-1)的值可能是45度,也可能是225度。这样就是它的周期性,对于正切函数来说,它的周期是180度,所以两个相差180度的角具有相同的正切和斜率:

1
tanθ=tan(θ+180)

然而,Math.atan()只能返回一个角度值,因此确定它的角度非常的复杂,而且,90度和270度的正切是无穷大,因为除数为零,我们也是比较难以处理的,因此我们更多的会采用第二个函数Math.atan2()进行处理。

二、Math.atan2()

Math.atan2()接受两个参数x和y,方法如下:

1
2
3
4
angel=Math.atan2(y,x) 

// x 指定点的 x 坐标的数字。
// y 指定点的 y 坐标的数字。

计算出来的结果angel是一个弧度值,也可以表示相对直角三角形对角的角,其中 x 是临边边长,而 y 是对边边长。

三、 对比Math.atan() 与 Math.atan2()输出测试

//输入弧度值,return 角度值
function trace(x){
  //弧度=角度*Math.PI/180
  return 180*x/Math.PI
}

测试如下:
①对比一
x=Math.atan(1)//计算正切值为1的数字对应的弧度值,输出弧度值0.785398163397448 (当斜率值为-7/-7或7/7)
trace(x) //输出45 

x=Math.atan2(7,7) //输出弧度值 0.785398163397448  

trace(x)   //输出45 

②对比二
x=Math.atan2(7,-7) //输出弧度值 2.35619449019234 
trace(x)   //转换为角度值 135  

x=Math.atan2(-7,7) //输出弧度值 -0.785398163397448
trace(x)  //转换为角度值输出-45

x=Math.atan2(-7,-7)   //输出弧度值 -2.35619449019234
trace(x)  //转换为角度值输出-135

从这些测试可以看出,通过坐标系的自动调整,可以很自由的计算出处于不同象限的位置相对应的角度.

四、 计算两点间连线的倾斜角

Math.atan2()函数返回点(x,y)和原点(0,0)之间直线的倾斜角,那么如何计算任意两点间直线的倾斜角呢?

其实只需要将两点x、y坐标分别相减得到一个新的点(x2-x1,y2-y1),然后利用它求出角度就可以了。使用下面的一个转换可以实现计算出两点间连线的夹角:

//输入弧度值,return 角度值
function trace(x){
  //弧度=角度*Math.PI/180
  return 180*x/Math.PI
}
var x = Math.atan2(y2-y1,x2-x1);  //得到弧度值
trace(x)  //获取角度值

具体实例例子:

//测试,计算点(3,3)和(5,5)构成的连线的夹角 
x=Math.atan2(5-3,5-3) //输出弧度值 0.785398163397448
trace(x)  //输出角度值 45

参考资料:
JavaScript atan() 方法
JavaScript atan2() 方法
角度与弧度的转换

角度与弧度的转换

常用角度与弧度的转换

1
2
弧度 = 角度*Math.PI/180 
角度 = 弧度*180/Math.PI

以下内容以弧度与角度的相互转换为基础:

1
2
圆周率: PI  3.1416(弧度) 
圆:2Pi 6.2832(弧度)

弧度是角的度量单位, 弧长等于圆半径长的弧所对的圆心角为1弧度。

由上面这条推导出 2PI*R, 周长就是 3.14*2(圆弧度) * 半径 ,就是这样来的

同理 PI*d 也是一样 3.14*2*半径 = 直径*3.14

角度与弧度的转化就是等比公式的运算

1
角度 / 360   = 弧度 / 6.28 (请自行理解成分式)

1
2
角度 =   弧度*360/6.28(2PI)   = 弧度/2PI*360   >>(除2) 弧度/3.14*180 >> 弧度*180/Math.PI 
弧度 = 角度*6.28(2PI) /360 = 角度/360*2PI >>(除2) 角度/180*3.14 >> 角度*Math.PI/180
1
2
弧度 = 角度 * Math.PI / 180 
角度 = 弧度 * 180 / Math.PI

就系咁样推导出来的~~~

脑袋夹门了~~~

貌似是

1
2
3
4
5
6
7
8
9
10
11
12
13
1度 / 180度 = x弧度 / 3.14 ( 自行理解成分式) 
推成 1角度 = 180x = 3.14 >> x = 3.14 / 180

>> 弧度 = 角度 * (Math.PI / 180)


1弧度 / 3.14 = x角度 / 180度 ( 自行理解成分式)
推成 1弧度 = 3.14x = 180 >> x = 180 / 3.14

>> 角度 = 弧度 * (180 / Math.PI)


1弧度 约等于 57.2958角度

参考资料:
斜率计算:反正切函数 Math.atan() 与 Math.atan2() 的区别
JavaScript atan2() 方法

几个 CSS 方法论

在大型、复杂和快速迭代的系统中,CSS 将非常难以维护。原因之一就是 CSS 没有作用域的概念,每个 CSS 都是全局的,这意味着对 CSS 的任何修改就可能导致一些 UI 的级联改变。

CSS 的扩展语言 – CSS 预处理器,比如 Sass、Less 和 Stylus,使我们编写 CSS 更加容易,但在我看来,这些 CSS 的扩展语言并没有真正解决可扩展性问题。

在 CSS 支持作用域机制之前,我们需要一种机制,使我们的样式只与特定的 HTML 部分关联,这就是 CSS 方法论。本文将讨论如下的 CSS 方法论:

  • Object-Oriented CSS (OOCSS)
  • Block, Element, Modifier (BEM)
  • Scalable and Modular Architecture for CSS (SMACSS)
  • SUIT CSS
  • Systematic CSS

CSS 方法论是正式的,文档化的 CSS 编写方法,使我们能够将 CSS 作为一些小的独立的模块来开发和维护,而不是一个庞大的不可分割的一坨代码。采用一种 CSS 方法论 – 即便这是你自己创建的 – 它将使你在 Web 开发中更加得心应手。

相关文章:CSS Development at Large-Scale Websites

每个 CSS 方法论都为可扩展性和可维护性提供了一套解决方案,一个 CSS 方法论通常会定义:

  • CSS 和 HTML 的最佳实践
  • Class 和 ID 的命名约定
  • 有序的、分组的 CSS 样式
  • 代码格式

没有“最好”的 CSS 方法论,不同的个人/团队/项目适合于不同的方法论。希望通过本文你可以找到一个适合你的方法论,或者激发你创建一个你自己的。

Object-Oriented CSS (OOCSS)

Object-Oriented CSS 简称为 OOCSS,发布于 2009 年,这是第一个被广泛采用的 CSS 方法论,直到今天仍有很大的影响力。
OOCSS 主张结构与样式分离,明确区分内容和它的容器。在 OOCSS 中,样式规则使用相互独立的 CSS 选择器来界定。

案例分析

例如,一个按钮元素可以通过两个样式来设定:

  • .button – 提供按钮的基本样式
  • .grey-btn – 应用颜色和其他可视化属性

HTML:

<button class="button grey-btn">

CSS:

.button {
  box-sizing: border-box;
  height: 50px;
  width: 100%;
}
.grey-btn {
  background: #EEE;
  border: 1px solid #DDD;
  box-shadow: rgba(0, 0, 0, 0.5) 1px 1px 3px;
  color: #555;
}

OOCSS 方法论的目标之一是减少样式规则中相同属性的重复率。该方法论试图通过许多小的、模块化的、专有功能的 CSS 类来实现这一目标。很少通过类型选择器(比如:h1,div 和 body)来指定其样式。

注意:在 OOCSS 方法论中,不鼓励使用后代选择器:

CSS:

/* 在 OOCSS 中,不鼓励使用后代选择器 */
.wrapper .blog-post .button {
  ...
}

避免使用后代选择器,这样 HTML 的呈现就不依赖于特定的 HTML 结构(样式与结构分离)。
OOCSS 鼓励大家尽量复用已有样式,通过扩展现有的样式规则来创建新的样式类,而不要修改或覆盖已有的 CSS 属性。

下面我们想让无序列表的第一项的颜色突出一些,先看看不好的方式:

HTML:

<!-- 反例 -->
<ul class="to-do">
  <li>Combine my CSS files</li>
  <li>Run CSS Lint</li>
  <li>Minify my stylesheet</li>
</ul>

CSS:

/* OOCSS */
.to-do {
  color: #FFF;
  background-color: #000;
}
.first-to-do-item {
  color: #FF0000;
}

小结

OOCSS 的主要缺陷是将产生大量的 CSS 类,这将非常难以维护。在我看来,面向对象编程的理念并不是非常适合 CSS。但这并不是说 OOCSS 的原则不实用,相反,OOCSS 是一个最基础的方法论,它给大规模的 CSS 带来了福音。

Block, Element, Modifier (BEM)

Block, Element, Modifier 通常被称为 BEM,由俄罗斯的 Google 团队设计。BEM 的主要设计理念是用不同的角色来区分不同的 CSS 类,也就是说用角色来命名 CSS 类。BEM 补充了 OOCSS 的不足,因为 OOCSS 并没有任何的命名约定。

在 BEM 方法论中,block 是一个独立的模块化的 UI 组件,一个 block 可以由多个 HTML 元素组成,甚至可以由多个 block 组成,例如导航菜单或搜索表单。element 是 block 的组成部分,服务于一个单一的目的,例如,在一个导航中,element 就是导航菜单中的链接,在实际中就是一个 li 元素和一个 a 元素。modifier 用于改变 block 和 element 的默认样式。

下面是 BEM 的命名规范:

  • .block
  • .block–modifier
  • .block__element
  • .block__element–modifier

案例分析

看下面的登录表单:

<form>
  <label>Username <input type="text" name="username" /></label>
  <label>Password <input type="password" name="password" /></label>
  <button>Sign in</button>
</form>

用 BEM 方法论将如下实现:

<form class="loginform loginform--errors">
  <label class="loginform__username loginform__username--error">
    Username <input type="text" name="username" />
  </label>
  <label class="loginform__password">
    Password <input type="password" name="password" />
  </label>
  <button class="loginform__btn loginform__btn--inactive">
    Sign in
  </button>
</form>

.loginform 类就是 block。

  • .loginform__username 用户名
  • .loginform__password 密码
  • .loginform__btn 登录按钮

还有三个 modifier 分别是:

  • .loginform__username–error 使用户名元素呈现为一个错误提醒的样式
  • .loginform__btn–inactive 使登录按钮元素呈现为不可用的样式
  • .loginform–errors 使整个登录表单呈现为错误提醒的样式

BEM 的命名约定有助于 CSS 作者遵循 OOCSS 的扁平化设计原则,避免了使用深层次的后代选择器。例如下面的 CSS 选择器:

.loginform .username .error {
  ...
}

可以使用单一一个 CSS 类来实现:

.loginform__username--error {
  ...
}

小结

BEM 是一个非常健壮的命名约定,可以方便地区分不同 CSS 类的目的,而且在 HTML 中也可以很方便地识别 CSS 类之间的关系。

BEM 的反对者主要有以下两类:
-CSS 类名太长太丑
-命名约定对不熟练的开发者不友好

Scalable and Modular Architecture for CSS (SMACSS)

Jonathan Snook 在 2011 年出版了《Scalable and Modular Architecture for CSS》这本书,简称为 SMACSS,发音是 [smacks]。
该方法论的核心思路是如何为 CSS 样式分类。Snook 提出了一下五类:

  • Base 为单个 HTML 元素设置默认样式,通常是标签选择器:
h1 {
 font-size: 32px;
}
div {
  margin: 0 auto;
}
a {
  color: blue;
}
  • Layout 与网页布局相关的样式,样式名通常以 layout- 或 l- 开头:
.layout-sidebar {
  width: 320px;
}
.l-comments {
  width: 640px;
}
  • Modules 模块和可复用的组件:
.call-to-action-button {
  text-transform: uppercase;
  color: #FFF200;
}
.search-form {
  display: inline-block;
  background-color: E1E1E1;
}
  • State 指定界面特定状态的样式规则:
.is-hidden {
  display: none;
}
.is-highlighted {
  color: #FF0000;
  background-color: #F4F0BB;
  border: 1px solid #CBBD15;
}
  • Themes 影响整体布局和模块的样式,由用户设置触发。

SMACSS 提供了一个比 BEM 更简单的命名约定。基础样式(base)没有 CSS 类名,因为他们都是标签选择器(h1, p, a 等),模块(module)有一个唯一的样式名,子模块用父模块名作为前缀。

看下面布局,在 .l-footer 中,有一个搜索模块,并且搜索表单至少被用户提交过一次:

<section class="l-footer">
  <form class="search is-submitted">
    <input type="search" />
    <input type="button" value="Search">
  </form>
</section>

SMACSS 也不推荐使用后代选择器,Jonathan Snook 还介绍了CSS 的最佳实用深度理论,该理论的核心思想是通过类名来精确控制目标元素的样式,从而减少 HTML 结构对样式的影响。

SUIT CSS

Nicolas Gallagher 提出的 SUIT CSS 发布于 2014年,他通过 CSS 预处理器定义了一套类似 BEM 命名规范,SUIT CSS 的命名方式有如下五种:

  • u-utilityName
  • ComponentName
  • ComponentName–modifierName
  • ComponentName-elementName
  • ComponentName.is-stateOfName

该命名约定突出的区分了:

  • General utility classes
  • Standalone/modular UI components
  • Individual elements
  • Modifiers

看下面的登录表单:

<form class="LoginForm LoginForm--errors">
  <label class="LoginForm-username is-required">
    Username <input type="text" name="username" />
  </label>
  <label class="LoginForm-password">
    Password <input type="password" name="password" />
  </label>
  <button class="LoginForm-button is-inactive">Sign in</button>
</form>

Systematic CSS

Systematic CSS 是原文作者最近才提出来的一个方法论,借鉴了许多 OOCSS、BEM、SMACSS、SUIT CSS 和其他一些 CSS 方法论的原理和思路。Systematic CSS 可以作为现有 CSS 方法论的简单的替代方案,只需要记住少许几个命名约定,并且命名也更加直观。

在 Systematic CSS 方法论中,一个页面被分为一下四个部分:

  • Layout
  • Elements
  • Widgets
  • Modifiers

首先,使用 section 或 div 元素来创建一个页面布局:

<div class="CONTAINER">
  <header class="BANNER"></header>
  <nav class="NAVIGATION_PRIMARY"></nav>
  <nav class="NAVIGATION_SECONDARY"></nav>
  <main class="MAIN"></main>
  <aside class="ADVERTS"></aside>
  <footer class="FOOTER">
    <nav class="SITEMAP"></nav>
    <div class="LEGAL"></div>
  </footer>
</div>

其次,为内容和交互元素建立默认样式,比如,标题(h1, h2, h3)、段落(p)、列表(ul 和 ol)、表格(table)、表单(form)等。

然后,确定页面中那些重复的部分,将这些重复的部分提取为一个个独立的模块。在 Systematic CSS 中这些模块被称为 widget。看下面两个 widget:

<!-- navigation bar -->
<div class="NavBar">
  <ul>
    <li><a href="./">Home</a></li>
    <li><a href="about.html">About</a></li>
    <li><a href="learn/">Learn</a></li>
    <li><a href="extend/">Extend</a></li>
    <li><a href="share/">Share</a></li>
  </ul>
</div>
<!-- search form -->
<div class="SearchBox">
  <form action="search.html" method="get">
    <label for="input-search">Search</label>
    <input name="q" type="search" id="input-search" />
    <button type="submit">Search</button>
  </form>
</div>

最后,为一些默认样式添加修饰(modifier)类。
看下面例子,navbar-primary 修饰类改变了 NavBar 的默认样式,navbar-selected 修饰类标记了当前的选中项:

<div class="NavBar navbar-primary">
  <ul>
    <li><a href="./">Home</a></li>
    <li><a href="about.html" class="navbar-selected">About</a></li>
    <li><a href="learn/">Learn</a></li>
    <li><a href="extend/">Extend</a></li>
    <li><a href="share/">Share</a></li>
  </ul>
</div>

在 Systematic CSS 中,一个 CSS 类可以是:

  • 布局(layout)名称
  • 组件(widget)名称
  • 修饰符

一个 CSS 类只能是以上三种的一种,不能将以上三种结合使用。它们分别有不同的命名约定:

  • Layout - 全大写
  • Widget - 大驼峰
  • Modifier - 全小写加连接符

这个命名约定的优点是,类的层次结构由它们的名字来代表。
布局的类名采用全大写的形式,非常显眼,例如:.NAVIGATION,.SIDEBAR,.FOOTER。
组件的类名采用大驼峰的形式,比较显眼,例如:.MainMenu,.ImageGrid,.BlogPost。
修饰类是最不重要的类,因为它们修饰一些默认样式,不是默认样式的必要部分,所以它们采用全小写的形式,也最不显眼,例如:.is-highlighted,.has-errors,.hidden。

其他 CSS 方法论

  • Atomic Design
  • DoCSSa
  • csstyle

参考资料:
A Look at Some CSS Methodologies
5 Standardized Methods for Writing CSS
Are You Using CSS3 Appropriately?
A List of CSS Styles Guides for Inspiration

跨域图片资源权限(CORS enabled image) - HTML | MDN

HTML 规范文档为 images 引入了 crossorigin 属性, 通过设置适当的头信息 CORS , 可以从其他站点加载 img 图片, 并用在 canvas 中,就像从当前站点(current origin)直接下载的一样.

crossorigin 属性的使用细节, 请参考 CORS settings attributes.

什么是 “被污染的(tainted)” canvas?

尽管没有CORS授权也可以在 canvas 中使用图像, 但这样做就会污染(taints)画布。 只要 canvas 被污染, 就不能再从画布中提取数据, 也就是说不能再调用 toBlob(), toDataURL()getImageData() 等方法, 否则会抛出安全错误(security error).

这实际上是为了保护用户的个人信息,避免未经许可就从远程web站点加载用户的图像信息,造成隐私泄漏。

如果用户登陆过QQ等社交网站, 假若不做保护 ,则可能打开某个网站后,该网站利用 canvas 将用户的图片信息获取,上传,进而引发泄露.

示例: 从其他站点保存图片

首先, 图片服务器必须设置相应的 Access-Control-Allow-Origin 响应头。添加 img 元素的 crossOrigin 属性来请求头。比如Apache服务器,可以拷贝 HTML5 Boilerplate Apache server configs 中的配置信息, 来进行回应:

1
2
3
4
5
6
7
8
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>

这些设置生效之后, 就可以像本站的资源一样, 保存其他站点的图片到 DOM存储) 之中(或者其他地方)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var img = new Image,
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
src = "http://example.com/image"; // 具体的图片地址

img.crossOrigin = "Anonymous"; //或 img.crossOrigin = "*";

img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage( img, 0, 0 );
localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") );
}
img.src = src;
// 确保缓存的图片也触发 load 事件
if ( img.complete || img.complete === undefined ) {
img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
img.src = src;
}

参考资料:
Chrome:在WebGL中使用跨域图片
HTML规范-crossorigin属性

Mac 终端切换root用户

Mac 终端切换root用户方法

方法一:

    1. 打开Terminal
    1. jonesduan-MacBook-Pro:~ user$ sudo -i
    1. 输入root密码即可。

方法二:

    1. 打开Terminal
    1. jonesduan-MacBook-Pro:~ user$ sudo su
    1. 输入root密码即可。

两者方法区别在于:获取root权限后终端显示的不一样。

方法一是这样的:

1
jonesduan-MacBook-Pro:~ root#

方法二是这样的:

1
sh-3.2#

Mac 终端退出root权限切换user用户

从root用户切回user用户只需执行命令:su user (user是你自己安装时候的用户名),或是直接输入exit,也可Ctrl+D组合键推出

参考资料:
Mac下忘记root密码,修改root密码介绍参见
Mac下root用户的启用和停用参见

H5直播起航

直播起航

前言

前不久抽空对目前比较火的视频直播,做了下研究与探索,了解其整体实现流程,以及探讨移动端HTML5直播可行性方案。

发现目前 WEB 上主流的视频直播方案有 HLS 和 RTMP,移动 WEB 端目前以HLS为主(HLS存在延迟性问题,也可以借助 video.js 采用RTMP),PC端则以RTMP为主实时性较好,接下来将围绕这两种视频流协议来展开H5直播主题分享。

一、视频流协议HLS与RTMP

1. HTTP Live Streaming

HTTP Live Streaming(简称 HLS)是一个基于 HTTP 的视频流协议,由 Apple 公司实现,Mac OS 上的 QuickTime、Safari 以及 iOS 上的 Safari 都能很好的支持 HLS,高版本 Android 也增加了对 HLS 的支持。一些常见的客户端如:MPlayerX、VLC 也都支持 HLS 协议。

HLS 协议基于 HTTP,而一个提供 HLS 的服务器需要做两件事:

  • 编码:以 H.263 格式对图像进行编码,以 MP3 或者 HE-AAC 对声音进行编码,最终打包到 MPEG-2 TS(Transport Stream)容器之中;
  • 分割:把编码好的 TS 文件等长切分成后缀为 ts 的小文件,并生成一个 .m3u8 的纯文本索引文件;

浏览器使用的是 m3u8 文件。m3u8 跟音频列表格式 m3u 很像,可以简单的认为 m3u8 就是包含多个 ts 文件的播放列表。播放器按顺序逐个播放,全部放完再请求一下 m3u8 文件,获得包含最新 ts 文件的播放列表继续播,周而复始。整个直播过程就是依靠一个不断更新的 m3u8 和一堆小的 ts 文件组成,m3u8 必须动态更新,ts 可以走 CDN。一个典型的 m3u8 文件格式如下:

#EXTM3U

#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=200000
gear1/prog_index.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=311111
gear2/prog_index.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=484444
gear3/prog_index.m3u8

#EXT-X-STREAM-INF:PROGRAM-ID=1, BANDWIDTH=737777
gear4/prog_index.m3u8

可以看到 HLS 协议本质还是一个个的 HTTP 请求 / 响应,所以适应性很好,不会受到防火墙影响。但它也有一个致命的弱点:延迟现象非常明显。如果每个 ts 按照 5 秒来切分,一个 m3u8 放 6 个 ts 索引,那么至少就会带来 30 秒的延迟。如果减少每个 ts 的长度,减少 m3u8 中的索引数,延时确实会减少,但会带来更频繁的缓冲,对服务端的请求压力也会成倍增加。所以只能根据实际情况找到一个折中的点。

对于支持 HLS 的浏览器来说,直接这样写就能播放了:

1
2
<video src="http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8"
height="300" width="400" preload="auto" autoplay="autoplay" loop="loop" webkit-playsinline="true"></video>

注意:HLS 在pc端仅支持safari浏览器,类似chrome浏览器使用HTML5 video标签无法播放 m3u8 格式,可直接采用网上一些比较成熟的方案,如:sewise-playerMediaElementvideojs-contrib-hlsjwplayer

2. Real Time Messaging Protocol

Real Time Messaging Protocol(简称 RTMP)是 Macromedia 开发的一套视频直播协议,现在属于 Adobe。这套方案需要搭建专门的 RTMP 流媒体服务如 Adobe Media Server,并且在浏览器中只能使用 Flash 实现播放器。它的实时性非常好,延迟很小,但无法支持移动端 WEB 播放是它的硬伤。

虽然无法在iOS的H5页面播放,但是对于iOS原生应用是可以自己写解码去解析的, RTMP 延迟低、实时性较好。

浏览器端,HTML5 video标签无法播放 RTMP 协议的视频,可以通过 video.js 来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
<link href="http://vjs.zencdn.net/5.8.8/video-js.css" rel="stylesheet">

<video id="example_video_1" class="video-js vjs-default-skin" controls preload="auto" width="640" height="264" loop="loop" webkit-playsinline>
<source src="rtmp://10.14.221.17:1935/rtmplive/home" type='rtmp/flv'>
</video>

<script src="http://vjs.zencdn.net/5.8.8/video.js"></script>
<script>
videojs.options.flash.swf = 'video.swf';
videojs('example_video_1').ready(function() {
this.play();
});
</script>

3. 视频流协议HLS与RTMP对比

协议 原理 延时 优点 使用场景
HLS 短链接Http 集合一段时间数据生成ts切片文件更新m3u8文件 10s - 30s 跨平台 移动端为主
RTMP 长链接Tcp 每个时刻的数据收到后立即发送 2s 延时低、实时性好 PC+直播+实时性要求高+互动性强

二、直播形式

直播形式

目前直播展示形式,通常以YY直播、映客直播这种页面居多,可以看到其结构可以分成三层:① 背景视频层 ② 关注、评论模块 ③ 点赞动画

而现行H5类似直播页面,实现技术难点不大,其可以通过实现方式分为:① 底部视频背景使用video视频标签实现播放 ② 关注、评论模块利用 WebScoket 来实时发送和接收新的消息通过DOM 和 CSS3 实现 ③ 点赞利用 CSS3 动画

了解完直播形式之后,接下来整体了解直播流程。

三、直播整体流程

直播整体流程大致可分为:

  • 视频采集端:可以是电脑上的音视频输入设备、或手机端的摄像头、或麦克风,目前以移动端手机视频为主。

  • 直播流视频服务端:一台Nginx服务器,采集视频录制端传输的视频流(H264/ACC编码),由服务器端进行解析编码,推送RTMP/HLS格式视频流至视频播放端。

  • 视频播放端:可以是电脑上的播放器(QuickTime Player、VLC),手机端的native播放器,还有就是 H5 的video标签等,目前还是以手机端的native播放器为主。

直播整体流程

四、H5 录制视频

对于H5视频录制,可以使用强大的 webRTC (Web Real-Time Communication)是一个支持网页浏览器进行实时语音对话或视频对话的技术,缺点是只在 PC 的 Chrome 上支持较好,移动端支持不太理想。

1. 使用 webRTC 录制视频基本流程

① 调用 window.navigator.webkitGetUserMedia() 获取用户的PC摄像头视频数据。
② 将获取到视频流数据转换成 window.webkitRTCPeerConnection (一种视频流数据格式)。
③ 利用 WebScoket 将视频流数据传输到服务端。

注意:虽然Google一直在推WebRTC,目前已有不少成型的产品出现,但是大部分移动端的浏览器还不支持 webRTC(最新iOS 10.0也不支持),所以真正的视频录制还是要靠客户端(iOS,Android)来实现,效果会好一些。

WebRTC支持度

2. iOS原生应用调用摄像头录制视频流程

① 音视频的采集,利用AVCaptureSession和AVCaptureDevice可以采集到原始的音视频数据流。
② 对视频进行H264编码,对音频进行AAC编码,在iOS中分别有已经封装好的编码库(x264编码faac编码ffmpeg编码)来实现对音视频的编码。
③ 对编码后的音、视频数据进行组装封包。
④ 建立RTMP连接并上推到服务端。

视频流程

五、搭建Nginx+Rtmp直播流服务

1. 安装nginx、nginx-rtmp-module

① 先clone nginx项目到本地:

1
brew tap homebrew/nginx

② 执行安装nginx-rtmp-module

1
brew install nginx-full --with-rtmp-module

2. nginx.conf配置文件,配置RTMP、HLS

查找到nginx.conf配置文件(路径/usr/local/etc/nginx/nginx.conf),配置rtmp、hls。

① 在http节点之前添加 rtmp 的配置内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
rtmp {
server {
#监听的端口
listen 1935;
# RTMP 直播流配置
application rtmplive {
live on;
#为 rtmp 引擎设置最大连接数。默认为 off
max_connections 1024;
}
# HLS 直播流配置
application hls{
live on;
hls on;
hls_path /usr/local/var/www/hls;
hls_fragment 1s;
}
}
}

② 在http中添加 hls的配置

1
2
3
4
5
6
7
8
9
10
location /hls {  
# Serve HLS fragments
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /usr/local/var/www;
#add_header Cache-Controll no-cache;
expires -1;
}
  1. 重启nginx服务

重启nginx服务,浏览器中输入 http://localhost:8080,是否出现欢迎界面确定nginx重启成功。

1
nginx -s reload

六、直播流转换格式、编码推流

当服务器端接收到采集视频录制端传输过来的视频流时,需要对其进行解析编码,推送RTMP/HLS格式视频流至视频播放端。通常使用的常见编码库方案,如x264编码faac编码ffmpeg编码等。

鉴于 FFmpeg 集合了多种音频、视频格式编码,我们可以优先选用FFmpeg进行转换格式、编码推流。

1.安装 FFmpeg 工具

1
brew install ffmpeg

2.推流MP4文件

视频文件地址:/Users/gao/Desktop/video/test.mp4
推流拉流地址:rtmp://localhost:1935/rtmplive/home,rtmp://localhost:1935/rtmplive/home

1
2
3
4
5
//RTMP 协议流
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -acodec aac -f flv rtmp://10.14.221.17:1935/rtmplive/home

//HLS 协议流
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://10.14.221.17:1935/hls/test

注意: 当我们进行推流之后,可以安装VLC、ffplay(支持rtmp协议的视频播放器)本地拉流进行演示

3.FFmpeg推流命令

① 视频文件进行直播

1
2
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv  -q 10 rtmp://192.168.1.101:1935/hls/test
ffmpeg -re -i /Users/gao/Desktop/video/test.mp4 -vcodec libx264 -vprofile baseline -acodec aac -ar 44100 -strict -2 -ac 1 -f flv -q 10 rtmp://10.14.221.17:1935/hls/test

② 推流摄像头+桌面+麦克风录制进行直播

1
ffmpeg -f avfoundation -framerate 30 -i "1:0" \-f avfoundation -framerate 30 -video_size 640x480 -i "0" \-c:v libx264 -preset ultrafast \-filter_complex 'overlay=main_w-overlay_w-10:main_h-overlay_h-10' -acodec libmp3lame -ar 44100 -ac 1  -f flv rtmp://192.168.1.101:1935/hls/test

更多命令,请参考:
FFmpeg处理RTMP流媒体的命令大全
FFmpeg常用推流命令

七、H5 直播视频播放

移动端iOS和 Android 都天然支持HLS协议,做好视频采集端、视频流推流服务之后,便可以直接在H5页面配置 video 标签播放直播视频。

1
2
3
4
<video controls preload="auto"  autoplay="autoplay" loop="loop" webkit-playsinline>    
<source src="http://10.14.221.8/hls/test.m3u8" type="application/vnd.apple.mpegurl" />
<p class="warning">Your browser does not support HTML5 video.</p>
</video>

ps:① video标签添加webkit-playsinline属性(iOS支持)是保证视频在网页中内嵌播放。
② 针对微信浏览器,video标签层级最高的问题,需要申请添加白名单,请参照 http://bbs.mb.qq.com/thread-1242581-1-1.html?ptlang=2052。

八、总结

本文从视频采集上传,服务器处理视频推流,以及H5页面播放直播视频一整套流程,具体阐述了直播实现原理,实现过程中会遇到很多性能优化问题。

① H5 HLS 限制必须是H264+AAC编码。

② H5 HLS 播放卡顿问题,server 端可以做好分片策略,将 ts 文件放在 CDN 上,前端可尽量做到 DNS 缓存等。

③ H5 直播为了达到更好的实时互动,也可以采用RTMP协议,通过video.js实现播放。

参考资料: