动画GIF在网上流行是有理由的,它们提供比普通图像更多的参与度,同时与典型视频相比更容易消化,然而,GIF存储视频需要大量内存,导致页面加载时间慢和数据使用率高。使用HTML5视频,您可以将GIF内容的大小减少多达98%,同时仍然保留浏览器中GIF格式的独特品质。
动画GIF对我们来说很有意义, 在所有浏览器中,它们很容易制作并且运行良好。但GIF格式最初并不适用于动画。GIF格式的原始设计是提供一种使用无损压缩算法(称为LZW压缩)压缩单个文件内的多个图像的方法,这意味着它们可以在相当短的时间内下载,即使在慢速连接上也是如此。之后,添加了基本的动画功能,允许文件中的各种图像(帧)绘制时间延迟。默认情况下,构成动画的一系列帧仅显示一次,在显示最后一帧后停止。Netscape Navigator 2.0是第一款增加动画GIF循环功能的浏览器,它导致动画GIF的兴起,知道现在依然在流行中。
作为动画平台,GIF格式非常有限。动画中的每个帧仅限于256种颜色的调色板,多年来,压缩技术的进步使得动画和视频文件的压缩和使用方式得到了一些改进,与正确的视频格式不同,GIF格式不利用任何新技术,这意味着即使几秒钟的内容也会导致非常大的文件大小,因为存储了大量重复信息。
?
他们是由W3C推荐的前端三件套,并且互相之间可以很好地配合
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<!-- 关于网页的一些属性 -->
<!-- css样式 -->
</head>
<body>
<!-- 主体内容 -->
</body>
</html>
刚学半年web,只是写个作业可以这么用吧(●’?’●)
前为名字,后为文件格式
项目名 文件夹
images 文件夹
css 文件夹
网站名1.css
...
html 文件夹 也可以直接把每个网站卸载更外面一层
网站名1.thml
...
html的内容由一个个标签 嵌套或并列 组成
标签用 <标签名>内容</标签名> 或者 <标签名/>
文字可以直接输入,标签只不过是用来控制他们的 位置和显示效果 的
通用的标签主要
前者较为普遍;后者教为特殊
主要用来定一个区域来 划分更小的区域或者直接写入内容。
使用时用前者。
有许多特殊的标签,他们没有特别之处,只是特别拎出来以方便理解文件的整个结构
如
英文 | 通常的内容 |
header | 页眉 |
nav | 导航栏 |
main | 主要内容 |
article | 与上下文无关的独立内容 |
section | 其中一个区域 |
aside | 侧栏或边栏 |
footer | 页脚 |
detail | 某个细节的描述 |
summary | detail的标题 |
最后两个detail和summary通常是相互配合的。
上面这些都是有着特殊用处的标题,可以用下面的这些替代,但使用时相对复杂一些
主要用来直接写入内容。
使用时前后使用方法的皆有
空行之前的表示重要
用前者的有
英文 | 含义 |
h1 | 标题,字体最大的标题 |
中间部分 | 字体依次减小的标题 |
h6 | 标题,字体最小的标题 |
p | 段落 |
br | 换行 |
- | - |
hr | 水平线 |
sub | 下标文本 |
sup | 上标文本 |
ins | 插入文本 |
del | 删除文本 |
wbr | 此处适合换行 |
pre | 预格式文本 |
address | 表示地址 |
bdo | 文字方向 |
blockauote | 长的引用语 |
q | 短的引用语 |
mark | 定义有记号的文本 |
table te td th是相互配合组成一个表格的,table>tr>td
表格 | |
table | 表格 |
tr | 行标签 |
td | 列标签 |
th | 标题列 |
li必须在ol或ul内。通常用ul li
列表 | |
ol | 有序列表 |
ul | 无序列表 |
li | 列表项 |
表单可以用作问卷等
input属性typr value
表单 | |
form | 表单 |
label | 点击时自动定位到里面的input |
input | 输入 |
select | 多选 |
option | 多选的一个选项 |
用后者的有
英文 | 含义 |
br | 换行 |
img | 引入图片 |
video | 引入视频 |
audio | 引入 |
abbr | 此处适合换行 |
img video audio使用时需要更多的属性,比如URL路径等
以video为例:<video src="路径" autoplay="autoplay" controls="controls"/>
依次表示 URL路径 自动播放 显示播放器控件
他们有各自的属性
都必须有src属性以表示路径,img只需要src即可显示
各自可以在属性里选择width和height以显示宽度和高度(这两个在css 属性里介绍)
video和audio(他们有一样的可选属性)的其他可选属性在下面显示,一般一个只有一个可选择的值,那个值就是名字自己(言下之意就是,只写自己需要的属性)
英文 | 作用 |
autoplay | 自动播放 |
controls | 显示控件 |
loop | 循环播放 |
poster | 定义视频下载时需要的图像,值用路径表示 |
preload | autoplay时无效,定义文件预加载的时机,有3个值,具体在下面介绍 |
muted | 静音 |
preload的值
auto:默认值,直接加载 音频/视频
metadata:仅加载 音频/视频 元数据
none:不加载 音频/视频
用于标签的定义
class表示类,可以用于很多类似的标签,以方便使用一样的css样式
id是唯一的,可以为独特的标签单独使用样式
css一共有3段:选择器{属性:值;…}
样式有3种添加的方法
常见的伪 选择器/元素 | 表示方法 |
通用选择器 | * |
标签选择器 | 标签名 |
类选择器 | .类名 前面是一个下点 |
ID选择器 | #ID号 |
- | 以下选择器用于分隔不同的选择器 |
后代选择器 | 一个空格,其实这不是个选择器(但很像),可以选择出所有的后代 |
分组选择器 | , 逗号 |
子元素选择器 | > |
- | 以下选择器均需紧跟在最上面的选择器后 |
相邻兄弟选择器 | + |
不相邻兄弟选择器 | ~ |
属性选择器 | [属性值] 详细介绍 |
伪类选择器 | :单词 详细介绍 |
伪属性 | ::单词 详细介绍 |
后一个选择器不一定是前一个的子元素,也可以通过一些符号进行兄弟之间的变化 |
div>p
选中选中div中的p子元素(没有中间层的关系)
div+p
选中跟在div后的p
div~p
选中前面有相同父元素的div 的p
| 属性选择器 | 介绍,有红字是因为|竖线在编辑器里是特殊字符,在代码块内才能正常显示 |
|–|–|
| [属性] | 具有该属性的 |
|[属性=某值]|属性=某值的|
|[属性~=某值]|属性=用空格分隔的字词列表,某值是其中一个值
|[属性|=某值]|属性=用|分隔的字词列表,某值是其中一个值
|[属性^=某值]|具有该属性,以 某值 开头
|[属性$=某值]|具有该属性,以 某值 结尾
|[属性*=某值]|具有该属性,包含 某值
这类选择器全部按前面(一部分)选择器的介绍,紧跟在后面,以":"(冒号)为标识符(即以它开头,可以看成选择器的一部分)
以下均不写冒号
伪类选择器 | 介绍 |
- | 下面定义的是应用于超链接文本a的4种(鼠标在该元素的)状态, |
link | 正常状态(未访问) |
visited | 被访问之后 |
hover | 经过时 |
active | 单机被激活时 |
伪类选择器也有一些分类,见下 | |
结构伪类选择器 | 介绍 |
– | – |
first-child | 第一个子元素 |
lastchild | 最后一个子元素 |
- | 注意以下4个带()的,()内可以是一个算式或数字,选中符合结果的(可以是多个) |
nth-child() | 正数 第()个子元素 |
nth-last-child() | 倒数 第()个子元素 |
nth-of-type() | 父元素的 第()个同类子元素 |
nth-last-of-type() | 父元素的 倒数 第()个同类子元素 |
first-of-type | 父元素的 第一个同类子元素 |
last-of-type | 父元素的 倒数 第一个同类子元素 |
only-child | 父元素的唯一的子元素,(当然,)可能有很多子元素是唯一的 |
only-of-type | 父元素的唯一一个相同类型的子元素 |
empty | 没有任何内容的子元素 |
状态伪类选择器 | 介绍,UI元素一般指包含在form元素内的表单元素 |
enabled | 范围内的所有可用UI元素 |
disabled | 范围内的所有不可用UI元素 |
checked | 范围内的所有可用UI元素 |
否定伪类选择器 | |
not() | 不匹配该元素的选择器,()里是一个选择器的属性,将选取 不符合该选择器 的元素 |
目标伪类选择器 | |
target | 前面的匹配元素被相关URL指向,样式效果才生效,是动态选择器 |
用法:紧跟在后面,用两个":“作为标识符(也可以只用一个,但两个更好)。css样式里需包含content属性,值为带引号的字符(当然也可以什么都不写)
如:div:after{content=”";}
伪元素 | 介绍 |
before | 在前面插入一段文字 |
after | 在后面插入一段文字 |
用途:广泛
用法:有数字和单位组成:200px
当数字为0时,单位可有可无
使用频率:px最常用,其次em
单位有绝对和相对之分
相对单位 | 单位长度的含义 |
px | 像素(Pixel) |
em | 相对于父元素字体大小的倍数 |
ex | 相对于字符x的高度,通常为字体高度的一半 |
ch | 相对于数字0的宽度 |
rem | 相对于根元素(html)字体大小的倍数。若未被设置,则相对于浏览器的默认字体尺寸(一般为16px) |
vw | 相对于视口的宽度(视口被均分为100vw) |
vh | 相对于视口的高度(视口被均分为100vw) |
xmin | 相对于视口的 高度或宽度 中较小的那个(小的被均分为100vmin) |
vmax | 相对于视口的 高度或宽度 中较大的那个(大的被均分为100vmax) |
– | – |
绝对单位 | 单位长度的含义 |
pt | 点(point) |
pc | 派卡(pica),=我国新四号铅字的尺寸 |
in | 英寸(inch) |
cm | 厘米(centimeter) |
mm | mm(millimeter) |
用途:属性transition(变化时间)
用法:数字+单位:200ms
使用频率:都常用
单位 | 含义 |
s | 秒 |
ms | 毫秒 |
用途:
用法:数字+单位:120deg
使用频率:第一个deg最常用
单位 | |
deg | 角度(degrees) ,一个圆 360° |
grad | 梯度(gradians),一个圆 400梯度 |
rad | 弧度(radians),一个圆 PI |
turn | 转、圈(turns) |
用途:color,background(-color)
用法:值
使用频率:2和-1最为常用,-1代表最后一个
值 | 描述 |
white | 白色 |
具体英文单词↑ | 代表这个单词指代的颜色 |
rgb(r,g,b) | RGB值,rgb分别代表红绿蓝,[0,255] |
rgba(r,g,b,a) | 见上↑.a代表透明度 [0,1] |
rgb(r%,g%,b%) | RGB值(255*x%),三个值[0,100] ,最后即为0%-100% |
#rrggbb | RGB值,每个值用 两位 16进制数 表示(大小写不敏感),最后值在0-255 |
hsl(h,s,l) | hsl分别代表 色相(hue,[0,360])/饱和度(saturation,[0%,100%])/亮度(lightness,[0%,100%]) |
hsla(h,s,l,a) | ↑ a代表透明度 |
只在属性font-family中用到,定义字体类型
{font-family="微软雅黑"}
也可以不用双引号
{font-family=微软雅黑}
长度表示一栏:
1:值完全用长度表示,下面不再具体介绍
其他值的类型:值不能用长度表示,能用其他的较规范的表示表示
0:值不能用长度表示,也无法用其他规范的值表示
>1:有多个值,都用长度表示(同时出现多个值:长度可变)
属性 | 含义 | 长度表示 |
width | 宽度 | 1 |
height | 高度 | 1 |
position | 定位方式 | 字符 |
display | 显示方式 | 字符 |
float | 浮动方式 | 字符 |
clear | 清除部分浮动 | 字符 |
margin | 外边距 | 1-4 |
border | 边框 | 1-4 |
padding | 内边距 | 1-4 |
text-align | 字体左右的对齐方式 | 字符 |
text-indent | 文字之前空多少长 | 1 |
background | 背景,包括背景图,背景色,是否重复 | 0 |
line-heignt | 行高 | 1 |
font-family | 字体 | 字符 |
font-size | 字的大小 | 1 |
font-weight | 字的粗细 | 1 |
color | 字的颜色 | 颜色 |
opacity | 透明度0-1 | 0 |
z-index | 离屏幕远近,越大越显示在上面 | 0 |
transition | 状态变化的时间 | 时间 |
animation | 动画 | 0 |
overflow | 溢出 | 字符 |
值 | 含义 |
absolute | 绝对定位,相对于最近的定位过的父元素偏移 |
relative | 相对定位,在文件流中,相对自生原本在的位置偏移 |
fixed | 绝对定位,相对视口偏移 |
这里提到了偏移,它相对于定位的位置进行偏移 | |
relative在文件流中的意思是,在这个页面中单独占据一块空间,原始位置不会与在文件流中的同级元素重叠 |
block,none,grid较为常用
值 | 含义 |
block | 块状元素 |
none | 不显示 |
flex | flex布局 |
grid | grid布局 |
inline | 行内元素 |
inline-block | 行内块元素 |
grid和flex可以去网上找资料学习
着重介绍一下grid布局
含义:可以方便地进行网格状的布局,而后可以随意组合,分配给子元素
有许多属性和它配合使用(前提:display:grid,至少与前3个一起使用,前4个可以更美观地表示)
属性 | 含义 | 用法与解释 |
grid-template | 融合了下面上半部分的所有项 | 较为复杂,不讲 |
grid-template-rows | 每行的宽度(单独设置) | 多个 长度/auto/xfr 表示,有几行写几个(空格分开) |
grid-template-colums | 每列的宽度(单独设置) | ↑ 有几列写几个 |
grid-template-areas | 给每个格子命名,名字一样则合并 | 每行一对双引号,(适当使用回车) |
grid-gap | 行间距和列间距(统一设置) | 行间距 列间距 |
grid-row-gap | 行间距(统一设置) | 长度 |
grid-colum-gap | 列间距(统一设置) | 长度 |
place-items | 内容的 左右和上下 位置情况 | ↓ start/end/center/stretch |
align-items | 内容的 左右 位置情况 | ↑ 开头/末尾/中间/拉伸以盛满 |
justify-items | 内容的 上下 位置情况 | ↑ 对align来说,左是开头,右是末尾 |
place-content | 整个表格 左右和上下 位置情况 | ↑ 对juestify来说,上是开头,下是末尾 |
align-content | 整个表格的 左右 位置情况 | ↑有两个值的(place),用空格分开 |
justify-content | 整个表格的 上下 位置情况 | ↑ |
– | – | 以下都是子元素获得父元素分配的某个空间的语句 |
grid-template-area | 用指定的名字获取父类给这个名字的空间 | 字符串 |
– | – | 以下用 起始/末尾 的(间隔,即gap) 行/列 获得空间 |
grid-area | 起始/末尾 的 行/列 | 起始行/起始列/末尾行/末尾列 |
grid-row | 起始/末尾 的行 | 起始行/末尾行 |
grid-row-start | 起始 行 | 起始行 |
grid-row-end | 末尾行 | 末尾列 |
grid-column | 起始/末尾 的列 | 起始列/末尾列 |
grid-column-start | 起始列 | 起始列 |
grid-column-end | 末尾列 | 末尾列 |
着重介绍一下flex布局
含义:可以方便地进行弹性布局,尤其是垂直对齐的问题
有许多属性和它配合使用(前提:display:flex)
| 属性 | 含义
|–|–|–|
|flex-direction| 决定主轴的方向
| justify-content | 子元素在主轴方向上的分布方式
| align-items | 子元素在副轴方向上的分布方式
flex-direction的值 | 含义 |
row | 从上到下 |
row-reserve | 从下到上 |
column | 从左到右 |
column-reserve | 从右到左 |
justify-content的值 | 含义 |
flex-start | 主轴起始除处 |
flex-end | 主轴末尾处 |
center | 主轴中间 |
space-around | 元素两边留空隙,每个元素各占一份,不会像magin一样重合 |
space-between | 元素之间留空隙,这意味着最边上没有空隙 |
align-items的值 | 含义 |
flex-start | 副轴起始除处 |
flex-end | 副轴末尾处 |
center | 副轴中间 |
stretch | 全部占满 |
不设置这个属性,它可能会在区域之外显示
值 | 含义 |
auto | 超出长度时,可滚动 |
hidden | 隐藏 |
其他 | - |
分别代表 外边距/边框/内边距
他们都可以用4个长度值表示,分别代表 上右下左 的长度
也可以有auto值,即自动,它会使两边相等
如 margin:0 auto;
它的左右两边是相等的
后面的值可以省略,但至少有第一个值
第4个值(左)省略,则=第2个值(右)
第3个值(下)省略,则=第1个值(上)
第2个值(右)省略,则=第1个值(上)
他们的四个值可以单独设置,以margin为例子
margin-top,margin-right,margin-bottom,margin-left
值 | 含义(浮动方向/对齐方向) |
left | 左 |
center | 中 |
right | 右 |
先清除上一个区域来的浮动(float),而后可以设置自己的浮动
值 | 清除浮动 |
left | 左 |
right | 右 |
both | 左右 |
属性自由书写,其中的属性可以缺少一些或调换顺序
下面是两个较频繁使用的格式
有多个子属性
子属性 | 含义 | 值 |
background-image | 图片链接 | url(“路径”) |
background-repeat | 是否重复 | repeat/no-repeat |
background-position | 背景图的起始位置 | |
background-position-x | 离左边距离 | 长度/x% |
background-position-y | 离上边距离 | 长度/y% |
background-color | 背景色 | 颜色 |
background-size | 背景大小 | 长度 长度 |
background-attachment | 是否随着滚动而滚动 | scroll(默认)/fixed(不会)/local(会) |
<script type="text/javascript">
变量及赋值
计算语句
函数
事件
</script>
一定有事件,因为没有事件,js就没有什么意义
所有类型的变量 都用 var 变量名=值; 来定义
数据类型 | 含义 | 值 |
Undefined | 定义但未赋值 | undefined |
Null | 未定义,由Undefined衍生而来,值==undef | null |
Boolean | 布尔类型 | true false |
Number | 数字,正数,浮点数等, | 数字 |
String | 字符串,用单引号或双引号都可 | 字符串 |
Object | 项目,上面有属性,如{name:“myname”,age:18} | |
Array | 数组可以混杂其他类型,如[1,“myname”] | |
DOM对象 | 一个具体的标签,如window, document,element |
可用数据类型 | 数据类型相关函数 | 目的类型 | 含义与用法 |
Boolean Number | toString() | String | 变换成字符串 |
- | 下面两个 String转Number 的函数 | - | 下面的都把源操作数放()里 |
String | praseInt() | 整数(Number) | 字符串变整数 |
String | praseFloat() | 浮点数(Number) | 字符串变浮点数 |
- | 下面的是三种强制类型转换 | - | |
所有 | Boolean() | Boolean | 空字符串 0 undefined null 值为false,其他的true |
所有 | Number() | Number | 匹配整个数,fasle null为0,true为1 |
所有 | String() | String | 强制类型转换,变成字符串 |
isNaN | 判断是不是数 |
赋值符 | 含义 |
= | 右边赋给左边 |
– | – |
运算符 | |
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 余 |
++ | 自增,放在数的 前/后 不一样 |
? : | 三目运算符 |
( ) | 提高优先级,左括号,右括号 |
- | - |
判断符 | |
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
== | 值相等 |
!= | 值不相等 |
=== | 值和类型 都相等 |
!== | 值和类型 都不相等 |
- | - |
运算赋值符 | 左边的值加上右边的值,再赋值给左边 |
+= | |
-= | |
*= | |
\= | |
%= |
语句 | 使用 |
定义及赋值 | var i=3;也可以单独定义/赋值,定义统一用var |
选择(分支) | if-else或switch–case-default-break |
循环 | for或while或do-while |
跳出 | break或continue |
返回 | return |
一个事件只能绑定一个函数,如果一个事件要做多件事情,都放到一个函数里
表达形式:(被触发)对象.事件属性=…
不只是a有事件属性,其他的标签都有事件属性
事件属性有很多,根据可以触发者(比如鼠标,键盘,窗口)的不同分类
只列举常用的
鼠标触发:onclick(点击),onmouseover(鼠标再其上,和选择器hover有点像,但有不同)
窗口触发(被触发对象必须是window):onload(窗口完成后)
动态绑定
写在js样式里,被包含在其他的事件的函数中绑定
对象需要给出
参照函数的三种绑定方式
静态绑定
直接在标签里绑定,这时候对象就是自己这个标签
参照函数的第2种
比如 <p onclick="函数名(参数)">
函数里的组成与js的组成成分一致
函数定义的3种方式
参数可以有多个,定义时不需要声明类型,只需要参数名即可
第2种定义方式可以让定义在标签内的事件连结到这个函数
事件绑定
前两种定义方式
不需要 (参数)
需要用到匿名函数
直接用 事件=函数名(参数); 的结果
是 直接触发事件
第3种定义方式一般直接赋给内部的事件,即 事件=function (){函数体}
所周知,16年无疑是直播行业的春天,同时也是H5的一次高潮。
so,到现在用H5技术在移动端做网页直播也是见怪不怪了,但是!!!
今天我们的主角是webApp下播放视频
参考文献:
1)HTML5+CSS3+JQuery打造自定义视频播放器
简介
HTML5的<video>标签已经被目前大多数主流浏览器所支持,包括还未正式发布的IE9也声明将支持<video>标签,利用浏览器原生特性嵌入视频有很多好处,所以很多开发者想尽快用上,但是真正使用前还有些问题要考虑,尤其是 Opera/Firefox 和IE/Safari浏览器所支持的视频编码不同的问题,Google几个月前发布的开源视频编码VP8有望能解决这一问题,另外Google还发布了开放网络媒体项目WebM,旨在帮助开发者为开放网络制作出世界级媒体格式,Opera, Firefox, Chrome和IE9都将支持VP8,而且Flash Player也将可以播放VP8,这就意味着我们很快就可以只制作一个版本的视频然后在所有主流浏览器上播放了。另外一个主要的问题就是如何构建自定义的HTML5<video>播放器,这是目前Flash Player的优势所在,利用Flash的IDE所提供的接口可以很方便的构建一个个性化的视频播放器,那HTML5的<video>标签要怎样才能实现呢?这个问题就是本文所要解决的!我们将开发一个HTML5<video>视频播放器的jQuery插件,并且可以很方便的进行自定义,将分为以下几个部分:
1.视频控制工具条
2.视频控制按钮
3.打包成jQuery插件
4.外观和体验
5.自定义皮肤
视频控制工具条
做为一个专业的web开发人员,我们创建一个视频播放器时一定希望它的外观在各个浏览器中看起来一致(consistent),但是通过下面的图可以看到目前各个浏览器提供的视频控制工具条外观各不相同:
那就没办法了,我们得自己从头来创建这个控制工具条,利用HTML和CSS再加上一些图片实现起来并不算很难,另外通过HTML5多媒体元素提供的API我们可以很方便将创建的任何按钮与播放/暂停等事件进行绑定。
视频控制按钮
基本的视频控制工具条要包含一个播放/暂停按钮,一个进度条,一个计时器和一个音量控制按钮,我们将这些按钮放在<video>元素下面,并用一个div作为父容器:
Java代码
<div class="ghinda-video-controls">
<a class="ghinda-video-play" title="Play/Pause"></a>
<div class="ghinda-video-seek"></div>
<div class="ghinda-video-timer">00:00</div>
<div class="ghinda-volume-box">
<div class="ghinda-volume-slider"></div>
<a class="ghinda-volume-button" title="Mute/Unmute"></a>
</div>
</div>
复制代码
注意,我们使用元素的class属性来代替ID属性是为了方便在一个页面上使用多个播放器。
打包成jQuery插件
创建好控制按钮后我们需要配合多媒体元素的API来实现视频控制的目的,正如前面提到的一样我们将我们的播放器打包成jQuery插件,这样可以很好的实现复用,代码如下:
Java代码
$.fn.gVideo=function(options) {
// build main options before element iteration
var defaults={
theme: 'simpledark',
childtheme: ''
};
var options=$.extend(defaults, options);
// iterate and reformat each matched element
return this.each(function() {
var $gVideo=$(this);
//create html structure
//main wrapper
var $video_wrap=$('<div></div>').addClass('ghinda-video-player').addClass(options.theme).addClass(options.childtheme);
//controls wraper
var $video_controls=$('<div class="ghinda-video-controls"><a class="ghinda-video-play" title="Play/Pause"></a><div class="ghinda-video-seek"></div><div class="ghinda-video-timer">00:00</div><div class="ghinda-volume-box"><div class="ghinda-volume-slider"></div><a class="ghinda-volume-button" title="Mute/Unmute"></a></div></div>');
$gVideo.wrap($video_wrap);
$gVideo.after($video_controls);
这里先假设您了解jQuery并知道如何创建一个jQuery插件,因为这个不在本文的讨论范围之内,在上面这段脚本中我们使用jQuery动态创建视频控制工具条的元素,接下来为了绑定事件我们需要获取对应的元素:
Java代码
//get newly created elements
var $video_container=$gVideo.parent('.ghinda-video-player');
var $video_controls=$('.ghinda-video-controls', $video_container);
var $ghinda_play_btn=$('.ghinda-video-play', $video_container);
var $ghinda_video_seek=$('.ghinda-video-seek', $video_container);
var $ghinda_video_timer=$('.ghinda-video-timer', $video_container);
var $ghinda_volume=$('.ghinda-volume-slider', $video_container);
var $ghinda_volume_btn=$('.ghinda-volume-button', $video_container);
$video_controls.hide(); // keep the controls hidden
这里我们通过className方式获取,先让工具条隐藏直到所有资源加载完成,现在来实现播放/暂停按钮:
Java代码
var gPlay=function() {
if($gVideo.attr('paused')==false) {
$gVideo[0].pause();
} else {
$gVideo[0].play();
}
};
$ghinda_play_btn.click(gPlay);
$gVideo.click(gPlay);
$gVideo.bind('play', function() {
$ghinda_play_btn.addClass('ghinda-paused-button');
});
$gVideo.bind('pause', function() {
$ghinda_play_btn.removeClass('ghinda-paused-button');
});
$gVideo.bind('ended', function() {
$ghinda_play_btn.removeClass('ghinda-paused-button');
});
大多数浏览器在右键点击视频时会提供一个独立的菜单,它也提供了视频控制功能,如果用户通过这个右键菜单控制视频那就会跟我们的自定义控件冲突,所以为了避免这一点我们需要绑定视频播放器自身的“播放”,“暂停”和“结束”事件,在事件处理函数中处理播放/暂停按钮,控制按钮的样式。
为了创建进度条的拖动块,我们使用了jQuery UI的Slider组件:
Java代码
var createSeek=function() {
if($gVideo.attr('readyState')) {
var video_duration=$gVideo.attr('duration');
$ghinda_video_seek.slider({
value: 0,
step: 0.01,
orientation: "horizontal",
range: "min",
max: video_duration,
animate: true,
slide: function(){
seeksliding=true;
},
stop:function(e,ui){
seeksliding=false;
$gVideo.attr("currentTime",ui.value);
}
});
$video_controls.show();
} else {
setTimeout(createSeek, 150);
}
};
createSeek();
正如你所看到的,这里我们写了一个递归函数,通过循环比较video的readyState属性来判断视频是否已经准备好,否则我们就不能获得视频的时长也无法创建滑动块,当视频准备好后我们初始化滑动块并显示控制工具条,下一步我们通过绑定video元素的timeupdate事件实现计时器功能:
Java代码
var gTimeFormat=function(seconds){
var m=Math.floor(seconds/60)<10?"0"+Math.floor(seconds/60):Math.floor(seconds/60);
var s=Math.floor(seconds-(m*60))<10?"0"+Math.floor(seconds-(m*60)):Math.floor(seconds-(m*60));
return m+":"+s;
};
var seekUpdate=function() {
var currenttime=$gVideo.attr('currentTime');
if(!seeksliding) $ghinda_video_seek.slider('value', currenttime);
$ghinda_video_timer.text(gTimeFormat(currenttime));
};
$gVideo.bind('timeupdate', seekUpdate);
这里我们用seekUpdate函数获取video的currentTime属性值然后调用gTimeFormat函数进行格式化后得到当前播放的时间点。
至于音量控制控件我们还是利用jQuery UI的Slider组件然后利用自定义函数实现静音和取消静音的功能:
Java代码
$ghinda_volume.slider({
value: 1,
orientation: "vertical",
range: "min",
max: 1,
step: 0.05,
animate: true,
slide:function(e,ui){
$gVideo.attr('muted',false);
video_volume=ui.value;
$gVideo.attr('volume',ui.value);
}
});
var muteVolume=function() {
if($gVideo.attr('muted')==true) {
$gVideo.attr('muted', false);
$ghinda_volume.slider('value', video_volume);
$ghinda_volume_btn.removeClass('ghinda-volume-mute');
} else {
$gVideo.attr('muted', true);
$ghinda_volume.slider('value', '0');
$ghinda_volume_btn.addClass('ghinda-volume-mute');
};
};
$ghinda_volume_btn.click(muteVolume);
最后当我们自己的自定义视频控制工具条构造完成后需要移除<video>标签的controls属性,这样浏览器默认的工具条就被去掉了。
好了,我们的插件功能已经全部完成了,调用方法:
Java代码
$('video').gVideo();
这会将我们的插件应用到页面上每一个video元素上。
外观和体验
好的,现在到了比较有意思的部分,也就是播放器的外观和体验了。当插件功能已经完成后利用一点CSS就可以很容易地自定义样式了,我们将全部使用CSS3来实现。
首先,我们给播放器主容器加一些样式:
Java代码
.ghinda-video-player {
float: left;
padding: 10px;
border: 5px solid #61625d;
-moz-border-radius: 5px; /* FF1+ */
-ms-border-radius: 5px; /* IE future proofing */
-webkit-border-radius: 5px; /* Saf3+, Chrome */
border-radius: 5px; /* Opera 10.5, IE 9 */
background: #000000;
background-image: -moz-linear-gradient(top, #313131, #000000); /* FF3.6 */
background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #313131),color-stop(1, #000000)); /* Saf4+, Chrome */
box-shadow: inset 0 15px 35px #535353;
}
下一步,我们设置视频控制工具条左边浮动使它们水平对齐,利用CSS3的opacity和transitions我们给播放/暂停和静音/取消静音按钮添加了非常不错的悬浮效果:
Java代码
.ghinda-video-play {
display: block;
width: 22px;
height: 22px;
margin-right: 15px;
background: url(../images/play-icon.png) no-repeat;
opacity: 0.7;
-moz-transition: all 0.2s ease-in-out; /* Firefox */
-ms-transition: all 0.2s ease-in-out; /* IE future proofing */
-o-transition: all 0.2s ease-in-out; /* Opera */
-webkit-transition: all 0.2s ease-in-out; /* Safari and Chrome */
transition: all 0.2s ease-in-out;
}
.ghinda-paused-button {
background: url(../images/pause-icon.png) no-repeat;
}
.ghinda-video-play:hover {
opacity: 1;
}
如果您仔细看了前面那段根据视频播放状态(Playing/Paused)添加和移除播放/暂停按钮样式的JavaScript代码,就会明白为什么.ghinda-paused-button为什么要重写.ghinda-video-play的背景属性了。
现在轮到滑动块了,我们进度条和音量控制的滑动块的实现都是利用了jQuery UI的Slider组件,这个组件它本身自带了样式,定义在jQuery UI对应的css文件中,但是为了使滑动块和播放器其他控件外观保持一致我们全部重写了它的样式:
Java代码
.ghinda-video-seek .ui-slider-handle {
width: 15px;
height: 15px;
border: 1px solid #333;
top: -4px;
-moz-border-radius:10px;
-ms-border-radius:10px;
-webkit-border-radius:10px;
border-radius:10px;
background: #e6e6e6;
background-image: -moz-linear-gradient(top, #e6e6e6, #d5d5d5);
background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #e6e6e6),color-stop(1, #d5d5d5));
box-shadow: inset 0 -3px 3px #d5d5d5;
}
.ghinda-video-seek .ui-slider-handle.ui-state-hover {
background: #fff;
}
.ghinda-video-seek .ui-slider-range {
-moz-border-radius:15px;
-ms-border-radius:15px;
-webkit-border-radius:15px;
border-radius:15px;
background: #4cbae8;
background-image: -moz-linear-gradient(top, #4cbae8, #39a2ce);
background-image: -webkit-gradient(linear,left top,left bottombottom,color-stop(0, #4cbae8),color-stop(1, #39a2ce));
box-shadow: inset 0 -3px 3px #39a2ce;
}
这时候音量控制的滑动块一直显示在音量按钮旁边,我们需要将它改成默认隐藏,当鼠标悬浮在音量按钮上再动态显示出来,使用transitions来实现这个效果会是个不错的的选择:
Java代码
.ghinda-volume-box {
height: 30px;
-moz-transition: all 0.1s ease-in-out; /* Firefox */
-ms-transition: all 0.1s ease-in-out; /* IE future proofing */
-o-transition: all 0.2s ease-in-out; /* Opera */
-webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */
transition: all 0.1s ease-in-out;
}
.ghinda-volume-box:hover {
height: 135px;
padding-top: 5px;
}
.ghinda-volume-slider {
visibility: hidden;
opacity: 0;
-moz-transition: all 0.1s ease-in-out; /* Firefox */
-ms-transition: all 0.1s ease-in-out; /* IE future proofing */
-o-transition: all 0.1s ease-in-out; /* Opera */
-webkit-transition: all 0.1s ease-in-out; /* Safari and Chrome */
transition: all 0.1s ease-in-out;
}
.ghinda-volume-box:hover .ghinda-volume-slider {
position: relative;
visibility: visible;
opacity: 1;
}
利用一些基础的CSS属性以及CSS3提供的新属性我们打造了一个全新的播放器外观,它看起来是这个样子:
自定义皮肤
可能您已经注意到,我们在编写插件的时候已经定义了一些默认选项,它们是theme和childtheme,可以在调用插件的时候根据需要方便的应用自定义皮肤。
这里解释下theme就是所有控件的一整套样式定义,childtheme就是在theme基础上重写某些样式,我们在调用插件的时候可以同时指定这两个选项或者其中的一个:
Java代码
$('video').gVideo({
childtheme:'smalldark'
});
我们写了一个示例的皮肤smalldark,它只重写了部分的样式,显示效果是这样的:
总结
利用HTML5 video,JavaScript和CSS3打造自定义的视频播放器真的非常容易,t实现工具条功能用JavaScrip,外观和体验交给CSS3,我们得到了一个功能强大并且易于定制的解决方案!
enjoy!
2)mui Html5 Video 实现方案
前言: 最近项目中需要用到html5 视频播放功能,于是稍微研究了解了下,遇到了很多坑,特此记录下.
一、 Html5 Video
参考来源: http://www.xuanfengge.com/html5-video-play.html
(这篇博文确实帮助很大)
1.1、 目的
将Html5 Video功能应用到实际项目中,针对不同的平台和环境,进行个性化处理。
基本只考虑webkit浏览器兼容问题
1.2、 Html5 Video支持格式
只支持: .mp4后缀(.h264编码格式),和.webm后缀(专用web视频格式),以及.ogg后缀(音频文件)
注意: Html5 Video 可以添加多个source源来进行兼容适配,这样,当第一个源读取出问题时会自动读取下一个源. 比如可以同时在前面加上.webm和.mp4源,这样一个出错时会自动读取另一个可用源(因为不同浏览器,支持的格式是不一样的)
但是,Hybird模式的 Android 下,有些机型只能读取第一个source来源(测试华为和联想都是),所以也就是说在这种情况下,要确保第一个source源是正确的
各大浏览器兼容如图所示:
见图1
1.3、 不同平台环境和对应实现方案
说明: 这里分为两大块,普通浏览器环境(pc和手机,主要是移动端,pc不做特别处理)和Hybird模式的APP环境(Android和iOS).
注: Html5 video可以播放本地视频或者网络视频
1.3.1、 普通浏览器环境
*用Html5 Video 自带的播放栏控件
*用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.
注: 播放效果则由各大浏览器自行实现
手机端浏览器实现的不同效果,比如:
QQ浏览器(包括QQ客户端内置的浏览器):播放时会自动进入全屏
华为自带浏览器: 正常小窗口播放
1.3.2、 Hybird App环境
说明: 内联播放是指直接在video标签中播放视频,没有必要进入全屏
1.3.2.1、 Android内联播放
*用Html5 Video 自带的播放栏控件
*用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.
*Android内联播放需要注意,必须开启硬件加速,由于有些Android手机 webview是默认关闭硬件加速的,所以必须在创建这个带视频播放的webview时手动添加 硬件加速属性才行.(详情见plus创建webview的style)
style.hardwareAccelerated=true;
1.3.2.2、 iOS内联播放
*用Html5 Video 自带的播放栏控件
*用 Video 视频统一处理方法处理后,点击图片手动隐藏图片,设置视频大小,手动播放视频.
*内联播放注意要点,由于iOS下默认是全屏播放的,所以需要经过设置才能正常内联播放
第一步:在项目的manifest里面配置允许webview内联播放
"plus": { "splashscreen": { "autoclose": true,/*是否自动关闭程序启动界面,true表示应用加载应用入口页面后自动关闭;false则需调plus.navigator.closeSplashscreen()关闭*/ "waiting": true/*是否在程序启动界面显示等待雪花,true表示显示,false表示不显示。*/ }, "allowsInlineMediaPlayback": true,/*设置ios下允许内联播放*/ "popGesture": "close"
第二步: 创建video标签时,手动加上内联播放的属性(iOS不支持preload)
<!-- 让ios支持内联播放,必须添加 webkit-playsinline 标签 --> <video webkit-playsinline id="videoMedia" controls="controls" preload>
这样iOS手机在播放的时候才会采用内联播放
1.3.2.3、 Android非内联播放
*通过NJS使用原生播放器来播放视频,传入的url可以是本地的或网络的地址
*用 Video 视频统一处理方法处理后,点击图片之后,图片保持不变(所以没有必要隐藏图片),直接获取视频的资源地址,传给原生播放器播放
注: 这种模式下,性能要比直接html5自带播放器播放高
1.3.2.4、 iOS非内联播放
*用Html5 Video 自带的播放栏控件(非内联播放需要去除特定内联属性”webkit-playsinline”,这样才能全屏播放)
if(!isInlinePlay){ //如果是非内敛,ios需要去除内联样式 mediaTarget.removeAttribute('webkit-playsinline'); }
*用 Video 视频统一处理方法处理后,点击图片之后,图片保持不变(所以没有必要隐藏图片),直接调用video.play()播放视频(这时候会用一个全屏播放器来播放视频)
1.3.3、 注意要点
如果采用NJS通过Android原生播放器播放视频,目前无法监听到视频的一些自定义事件.(如下载中,播放完毕,暂停,播放时间等)
而如果采用Html5 Video自带播放,这些是可以通过脚本控制的.
所以选定方案时需要进行衡量
*另外,在Html5 Video播放时,无法监听到规定的结束事件seeked,只能在timeUpdate里面判断,如果ended为true就代表结束了.
*在NJS通过Android原生播放器播放时,可以通过document监听resume和pause事件判断是否进入播放和退出播放
1.4、 Tips
1.4.1、 关于Video 视频统一处理的方案
说明: 由于将一个<Video>直接显示在页面中,会有各种五花八门的播放器效果,如图:
(这里引用了参考来源的图)
见图2
显然,体验效果并不好,所以现在的做法是用一张模拟播放的图片来替代<Video>所在的地方,而将Video元素设置为1*1像素大小.然后给图片设置点击监听,监听到点击时,播放视频.
注意:
*这里不要用{display: none}或者{width:0;height:0;}的方式,因为这样视频元素会处于未激活的状态,给后续的处理带来麻烦.
*这里没有考虑ios<6和一些低版本的Android的兼容性问题了(这些版本里,无法直接通过video.play()来播放),因为项目环境基本上要求Android>4.0 iOS 7.0以上的.
*关于点击图片播放视频后,如果是内联播放模式下(或者是普通浏览器),就应该将图片隐藏,然后将视频大小设置为本来的大小(一般为图片大小);如果是非内联播放模式(全屏模式),就没有必要隐藏图片了,因为iOS下会自动打开一个全屏播放器来播放视频,Android下考虑到Html5 video较卡,所以也会通过NJS使用原生播放器来全屏播放视频.
1.4.2、 Android NJS播放视屏的实现代码
说明: 这个是Dcloud论坛中有人分享的
//非内联模式下的plus下的android才用到 var Intent=plus.android.importClass("android.content.Intent"); var Uri=plus.android.importClass("android.net.Uri"); var main=plus.android.runtimeMainActivity(); var intent=new Intent(Intent.ACTION_VIEW); var uri=Uri.parse(url); intent.setDataAndType(uri, "video/*"); main.startActivity(intent);
1.4.3、 如何读取元素的宽高
*在获取视频的宽度时,发现用 video.style.width无法获取到宽度.
后来查了资料,发现dom.style.width(height)只能获取在stye直接赋予的值.而如果是通过css样式表赋予的值是无法直接获取的,只能通过dom.offsetWidth(offsetHeight)获取.
*设置元素宽和高是不要直接在style里设置,而是最好通过css样式表赋予
*读取元素宽和高时,用offsetWidth(offsetHeight)
1.4.4、 关于全屏播放的问题
在PC端webkit浏览器下,全屏代码如下:
进入全屏: videoContainer(对应的dom).webkitRequestFullscreen();
退出全屏: document.webkitCancelFullScreen();
*但是经测试,在手机浏览器和Hybird模式下的手机环境中都无法使用,
应该是手机浏览器中video 播放器的全屏模式和pc端的有区别,已经脱离了webkit的全屏组件,而是用原生自己实现的.
1.5、 遇到问题及解决方法
1.5.1、 Video.currentTime 设置值时设置无效,或者变为0
原因分析:
与测试的服务器和端口有关,测试环境是放在hbuild本地浏览器的,没有处理好视频快进问题,所以会导致每次快进后,视频都会重置-在某些测试服务器上,则出现快进无效,但不会重置
解决方法:
将网页用其它正式服务器打开均可正常,如tomcat,wampserver,甚至直接在本地打开也行.
1.5.2、 无法通过代码Video.src获取资源路径
原因分析:
本实例中,Video是通过source添加src的,无法直接读取video的src
解决方法:
可以通过读取到第一个source的标签,再获取source的src
注:本来这个方法有一个缺点就是有可能第一个source的src不可用.但是由于Android中第一个source必须有用才行.否则无法正常播放.所以在确保第一个source正确的情况下能这样用.
1.5.3、 部分Android机型无法退出全屏
描述:
在使用Html5 Video自带播放器播放时,部分Android机型(如联想K860点击全屏按钮进入全屏后,无法退出全屏-因为进入全屏后,全屏按钮不见了)
原因分析: 可能是手机厂商擅自劫持了浏览器或者篡改了浏览器实现方式
解决方法:
目前无法解决,在这类机型中,建议直接采用非内联模式播放或者是尽量不要手动进入全屏
3)移动端HTML5<video>视频播放优化实践
遇到的挑战
移动端HTML5使用原生<video>标签播放视频,要做到两个基本原则,速度快和体验佳,先来分析一下这两个问题。
下载速度
以一个8s短视频为例,wifi环境下提供的高清视频达到1000kbps,文件大小大约1MB;非wifi环境下提供的低码率视频是500kbps左右,文件大小大约500KB;参考QzoneTouch多普勒测速,2g网络的平均速度是14KB/s,那么下载一个低码率视频耗时35s;那么要想流畅播放视频,就需要一个加载等待的过程,这个过程要有明确的反馈,不能让用户有“坏掉了”的感觉。
多普勒测速数据参考
# | dns(s) | conn(s) | rtt(s) | tran(kb/s) |
---|---|---|---|---|
2g | 3.85785 | 2.33482 | 2.57478 | 14.0374 |
3g | 1.60643 | 0.743109 | 0.608047 | 60.1967 |
wifi | 0.986921 | 0.550208 | 0.444332 | 70.8728 |
用户体验
视频是否可以自动播放,是否能循环播放,是否能显示下载进度,播放的时候如何隐藏控制条,暂停的时候又能显示出来呢。这些问题看上去貌似简单,但是由于PC/iOS/Android这些不同平台、不同的浏览器内核、甚至相同内核的不同版本,所实现的<video>属性、方法和事件差异较大,解决兼容性问题又给开发造成了很大困扰。
分析原因
事件差异
下面是播放一个短视频,在不同平台触发事件和获取属性的差异表现。
PC
# | event | readyState | currentTime (s) | buffered (s) | duration (s) | 视频状态 |
---|---|---|---|---|---|---|
1 | loadstart | NOTHING | 0 | – | – | – |
2 | suspend | NOTHING | 0 | – | – | – |
3 | play | NOTHING | 0 | – | – | – |
4 | waiting | NOTHING | 0 | – | – | – |
5 | durationchange | METADATA | 0 | 5.35 | 7.91 | 获取到视频长度 |
6 | loadedmetadata | METADATA | 0 | 0.66 | 7.91 | 获取到元数据 |
7 | loadeddata | ENOUGHDATA | 0 | 0.66 | 7.91 | – |
8 | canplay | ENOUGH_DATA | 0 | 0.66 | 7.91 | – |
9 | playing | ENOUGH_DATA | 0 | 0.66 | 7.91 | 开始播放 |
10 | canplaythrough | ENOUGH_DATA | 0 | 0.66 | 7.91 | 可以流畅播放 |
11 | progress | ENOUGH_DATA | 0.11 | 3.68 | 7.91 | 持续下载 |
12 | timeupdate | ENOUGH_DATA | 0.14 | 4.44 | 7.91 | 播放进度变化 |
… | … | … | … | … | … | … |
23 | progress | ENOUGH_DATA | 1.77 | 7.91 | 7.91 | 下载完毕 |
24 | suspend | ENOUGH_DATA | 1.77 | 7.91 | 7.91 | – |
25 | timeupdate | ENOUGH_DATA | 1.9 | 7.91 | 7.91 | 继续播放中 |
… | … | … | … | … | … | … |
48 | timeupdate | ENOUGH_DATA | 7.7 | 7.91 | 7.91 | – |
49 | timeupdate | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
50 | seeking | METADATA | 0 | 7.91 | 7.91 | – |
51 | waiting | METADATA | 0 | 7.91 | 7.91 | – |
52 | timeupdate | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
53 | seeked | ENOUGH_DATA | 0 | 7.91 | 7.91 | 播放完毕进度回到起点 |
54 | canplay | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
55 | playing | ENOUGH_DATA | 0 | 7.91 | 7.91 | 循环播放 |
56 | canplaythrough | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
57 | timeupdate | ENOUGH_DATA | 0.19 | 7.91 | 7.91 | – |
… | … | … | … | … | … | … |
iOS
# | event | readyState | currentTime (s) | buffered (s) | duration (s) | 视频状态 |
---|---|---|---|---|---|---|
1 | loadstart | NOTHING | 0 | – | – | – |
2 | play | NOTHING | 0 | – | – | – |
3 | waiting | NOTHING | 0 | – | – | – |
4 | durationchange | METADATA | 0 | – | 7.91 | 获取到视频长度 |
5 | loadedmetadata | METADATA | 0 | – | 7.91 | 获取到元数据 |
6 | loadeddata | ENOUGHDATA | 0 | – | 7.91 | – |
7 | canplay | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
8 | canplaythrough | ENOUGH_DATA | 0 | 7.91 | 7.91 | 可以流畅播放 |
9 | playing | ENOUGH_DATA | 0 | 7.91 | 7.91 | 开始播放 |
10 | progress | ENOUGH_DATA | 0 | 7.91 | 7.91 | 下载完毕 |
11 | suspend | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
12 | timeupdate | ENOUGH_DATA | 0.02 | 7.91 | 7.91 | 播放进度变化 |
… | … | … | … | … | … | … |
43 | timeupdate | ENOUGH_DATA | 7.8 | 7.91 | 7.91 | – |
44 | timeupdate | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
45 | seeked | ENOUGH_DATA | 0 | 7.91 | 7.91 | 播放完毕进度回到起点 |
46 | timeupdate | ENOUGH_DATA | 0.22 | 7.91 | 7.91 | 循环播放 |
… | … | … | … | … | … | … |
Android
# | event | readyState | currentTime (s) | buffered (s) | duration (s) | 视频状态 |
---|---|---|---|---|---|---|
1 | loadstart | NOTHING | 0 | – | – | – |
2 | play | NOTHING | 0 | – | – | – |
3 | waiting | NOTHING | 0 | 0 | – | – |
4 | durationchange | ENOUGH_DATA | 0 | 0 | 0 | – |
5 | durationchange | ENOUGH_DATA | 0 | 0 | 7.91 | 获取到视频长度 |
6 | loadedmetadata | ENOUGH_DATA | 0 | 0 | 7.91 | 获取到元数据 |
7 | loadeddata | ENOUGHDATA | 0 | 0 | 7.91 | – |
8 | canplay | ENOUGH_DATA | 0 | 0 | 7.91 | – |
9 | canplaythrough | ENOUGH_DATA | 0 | 0 | 7.91 | – |
10 | playing | ENOUGH_DATA | 0 | 0 | 7.91 | – |
11 | timeupdate | ENOUGH_DATA | 0 | 0 | 7.91 | – |
12 | progress | ENOUGH_DATA | 0 | 3.57 | 7.91 | 下载中 |
13 | timeupdate | ENOUGH_DATA | 0.2 | 6.89 | 7.91 | 开始播放 |
14 | progress | ENOUGH_DATA | 0 | 7.91 | 7.91 | 下载完毕 |
… | … | … | … | … | … | … |
49 | timeupdate | ENOUGH_DATA | 7.79 | 7.91 | 7.91 | – |
50 | progress | ENOUGH_DATA | 7.87 | 7.91 | 7.91 | – |
51 | timeupdate | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
52 | seeking | ENOUGH_DATA | 0 | 7.91 | 7.91 | 播放完毕进度回到起点 |
53 | timeupdate | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
54 | seeked | ENOUGH_DATA | 0 | 7.91 | 7.91 | 循环播放失败卡住了 |
55 | progress | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
56 | stalled | ENOUGH_DATA | 0 | 7.91 | 7.91 | – |
一些常用且需要重点关注的<video>事件
event | iOS | Android |
---|---|---|
****************** | *********************************************** | *********************************************** |
play | 只是要播放视频,响应的是video.play()方法,并不代表已经开始播放 | 和iOS一样,仅是响应video.play()方法 |
durationchange | 会执行一次,一定会获取到视频的duration | 可能会执行多次,只有最后一次才能获取到真实的duration,前面的duration都是0;但低版本Android可能获取到的duration是0或1;(本文提到的低版本Android大部分是4.1以下) |
canplay | 可以认为是视频元素没有问题,可以运行,没有更多含义了,基本用不上 | 同iOS |
canplaythrough | 会有明确的缓冲,表示可以流畅播放了; | 没有什么用,视频仍然会卡住,数据可能还没有开始加载; |
playing | 明确表示播放开始了; | 依然没有用,视频可能并没有开始播放; |
progress | 有明确的下载,可以获取到当前的buffer,并且全部下载完毕后不在触发; | 不一定有明确的数据下载,并且全部下载完毕后依然继续触发; |
timeupdate | 会有明确的进度变化,可以获取到currentTime; | 进度不一定变化,currentTime可能总是0,但是第一次有currentTime变化的timeupdate事件一定代表了视频开始播放了; |
error | iOS中会有明确的错误抛出; | Android中某些浏览器会莫名其妙的抛出error; |
stalled | 网络状况不佳,导致视频下载中断; | 在没有play之前,也可能会抛出该事件。 |
属性差异
attributes | iOS | android |
---|---|---|
****************** | *********************************************** | *********************************************** |
poster 封面图片 | 支持,但是加载速度明显比在<img>中要慢; | 不一定支持(浏览器厂商的实现标准不统一); |
preload 预加载 | iPhone不支持; | 可能支持; |
autoplay 自动播放 | iPhone Safari中不支持,但在webview中可能被开启;iOS开发文档明确说明蜂窝网络下不允许autoplay; | 可能支持; |
loop 循环播放 | 支持 | 可能支持; |
controls 控制条 | 支持,但是需要开始播放了才显示 | 基本都支持显示或者不显示 |
width和height | 一定给出明确的属性设置,切不能为0; | 如果不设置,仅仅通过CSS样式去控制视频大小,可能会导致标签失效。 |
其他怪异bug和不友好表现
iOS | android |
---|---|
********************************************************* | ********************************************************* |
物理位置覆盖在<video>区域上的元素,click和touch等事件会失效,比如一个<a>链接如果覆盖在<video>上,那么点击后没有任何效果。 | – |
iOS8.0+中,单页面播放视频超过16个,再播放的视频全部MediaError解码异常无法播放。 | – |
iPhone的Safari会弹出一个全屏的播放器来播放视频,iPad则支持内联播放。iOS7+ 如果webview(比如微信)开启了webview.allowsInlineMediaPlayback=YES; ,可以通过设置webkit-playsinline 属性支持内联播放; | 支持内联播放,但某些厂商会用自己的播放器劫持原生的视频播放; |
下载视频时,会先发送一个2字节的请求来获取视频元数据(比如时长),然后再不断的发送分包续传(206)请求来下载视频,抓包显示请求数和请求量至少有一倍的冗余(x2),这个严重的bug在iOS8中有明显的修复,但是分包的206请求仍然会有冗余数据的下载,浪费了流量。 | 比iOS的处理方式好,没有第一个2字节请求,没有流量损耗; |
– | 低版本Android(<=4.0.4)中,<video>如果在有相对和决定定位的层中,可能会导致整个页面错位。 |
– | 某些浏览器厂商会劫持<video>,用其“自己”的播放器来播放视频,“破坏”了产品本身的播放体验,那么只能case by case的解决了。 |
加载视频时没有进度提示,视觉上看不出是播放完了还是卡住了; | 加载视频时,大都会显示一个自带的loading UI(菊花)。 |
最佳实践
视频初始化
如果将一个<video>直接显示在页面中,那么就会看到各种五花八门的播放器初始效果;
这显然不是一个好的视觉体验,那么通常的做法是制作一个模拟的视频播放视图,比如一个封面加一个播放按钮。
而真实的<video>视频元素要隐藏起来,如何隐藏呢?最好不要用{display: none}
或者{width:0;height:0;}
的方式,因为这样视频元素会处于未激活的状态,给后续的处理带来麻烦。最佳的方式是将视频设置成1×1像素大小,放在视觉边缘的位置。
1 2 3 4 5 | <!--iOS--> <video webkit-playsinline width="1"height="1"class="vplayinside notaplink"x-webkit-airplay controls loop="loop"src="<%=src%>"></video> <!--Android--> <video width="1"height="1"controls loop="loop"src="<%=src%>"></video> |
自动播放
autoplay的支持依赖内核和网络状况,比如iPhone在蜂窝网络下明确禁用了autoplay;
经过试验,在没有明确的用户操作的情况下,直接通过video.play()
也是无法激活播放的;
并且在产品设计上,自动播放也不是一个舒服的用户体验,所以产品设计上尽量避免使用自动播放。
点击播放
之前提到,视频最好通过1px大小隐藏起来,那么这时如何触发播放呢?
经过试验,当在明确的用户操作(touch、click)时,通过这些用户行为事件的回调函数,用video.play()
是可以触发视频播放的,那么能否在用户操作后,再去同步的创建和播放视频呢?答案是肯定的,这无疑是一个视频元素初始化的最佳实践,但是有些差异需要注意。
iOS6+
可以在用户的touch时间中动态创建并播放视频。
iOS < 6
可以在用户的touch时间中动态创建视频,但不能播放;要再追加一个click事件来启动播放;也就是说,给伪造的视频播放按钮同时绑定tap和click事件,在tap的时候创建,在之后300毫秒的click中去播放。
Android
大部分高版本Android可以像iOS6+那样去处理,但是低版本的不行,必须要通过click事件去传递video.play()
,为了保持兼容,最好是用帮tap和click两个事件来分别完成视频的初始化和播放。
我们还发现,有些低版本Android中,无法通过video.play()
来播放视频,必须有真实的用户点击视频元素才能播放;这种情况,有一个技巧就是在tap的时候初始化并放大视频覆盖在播放视图中,让300毫秒后的真实点击行为穿透点击在视频元素上来实现播放。
循环播放
如果视频需要循环播放,那么就增加loop
属性,是否能循环播放就看浏览器是否支持了,因为还没有找到hack技巧来强制循环播放;
即使,在不支持循环播放的Android中,通过监听seeked
事件知道了播放进度到了终点或起点暂停了,此时也无法通过video.play()
来让视频重新播放。
监控下载进度
如何获取视频时长和已经下载的时长?
1 2 3 4 5 6 7 8 9 10 11 12 13 | // 视频时长 varduration=video.duration // 获取视频已经下载的时长 functiongetEnd(video){ varend=0 try{ end=video.buffered.end(0)||0 end=parseInt(end*1000+1)/1000 }catch(e){ } returnend } |
progress事件表示视频在加载,但是它的触发频率和时机并不规律,最佳做法是通过一个定时器去实时获取end,当end >=duration时,表示已经下载完毕,再终止定时器。
1 2 3 4 5 6 7 8 9 10 | vartimer=setInterval(function(){ varend=getEnd(video), duration=video.duration if(end<duration){ return } clearInterval(timer) },1000) |
全部下载后再播放
假设播放短视频,如果网络不佳,会造成播放断断续续,在iOS中这种停顿还没有一个明确的等待提示,这不是一个好的体验,那么是否可以将视频全部下载完毕再播放呢?
在iOS中,可以在视频刚开始下载的时候马上暂停,此时下载还将继续,可以做一个loading的菊花告知视频正在加载,然后等到视频全部下载完再开始播放。
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 | $(video).one('loadeddata',function(){ // 暂停,但下载还在继续 video.pause() // 启动定时器检测视频下载进度 vartimer=setInterval(function(){ varend=getEnd(video), duration=video.duration if(end<duration){ return } varwidth=$(video).parent().width() // 下载完了,开始播放吧 $(video).attr{ width:width, height:width } video.play() clearInterval(timer) },1000) }) |
缓冲播放——边下边播时,选择开始播放的最佳时间点
当视频越来越长或者网络慢时,等待视频全部下载完再播放也不是好的体验,最好能边下边播,缓冲到流畅状态就开始播放,那什么时候播放才是最佳时间点呢?
在iOS中,canplaythrough事件就是这个最佳时间点,它是通过动态计算缓冲量和下载速度得出的视频可以流畅播放的状态反馈。
canplaythrough event: The user agent estimates that if playback were to be started now, the media resource could be rendered at the current playback rate all the way to its end without having to stop for further buffering.
注意:下载完再播放和缓冲播放只适用于iOS。
统计播放时间和播放次数
要统计实际的播放时间,要累加timeupdate事件变化的时间,再减去中间可能暂停的时间。
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 | $video.on('playing',function(){ // 开始播放是打点 $video.attr('data-updateTime',+newDate()) }) $video.on('pause',function(){ // 暂停播放时清除打点 $video.removeAttr('data-updateTime') }) // 累加播放时间 $video.on('timeupdate',function(event){ var$video=$(event.target), updateTime=parseInt($video.attr('data-updateTime')||0), playingTime=parseInt($video.attr('data-playingTime')||0), times=parseInt($video.attr('data-times')||0), newtimes=0, video=$video.get(0), duration=parseFloat($video.attr('data-duration')||0), now=+newDate() // 播放时间 playingTime=playingTime+now-updateTime // 播放次数 newtimes=Math.ceil(playingTime/1000/duration) $video.attr('data-playingTime',playingTime) $video.attr('data-updateTime',now) }) |
异常处理
对error事件做详细的上报;
对stalled事件做统计上报,并提示用户网络慢等。
参考数据
微视触屏版iOS视频测速
网络环境 | 视频码率 | 获取到视频时长 时间点(s) | 开始流畅播放 时间点(s) | 全部下载完毕 时间点(s) | 视频长度(s) |
---|---|---|---|---|---|
wifi | 1000kbps | 2.86 | 3.97 | 5.85 | 8.69 |
非wifi | 500kbps | 4.56 | 8 | 10.62 | 8.67 |
搬好凳子看HTML
首先我们在HB下创建一个新的app项目,名称为 欠债
新建一个video.html
webkit-playsinline : 在ios中,加入此属性,可以关闭自动全屏播放
object-fit:fill : 视频充满video容器的大小
详细理由请看参考文献2or3
在此我们向项目里放置一个mp4格式的视频,视频内容不限,可以是小动画,也可以是
ps:要在meta中加上,否则视频会扩充变形哦
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
OK,现在布局已经完成,一个视频已经在页面中了
旁白:尼玛,点了没反应,那这怎么播放?
楼主:你们这群家伙看别的小视频等个1小时都行。。。
旁白:一个简单的播放器,至少要有 暂停/播放,进度条,视频时长,全屏等控件吧
楼主:来来来,不要急,先来个播放按钮写在video标签后面
<div class="bad-video"> <video class="" webkit-playsinline style="object-fit:fill;"> <source src='xx.mp4' type="video/mp4"></source> <p>设备不支持</p> <video> <img src="img/play.png"/> </div>
写好样式、
.bad-video { position: relative; overflow: hidden; background-color: #CCCCCC; } .bad-video .vplay{ position: absolute; width: 15%; z-index: 99; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); transform: translate(-50%, -50%); }
楼主:当当当
再在后面加一个控制条
<img src="img/play.png" class="vplay" /> <div class="controls"> <div> <div class="progressBar"> <div class="timeBar"></div> </div> </div> <div><span class="current">00:00</span>/<span class="duration">00:00</span></div> <div><span class="fill">全屏</span></div> </div>
.bad-video .controls { width: 100%; height: 2rem; line-height: 2rem; font-size: 0.8rem; color: white; display: block; position: absolute; bottom: 0; background-color: rgba(0, 0, 0, .55); display: -webkit-flex; display: flex; }.bad-video .controls>* { flex: 1; }.bad-video .controls>*:nth-child(1) { flex: 6; }.bad-video .controls>*:nth-child(2) { flex: 2; text-align: center; }.bad-video .controls .progressBar { margin: .75rem 5%; position: relative; width: 90%; height: .5rem; background-color: rgba(200, 200, 200, .55); border-radius: 10px; }.bad-video .controls .timeBar { position: absolute; top: 0; left: 0; width: 0; height: 100%; background-color: rgba(99, 110, 225, .85); border-radius: 10px; }
总算有个看起来像样的了
旁白:楼主,可是还是不能播放啊
楼主:叫你别急,要不你先去撸一把,我写好了文字@你
旁白:好啊,早说嘛,我先走了,记得@我
楼主:你走,省的我精神分裂码两个人的字
好,现在Html元素已经基本上弄好啦,看起来不是那么low了
*请认真填写需求信息,我们会在24小时内与您取得联系。