HTML5
什么是HTML5
定义
万维网的核心语言、标准通用标记语言下的一个应用超文本标记语言(HTML)的第五次重大修改
环境
支持Html5的浏览器包括Firefox(火狐浏览器),IE9及其更高版本,Chrome(谷歌浏览器),Safari,Opera等;国内的傲游浏览器(Maxthon),以及基于IE或Chromium(Chrome的工程版或称实验版)所推出的360浏览器、搜狗浏览器、QQ浏览器、猎豹浏览器等国产浏览器同样具备支持HTML5的能力
目的
HTML5的设计目的是为了在移动设备上支持多媒体。新的语法特征被引进以支持这一点,如video、audio和canvas 标记。HTML5还引进了新的功能,可以真正改变用户与文档的交互方式
新增特性
增加了新特性:语义特性,本地存储特性,设备兼容特性,连接特性,网页多媒体特性,三维、图形及特效特性,性能与集成特性, 地理定位, 设备权限与功能,CSS3特性
session、 video、 audio、 worker、 canvas、SVG requestAnimationFrame、 history、 file Reader、 Full Screen、 Page Visibility、 getUserMedia API、Battery API、Link Prefetching、orientationchange、navigator.vibrate、navigator.language 、navigator.onLine、contentEditable 、window.onblur & window.onfocus、loaction
对比HTML4
相比之前的进步:取消了一些过时的HTML4标记,将内容和展示分离,一些全新的表单输入对象,全新的,更合理的Tag,本地数据库,Canvas 对象,浏览器中的真正程序,Html5取代Flash在移动设备的地位
优点
-
提高可用性和改进用户的友好体验;
-
有几个新的标签,这将有助于开发人员定义重要的内容;
-
可以给站点带来更多的多媒体元素(视频和音频);
-
可以很好的替代FLASH和Silverlight;
-
当涉及到网站的抓取和索引的时候,对于SEO很友好;
-
将被大量应用于移动应用程序和游戏;
-
可移植性好。
缺点
随着标签功能与对应API的复杂化 , 各浏览器厂商在规则的实践上存在差异, 兼容性需要控制。
HTML5中的新增标签
为了更好地处理今天的互联网应用,HTML5添加了很多新元素及功能,比如: 图形的绘制,多媒体内容,更好的页面结构,更好的形式 处理,和几个api拖放元素,定位,包括网页 应用程序缓存,存储,网络工作者,等。
语义结构标签
HTML5提供了新的元素来创建更好的页面结构:
*标签* | *描述* |
---|---|
article | 定义页面的侧边栏内容 |
aside | 定义页面内容之外的内容。 |
bdi | 允许您设置一段文本,使其脱离其父元素的文本方向设置。 |
command | 定义命令按钮,比如单选按钮、复选框或按钮 |
details | 用于描述文档或文档某个部分的细节 |
dialog | 定义对话框,比如提示框 |
summary | 标签包含 details 元素的标题 |
figure | 规定独立的流内容(图像、图表、照片、代码等等)。 |
figcaption | 定义 |
footer | 定义 section 或 document 的页脚。 |
header | 定义了文档的头部区域 |
mark | 定义带有记号的文本。 |
meter | 定义度量衡。仅用于已知最大和最小值的度量。 |
nav | 定义运行中的进度(进程)。 |
progress | 定义任何类型的任务的进度。 |
ruby | 定义 ruby 注释(中文注音或字符)。 |
rt | 定义字符(中文注音或字符)的解释或发音。 |
rp | 在 ruby 注释中使用,定义不支持 ruby 元素的浏览器所显示的内容。 |
section | 定义文档中的节(section、区段)。 |
time | 定义日期或时间。 |
wbr | 规定在文本中的何处适合添加换行符。 |
-
传统页面的标签使用
-
HTML5页面中的标签使用,如:
<body>
<header>定义了文档的头部区域</header>
<div>
<article>定义页面的侧边栏内容</article>
<aside>定义页面内容之外的内容</aside>
</div>
<footer>定义 section 或 document 的页脚</footer>
</body>
canvas
*标签* | *描述* |
---|---|
canvas | 标签定义图形,比如图表和其他图像。该标签基于 JavaScript 的绘图 API |
多媒体
标签 | 描述 |
---|---|
audio | 定义音频内容 |
video | 定义视频(video 或者 movie) |
source | 定义多媒体资源 和 字体 |
embed | 定义嵌入的内容,比如插件。 |
track | 为诸如 和 元素之类的媒介规定外部文本轨道。 |
表单
标签 | 描述 |
---|---|
datalist | 定义选项列表。请与 input 元素配合使用该元素,来定义 input 可能的值。 |
keygen | 规定用于表单的密钥对生成器字段。 |
output | 定义不同类型的输出,比如脚本的输出。 |
HTML中移除的标签
以下的 HTML 4.01 元素在HTML5中已经被删除:
元素分类
根元素
html
:root
元数据元素
head、base、meta、title、link、style
分区元素
body、header、footer、aside、main、nav、section、article、h1~h6、hgroup、address
块元素
div、p、ol、ul、li、dd、dl、dt、hr、blockquote、figcaption、figure
内联文本元素
a、span、br、abbr、cite、code、small、time、bdi、bdo
data、dfn、kbd、mark、q、rb、rp、rt、rtc、ruby、samp、u、var、wbr
媒体元素
audio、img、video、map、track、area
内嵌元素
embed、iframe、object、param、picture、source
脚本元素
canvas、script、noscript
编辑标识元素
del、ins
表格元素
table、caption、thead、tbody、tfoot、tr、th、td、colgroup、col
表单元素
form、label、input、button、select、datalist、optgroup、option、textarea、fieldset、legend、meter、output、progress
交互元素
details、dialog、menu、summary
Web 组件
template、slot
兼容处理
在不支持HTML5新标签的浏览器里,会将这些新的标签解析成行内元素(inline)对待,所以我们只需要将其转换成块元素(block)即可使用,但是在IE9版本以下,并不能正常解析这些新标签,但是却可以识别通过document.createElement(‘tagName’)创建的自定义标签,于是我们的解决方案就是将HTML5的新标签全部通过document.createElement(‘tagName’)来创建一遍,这样IE低版本也能正常解析HTML5新标签了。
处理方式
在实际开发中我们更多采用的是通过检测IE浏览器的版本来加载三方的一个JS库来解决兼容问题(测试在IE下面的兼容性:ieTester软件的使用)
HTML5中新增标签全局属性
属性 | 描述 |
---|---|
contenteditable | 规定元素内容是否可编辑。 |
contextmenu(不兼容) | 规定元素的上下文菜单。上下文菜单在用户点击元素时显示。 不需要学习,没有网页兼容 |
data-* | 用于存储页面或应用程序的私有定制数据。 属性赋予我们在所有 HTML 元素上嵌入自定义 data 属性的能力 |
draggable | 规定元素是否可拖动。 |
dropzone | 规定在拖动被拖动数据时是否进行复制、移动或链接。 |
hidden | 规定元素仍未或不再相关。 |
spellcheck | 规定是否对元素进行拼写和语法检查。 |
translate | 规定是否应该翻译元素内容。 |
contenteditable
主要是用来替代textarea标签的,textarea标签在dom操作会有问题,为了兼容性问题考虑,我们会做两遍取值,很繁琐,样式的调整也不方便。
$('textarea').value = '333'
$('textarea').innerText = '333'
有了contenteditable这个属性,获取内容和编辑内容用起来很舒适
<section title="提示补充描述文本" id="" class="content" contenteditable>
我是一个section区块
</section>
$('[contenteditable]').innerText = '333'
data-*
Html5规范中规定自定义属性需要添加前缀data-,目的是提供与渲染无关的信息。
dataset是可读可写的属性,使用”.”来获取和写入属性,需要去掉data-前缀
属性名不应该包含任何大写字母,并且在前缀 “data-” 之后必须有至少一个字符
属性值可以是任意字符串
应用:对输入数据进行正则验证
<input hidden type="text" placeholder="请输入6位数字" data-io="IO" data-reg="^\d{6}$">
input.onkeypress = function (e) {
console.log(this.dataset)//所有data-*的数据
let reg = this.dataset.reg;
reg = new RegExp(reg);
console.log(reg.test(this.value))
}
应用:通过data-*把热度附加在伪元素中,好处是复制列表不会把热度复制进去
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>H5标签全局属性</title>
<style>
* {
margin: 0;
padding: 0;
}
ul>li:after {
content: attr(data-hot)
}
</style>
</head>
<body>
<ul></ul>
<script>
let list = document.querySelector('ul');
let hotNews = [{
content: "海牙老师今天长胖了.5克",
hot: 1777
},
{
content: "某国大豆出口额降低33%",
hot: 1293
},
{
content: "掘金胜快船 104:89",
hot: 1023
},
{
content: "外交部回应菅义伟当选日本首相",
hot: 8722
}
];
let fragment = document.createDocumentFragment();
for (let i = 0, len = hotNews.length; i < len; i++) {
let li = document.createElement('li');
li.innerText = hotNews[i]?.content; //hotNews[i]?undefined:hotNews[i].content
li.dataset.hot = hotNews[i]?.hot;
fragment.appendChild(li);
}
list.appendChild(fragment);
</script>
</body>
</html>
应用:查看人物属性
存储的(自定义)数据能够被页面的 JavaScript 中利用,以创建更好的用户体验(不进行 Ajax 调用或服务器端数据库查询
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>H5标签全局属性</title>
<style>
* {
margin: 0;
padding: 0;
}
ul>li:after {
content: attr(data-hot)
}
</style>
</head>
<body>
<h1>角色</h1>
<p>点击某个角色来查看其属性:</p>
<ul>
<li onclick="showDetails(this)" id="fire" data-type="火">迪卢克</li>
<li onclick="showDetails(this)" id="thunder" data-type="雷">菲谢尔</li>
<li onclick="showDetails(this)" id="water" data-type="水">芭芭拉</li>
</ul>
<script>
function showDetails(element) {
var elementType = element.getAttribute("data-type");
alert(element.innerHTML + "是" + elementType + "属性角色");
}
</script>
</body>
</html>
字符需要转化为驼峰命名
<div id="content" data-user-list="user_list">data-user_list自定义属性 </div>
var content= document.getElementById('content');
alert(content.dataset.userList)
hidden
hidden属性允许我们隐藏HTML页面中的元素。它以一种非常简单的方式工作:它将CSS的display属性设置为none
作用及使用场景:
1隐藏域在页面中对于用户是不可见的,在表单中插入隐藏域的目的在于收集或发送信息,以利于被处理表单的程序所使用。浏览者单击发送按钮发送表单的时候,隐藏域的信息也被一起发送到服务器。
2 有些时候我们要给用户一信息,让他在提交表单时提交上来以确定用户身份,如sessionkey,等等.当然这些东西也能用cookie实现,但使用隐藏域就简单的多了.而且不会有浏览器不支持,用户禁用cookie的烦恼。
3 有些时候一个form里有多个提交按钮,怎样使程序能够分清楚到底用户是按那一个按钮提交上来的呢?我们就可以写一个隐藏域,然后在每一个按钮处加上οnclick="document.form.command.value=“xx”"然后我们接到数据后先检查command的值就会知道用户是按的那个按钮提交上来的。
4 有时候一个网页中有多个form,我们知道多个form是不能同时提交的,但有时这些form确实相互作用,我们就可以在form中添加隐藏域来使它们联系起来。
5 javascript不支持全局变量,但有时我们必须用全局变量,我们就可以把值先存在隐藏域里,它的值就不会丢失了。
6 还有个例子,比如按一个按钮弹出四个小窗口,当点击其中的一个小窗口时其他三个自动关闭.可是IE不支持小窗口相互调用,所以只有在父窗口写个隐藏域,当小窗口看到那个隐藏域的值是close时就自己关掉。
HTML5中新增的功能标签
mark
突出显示部分文本:
<p>我是一个小红帽, 我非常<mark>可爱</mark> .</p>
我是一个小红帽, 我非常可爱 .
meter
使用 meter 元素来度量给定范围(gauge)内的数据:
<meter value="3" min="0" max="10">十分之三</meter>
<meter value="0.6">60%</meter>
属性 | 值 | 描述 |
---|---|---|
form | form_id | 规定 元素所属的一个或多个表单。 |
high | number | 规定被视作高的值的范围。 |
low | number | 规定被视作低的值的范围。 |
max | number | 规定范围的最大值。 |
min | number | 规定范围的最小值。 |
optimum | number | 规定度量的优化值。 |
value | number | 必需。规定度量的当前值。 |
output
显示输出结果(无法编辑)。
<output>$100</output>
dataList
定义下拉列表 datalist 元素规定输入域的选项列表。列表是通过 datalist 内的 option 元素创建的。如需把 datalist 绑定到输入域,请用输入域的 list 属性引用 datalist 的 id:
网址: <input type="url" list="url_list" name="link" >
<datalist id="url_list">
<option label="Baidu" value="http://www.baidu.com" >
<option label="Google" value="http://www.google.com" >
<option label="Microsoft" value="http://www.microsoft.com" >
</datalist>
progress
进度条
<progress value="22" max="100"></progress>
属性 | 值 | 描述 |
---|---|---|
max | number | 规定任务一共需要多少工作。 |
value | number | 规定已经完成多少任务。 |
自定义进度条移入分级案例
鼠标移入,进度条跟随鼠标,并分级显示颜色
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义滚动条</title>
<style>
.progress {
overflow: hidden;
display: flex;
margin: 40px;
width: 200px;
height: 8px;
border-radius: 4px;
box-shadow: 0 0 3px #333;
}
.progress .progress-unit {
width: 50%;
height: 100%;
background-color: orange;
}
</style>
</head>
<body>
<div class="progress">
<span class="progress-unit"></span>
</div>
<script>
let progress = document.querySelector('.progress');
let progressUnit = progress.querySelector('.progress-unit')
const COLORS = ['green', 'orange', 'red']
document.addEventListener('mousemove', function (e) {
let ex = e.clientX, ey = e.clientY;
let { left: ox, top: oy } = getPosition(progress);
let ratio = (ex - ox) / progress.offsetWidth;
console.log(ratio); //1 10/3 3 3 .6 * 3 1.8
progressUnit.style.width = `${ratio * 100}%`;
progressUnit.style.backgroundColor = COLORS[~~(ratio * (3))];
})
function getPosition (element) {
var pos = {
left: 0,
top: 0
}
while (element.offsetParent) {
pos.left += element.offsetLeft;
pos.top += element.offsetTop;
element = element.offsetParent;
}
return pos;
}
</script>
</body>
</html>
ruby&rt&rp
标签定义字符(中文注音或字符)的解释或发音。
ruby 注释是中文注音或字符。
在东亚使用,显示的是东亚字符的发音。
与 以及
ruby 元素由一个或多个字符(需要一个解释/发音)和一个提供该信息的 rt 元素组成,还包括可选的 rp 元素,定义当浏览器不支持 “ruby” 元素时显示的内容。
<ruby>汉<rt>Hàn</rt><rp>han</rp></ruby>
汉
details&summary
details 标签用于描述文档或文档某个部分的细节。 summary 标签包含 details 元素的标题,“details” 元素用于描述有关文档或文档片段的详细信息。
<details>
<summary>HTML 5</summary>
HTML5是一个非常好的版本
</details>
time
time 标签定义公历的时间(24 小时制)或日期,时间和时区偏移是可选的。
该元素能够以机器可读的方式对日期和时间进行编码,这样,举例说,用户代理能够把生日提醒或排定的事件添加到用户日程表中,搜索引擎也能够生成更智能的搜索结果。
<p>我们在每天早上 <time>9:00</time> 开始营业。</p>
<p>我在 <time datetime="2008-02-14">情人节</time> 有个约会。</p>
我们在每天早上 开始营业。
我在 有个约会。
wbr
Word Break Opportunity (wbr) 规定在文本中的何处适合添加换行符。
如果单词太长,或者您担心浏览器会在错误的位置换行,那么您可以使用
元素来添加 Word Break Opportunity(单词换行时机)。
<p>如果想学习 AJAX,那么您必须熟悉 XML<wbr>Http<wbr>Request 对象。</p>
如果想学习 AJAX,那么您必须熟悉 XML
表单新增
表单的输入类型
Input 类型: color
color 类型用在input字段主要用于选取颜色,如下所示:
Input 类型: date
date 类型允许你从一个日期选择器选择一个日期。
Input 类型: datetime
datetime 类型允许你选择一个日期(UTC 时间)。
Input 类型: datetime-local
datetime-local 类型允许你选择一个日期和时间 (无时区).
Input 类型: email
email 类型用于应该包含 e-mail 地址的输入域。
Input 类型: month
month 类型允许你选择一个月份。
Input 类型: number
number 类型用于应该包含数值的输入域。
属性 | 描述 |
---|---|
disabled | 规定输入字段是禁用的 |
max | 规定允许的最大值 |
maxlength | 规定输入字段的最大字符长度 |
min | 规定允许的最小值 |
pattern | 规定用于验证输入字段的模式 |
readonly | 规定输入字段的值无法修改 |
required | 规定输入字段的值是必需的 |
size | 规定以字符数计的 元素的可见宽度 |
step | 规定输入字段的合法数字间隔 |
value | 规定输入字段的默认值 |
Input 类型: range
range 类型用于应该包含一定范围内数字值的输入域。
range 类型显示为滑动条。
请使用下面的属性来规定对数字类型的限定:
- max - 规定允许的最大值
- min - 规定允许的最小值
- step - 规定合法的数字间隔
- value - 规定默认值
Input 类型: search
search 类型用于搜索域,比如站点搜索或 Google 搜索。
Input 类型: tel
定义输入电话号码字段:
电话号码:
Input 类型: time
time 类型允许你选择一个时间。
Input 类型: url
url 类型用于应该包含 URL 地址的输入域。
在提交表单时,会自动验证 url 域的值。
Input 类型: week
week 类型允许你选择周和年。
HTML5表单新增的属性
placeholder
占位符
autofocus
获取焦点
multiple
文件上传多选或多个邮箱地址
autocomplete
自动完成,用于表单元素,也可用于表单自身
form
指定表单项属于哪个form,处理复杂表单时会需要
novalidate
关闭验证,可用于标签
required
验证条件,必填项
pattern
正则表达式 验证表单
<form action="" autocomplete="on">
autofocus定位文本框焦点:<input type="text" autofocus> <br>
placeholder设置文本框默认提示:<input type="text" placeholder="请输入***"><br>
email邮件类型自带验证和提示:<input type="email"> <br>
required属性设置非空特性:<input type="tel" required><br>
pattern设置验证规则:<input type="tel" name="tel" required pattern="^(\+86)?1[358]\d{5}$"><br>
multiple多文件选择:<input type="file" multiple><br>
<input type="submit" value="提交"/>
</form>
accept
input file 设置文件选择类型
accept 属性规定了可通过文件上传提交的服务器接受的文件类型,可以限制你选择相关的文件,如果限制多个,可以用逗号分割。
注意:accept 属性仅适用于
提示:请不要将该属性作为您的验证工具。应该在服务器上对文件上传进行验证。
每个唯一文件类型说明符可以采用下列形式之一:
- accept=“image/*” — 接受任何图片文件类型.
- accept=“audio/*” — 接受任何音频文件类型.
- accept=“video/*” — 接受任何音频视频文件类型.
- accept=“image/png” 或 accept=“.png” — 只接受 png 图片.
- accept=“image/png, image/jpeg” 或 accept=“.png, .jpg, .jpeg” — PNG/JPEG 文件.
- accept=“.doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document” — 接受任何 MS Doc 文件类型.
<form>
<input type="file" name="pic" id="pic" accept="image/gif, image/jpeg" />
</form>
HTML5新增DOM API
获取元素
document.getElementsByClassName ('class') 通过类名获取元素,以伪数组形式存在。
document.querySelector('selector') 通过CSS选择器获取元素,符合匹配条件的第1个元素。
document.querySelectorAll('selector') 通过CSS选择器获取元素,以伪数组形式存在。复制代码
类名操作
Node.classList.add('class') 添加class
Node.classList.remove('class') 移除class
Node.classList.toggle('class') 切换class,有则移除,无则添加
Node.classList.contains('class') 检测是否存在class复制代码
Node指一个有效的DOM节点,是一个通称。
自定义属性
在HTML5中我们可以自定义属性,其格式如下data-*=“”,例如
data-info="我是自定义属性"复制代码
通过Node.dataset[‘info’] 我们便可以获取到自定义的属性值。
Node.dataset是以对象形式存在的,当我们为同一个DOM节点指定了多个自定义属性时,Node.dataset则存储了所有的自定义属性的值。
假设某元素
var demo = document.querySelector(‘#demo’);
1、读取 demo.dataset[‘name’] 或者 demo.dataset[‘age’]
2、设置demo.dataset[‘name’] = ‘web developer’
注:当我们如下格式设置时,则需要以驼峰格式才能正确获取
这样获取Node.dataset['myName'] ### 追加节点after
ChildNode.after()
方法会在其父节点的子节点列表中插入一些Node
或DOMString
对象。插入位置为该节点之后。DOMString
对象会被以Text
的形式插入。
[Throws, Unscopable]
void ChildNode.after((Node or DOMString)... nodes);
let parent = document.createElement("div");
let child = document.createElement("p");
parent.appendChild(child);
let span = document.createElement("span");
child.after(span, "Text");
console.log(parent.outerHTML);
// "<div><p></p><span></span>Text</div>"
before
ChildNode
.before
方法可以在ChildNode这个节点的父节点中插入一些列的
Node
或者DOMString
对象,位置就是在ChildNode节点的前面,
DOMString
对象其实和Text
节点一样的方式来完成插入的。
[Throws, Unscopable]
void ChildNode.before((Node or DOMString)... nodes);
let parent = document.createElement("div");
let child = document.createElement("p");
parent.appendChild(child);
let span = document.createElement("span");
child.before(span);
console.log(parent.outerHTML);
// "<div><span></span><p></p></div>"
remove
**ChildNode.remove()**
方法,把对象从它所属的 DOM 树中删除。
node.remove();
let oBox = document.querySelector('.box');
oBox.remove();
replaceWith
**ChildNode**
.replaceWith()
的方法用一套Node
对象或者DOMString
对象,替换了该节点父节点下的子节点 。DOMString
对象被当做等效的Text
节点插入。
[Throws, Unscopable]
void ChildNode.replaceWith((Node or DOMString)... nodes);
let parent = document.createElement("div");
let child = document.createElement("p");
parent.appendChild(child);
let span = document.createElement("span");
child.replaceWith(span);
console.log(parent.outerHTML);
// "<div><span></span></div>"
append
ParentNode.append
方法在ParentNode
的最后一个子节点之后插入一组Node
对象或DOMString
对象。被插入的
DOMString
对象等价为Text
节点。
与 Node.appendChild() 的差异:
ParentNode.append()允许追加 DOMString 对象,而 Node.appendChild() 只接受 Node 对象。
ParentNode.append() 没有返回值,而 Node.appendChild() 返回追加的 Node 对象。
ParentNode.append() 可以追加多个节点和字符串,而 Node.appendChild() 只能追加一个节点。
[Throws, Unscopable]
void ParentNode.append((Node or DOMString)... nodes);
var parent = document.createElement("div");
var p = document.createElement("p");
parent.append("Some text", p);
console.log(parent.childNodes); // NodeList [ #text "Some text", <p> ]
prepend
ParentNode.prepend
方法可以在父节点的第一个子节点之前插入一系列Node
对象或者DOMString
对象。DOMString
会被当作Text
节点对待(也就是说插入的不是HTML代码)。
ParentNode.prepend((Node or DOMString)... nodes);
let parent = document.createElement("div");
let p = document.createElement("p");
let span = document.createElement("span");
parent.append(p);
parent.prepend(span);
console.log(parent.childNodes); // NodeList [ <span>, <p> ]
<body>
<ul class="list">
<li data-idx="1" class="active">1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let oUl = document.querySelector('.list');
let aLi = document.querySelectorAll('.list>li');
oUl.prepend(aLi[0], aLi[1]); // 1 2 3 4 5 //1 2 按顺序整体压进去
oUl.prepend(aLi[2]); // 3 1 2 4 5 //从1 2 前面压进去 放入已有的dom元素则相当于截切粘贴的操作
</script>
</body>
replaceChildren [firefox safari]
方法将一个
Node
的后代替换为指定的后代集合。这些新的后代可以为DOMString
或Node
对象。兼容性有大问题,不会用,只支持firefox safari,chrome不支持
ParentNode.replaceChildren(...nodesOrDOMStrings) // 返回 undefined
//清空一个节点
//replaceChildren()为清空一个节点的后代提供了非常方便的机制,您只需在父节点不指定任何实参调用该方法即可。
myNode.replaceChildren(); //清空myNode的后代
HTML5 拖拽drag drop拖放事件
拖放(Drag 和 drop)是 HTML5 标准的组成部分。
定义和用法
ondragstart 事件在用户开始拖动元素或选择的文本时触发。
注意: 为了让元素可拖动,需要使用 HTML5 draggable 属性。
提示: 链接和图片默认是可拖动的,不需要 draggable 属性,其他标签要想拖拽需要加上draggable ='true'
。
在拖放的过程中会触发以下事件:
-
在拖动目标上触发事件
- ondragstart - 用户开始拖动元素时触发
- ondrag - 元素正在拖动时触发
- ondragend - 用户完成元素拖动后触发
-
释放目标时触发的事件:
- ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件
- ondragover- 当某被拖动的对象在另一对象容器范围内拖动时触发此事件
- ondragleave - 当被鼠标拖动的对象离开其容器范围内时触发此事件
<body> <div class="wrap"> <p id="p" draggable="true">我是一个p标签</p> <img id="img" draggable="true" src="images/h1.jpg" width="500" height="340" alt="h1"> </div> <div class="other wrap" > </div> <script> let oWrap = document.querySelector('.wrap'); let other = document.querySelector('.other'); document.ondragleave = function (e) { console.log(e); console.log('离开了'); //只要离开容器就触发 } oWrap.ondragleave = function (e) { console.log(e); console.log('离开了 wrap');//只有离开oWrap容器才触发 } </script> </body>
- ondrop- 在一个拖动过程中,释放鼠标键时触发此事件
如果想触发ondrop事件,那么就必须在释放鼠标的位置阻止浏览器的默认行为
<body> <div class="wrap"> <p id="p" draggable="true">我是一个p标签</p> </div> <script> document.ondragover = function (e) { /*如果想触发ondrop事件,那么就必须在这个位置阻止浏览器的默认行为*/ e.preventDefault(); } document.ondrop = function (e) { console.log('松开鼠标了') } </script> </body>
拖拽案例
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>drag drop H5 拖拽放置</title>
<style>
* {
margin: 0;
padding: 0;
}
img {
vertical-align: middle;
width: 200px;
height: auto;
}
p {
height: 30px;
background-color: orange;
color: #fff;
font-size: 20px;
line-height: 24px;
}
.wrap {
float: left;
width: 400px;
height: 280px;
margin: 20px;
box-shadow: 0 0 4px #222;
}
</style>
</head>
<body>
<div class="wrap">
<p id="p" draggable="true">我是一个p标签</p>
<img id="img" draggable="true" src="images/h1.jpg" width="500" height="340" alt="h1">
</div>
<div class="other wrap" >
</div>
<script>
let oWrap = document.querySelector('.wrap');
let other = document.querySelector('.other');
let dragEle = null;
document.ondragstart = function (e) {
e.target.style.border = '4px solid red';
dragEle = e.target;
e.dataTransfer.setData("text/html",e.target.id);
}
document.ondragover = function (e) {
/*如果想触发ondrop事件,那么就必须在这个位置阻止浏览器的默认行为*/
e.preventDefault();
}
document.ondrop = function (e) {
/*添加元素*/
//e.target.appendChild(obj);
/*通过e.dataTransfer.setData存储的数据,只能在drop事件中获取*/
var id = e.dataTransfer.getData("text/html");
e.target.append(document.querySelector(`#${id}`))
}
</script>
</body>
</html>
video 视频
H5 插入 video 多媒体标签
<video controls width="600" preload loop muted poster poster="images/bird.png" src="video/mp4.mp4"></video>
属性
属性 | 值 | 描述 |
---|---|---|
autoplay | autoplay | 如果出现该属性,则视频在就绪后马上播放。 |
controls | controls | 如果出现该属性,则向用户显示控件,比如播放按钮。 |
height | pixels | 设置视频播放器的高度。 |
loop | loop | 如果出现该属性,则当媒介文件完成播放后再次开始播放。 |
muted | muted | 如果出现该属性,视频的音频输出为静音。 |
poster | URL | 规定视频正在下载时显示的图像,直到用户点击播放按钮。 |
preload | auto metadata none | 如果出现该属性,则视频在页面加载时进行加载,并预备播放。如果使用 “autoplay”,则忽略该属性。 |
src | URL | 要播放的视频的 URL。 |
width | pixels | 设置视频播放器的宽度。 |
playsinline/webkit-playsinline | playsinline/webkit-playsinline | Iphone ios/ 微信浏览器支持小窗内播放 |
volume | double | 表示音频的音量。值从0.0(静音)到1.0(最大音量)。 |
paused | boolean | 是否已暂停,true:暂停 false:播放中 |
muted | boolean | 设置或返回音频/视频是否静音,true:静音 false:不静音 |
playbackRate | double | 音频/视频的当前播放速度。 1.0 正常速度 0.5 半速(更慢) 2.0 倍速(更快) |
duration | number | 数字值,表示音频/视频的长度,以秒计。 |
readyState | boolean | 返回视频是否就绪,视频准备阶段 开始加载0 加载完成4 |
currentTime | number | 获取当前到的播放时长 s |
方法
方法 | 描述 |
---|---|
addTextTrack() | 向音频/视频添加新的文本轨道 |
canPlayType() | 检测浏览器是否能播放指定的音频/视频类型 |
load() | 重新加载音频/视频元素 |
play() | 开始播放音频/视频 |
pause() | 暂停当前播放的音频/视频 |
事件
video元素可以触发以下事件
事件名称 | 描述 |
---|---|
abort | 在播放被终止时触发,例如, 当播放中的视频重新开始播放时会触发这个事件。 播放中断或src更改时都会触发 |
canplay | 在媒体数据已经有足够的数据(至少播放数帧)可供播放时触发。这个事件对应CAN_PLAY的readyState。 |
canplaythrough | 在媒体的readyState变为CAN_PLAY_THROUGH时触发,表明媒体可以在保持当前的下载速度的情况下不被中断地播放完毕。注意:手动设置currentTime会使得firefox触发一次canplaythrough事件,其他浏览器或许不会如此。 |
durationchange | 元信息已载入或已改变,表明媒体的长度发生了改变。例如,在媒体已被加载足够的长度从而得知总长度时会触发这个事件。 |
emptied | 媒体被清空(初始化)时触发。即video的src被清空或更改时触发,同时也会触发abort 事件 |
ended | 播放结束时触发。 |
error | 在发生错误时触发。元素的error属性会包含更多信息。参阅 HTMLMediaElement.error 获得详细信息。 |
interruptbegin | 声音在Firefox OS设备中断时触发,可能是应用程序被切换至后台或者更高优先级的应用占用了音频通道。 相关信息请参考 Using the AudioChannels API |
interruptend | 声音在Firefox OS设备中断后恢复播放时触发,应用程序被切换至前台或占用更高级音频通道的应用程序播放完毕后触发。相关信息请参考 Using the AudioChannels API |
loadedmetadata | 媒体的元数据已经加载完毕,现在所有的属性包含了它们应有的有效信息。 |
loadstart | 在媒体开始加载时触发。 |
mozaudioavailable | 当音频数据缓存并交给音频层处理时 |
pause | 播放暂停时触发。 |
play | 在媒体回放被暂停后再次开始时触发。即,在一次暂停事件后恢复媒体回放。 |
playing | 在媒体开始播放时触发(不论是初次播放、在暂停后恢复、或是在结束后重新开始)。 |
progress | 告知媒体相关部分的下载进度时周期性地触发。有关媒体当前已下载总计的信息可以在元素的buffered属性中获取到。 |
ratechange | 在回放速率变化时触发。 |
seeked | 在跳跃操作完成时触发。 |
seeking | 在跳跃操作开始时触发。 |
stalled | 在尝试获取媒体数据,但数据不可用时触发。 |
suspend | 在媒体资源加载终止时触发,这可能是因为下载已完成或因为其他原因暂停。 |
timeupdate | 元素的currentTime属性表示的时间已经改变。持续监听的事件,只要播放中就会触发,因为currentTime时一直在变的 |
volumechange | 在音频音量改变时触发(既可以是volume属性改变,也可以是muted属性改变)。 主要做声音调特别大的时候,会提示”音量过大可能会伤害耳朵“ |
waiting | 在一个待执行的操作(如回放)因等待另一个操作(如跳跃或下载)被延迟时触发。 |
<!-- Simple video example -->
<video src="videofile.ogg" autoplay poster="posterimage.jpg">
抱歉,您的浏览器不支持内嵌视频,不过不用担心,你可以 <a href="videofile.ogg">下载</a>
并用你喜欢的播放器观看!
</video>
<!-- Video with subtitles -->
<video src="foo.ogg">
<track kind="subtitles" src="foo.en.vtt" srclang="en" label="English">
<track kind="subtitles" src="foo.sv.vtt" srclang="sv" label="Svenska">
</video>
浏览器并不是都支持相同的视频格式,所以你可以在 `` 元素里提供多个视频源,然后浏览器将会使用它所支持的第一个源。
<video controls>
<source src="myVideo.mp4" type="video/mp4">
<source src="myVideo.webm" type="video/webm">
<p>你的浏览器不支持H5 video播放器 你可以点击链接下载视频 <!-- ie9以上都支持 -->
<a href="myVideo.mp4">点击下载</a> </p>
</video>
自定video
- 播放暂停切换
- 播放进度条反馈和控制 (拖拽 点击) 播放时间 00:00/05:30
- 音量调控 反馈 一键静音/取消静音
- 全屏播放 循环模式
- 播放状态反馈 未播放 已播放 播放完毕
- 本地存储播放时间 自动续播
准备
[
{
方法:[ play(),pause()],
属性: [paused],
element:[#contr-play]
},{
方法:[ mousedown,pause,mousemove,mouseup,play],
属性: [currentTime],
element:[player-progress,progress-line,progress-unit,player-time,player-current]
},{
方法:[ mousedown,mousemove,mouseup],
属性: [volume,muted],
element:[]
},{
}
]
* video设置currentTime的时候 会触发 canplay事件
* 拖拽进度条之后 播放视频
点击进度条某个位置 直接开始跳转播放
click {
mousedown
mouseup
}
实现
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>video 播放器</title>
<style>
@font-face {
font-family: "iconfont";
src: url('fonts/iconfont.eot?t=1600426546624');
/* IE9 */
src: url('fonts/iconfont.eot?t=1600426546624#iefix') format('embedded-opentype'),
/* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAS4AAsAAAAACZwAAARrAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDSAqGEIUYATYCJAMYCw4ABCAFhG0HWBtOCFGULspqsq+wKWO+GKJwXogXwkCjQQ+Evn05o/Tmdn/CWtgG7CKF5wDBE+3X3uzO7ldJh0qDrg1PXronIomYrETRBCXeT3b2b3lEpbkvLFIehM2H9pLZzSaenZwqqfxQaVQqGrOyuvDwtnv/7k9So5BJKoGZTRUuRKGkK0x2QTiRPI44CCLOEosr0+0G0l/4RJO7B8EAIYJA47YGzrlqkEjLIJhA8XigAzpJpmAVUP1xr+qf5x8s61myQaI496BcKFwuUIHGXmQtSLmCWgewTbzfuh4CYqPWYKb61l6Ip/TdBMhpmTrEJ+y0ghKhyRccm8gHgrDcszcAePB+Xn5wJB4MV9FvdLjVuaGyp3ePG401m+oojA7nBdiTwIJlgELeQv8FLsgv48TyX+BtA1IhQ/tqFrOS69292J+EWg+L1PiXB46lDIEMQJ9S5K7OHaDCkkeGYlixMFTCisJwDGwY/N9TXpbZD9gzAPwBiGwk6QcOfgcvpWOqmuni+nDbJCffPri99vX5c4JDgObuNOtT9Qobn1udt1pvXyqbSOFmnLnrUp1xT05in3ORNC6+nT8KbDu2cILh7o5rl+7K1J35e9b7WiHOI4LWDRwQhtIW7srqIIzWBM4jQrddyno3JkG8WNmroq/UENHQrkXGZmh3yKq0M7oO1tmbuCt311QH7T+SszNH3zmavW3H1gfaO3fpCqqAIVBbk3Rl6+lFYfQpjxD/1LCiaKujjpQD+89yHz9y0JxlpFBTnnAd1a2FWEvuByU7/O7KqNeXCjfdL1cSSuJRmNo0Bpn8r4CxZCUHHcjbCN6cUle4Ls4hLHIutpit6Y6v6lp58hMru1btw3RqwOWJcnTx2zmdm0nVEhLUh2y1sY2ZRsiMJ91hePo8SkrtC2z+X28ZH25J8x/lc11WCMukL2WvGeIKj5MKnvzeVZWYhp/oVg54db9lpL7krXtXOz+MYaIn3rxmf0cKGU2aLN3uOQqu3RZ9sPmavnmV/5xqhowU6qgjOVx+IowrAqhXhNR4GWQA/BR+4datAgD7sL8Y/reDi/+X/Q6P6ylpaaV/QlEAPuYvOLy+4WiCbQB/FQONFf8pRcM5VVS6w6c02xpCgLf/sbIGxMYS/JPCtVdDBTsQeUXIKQBGYASs0DSq5DI4iVXwQmsQW9JwcqJBcCQjYNEUAaJ0BozcM1ilm6iSL8HpfQGvDAexo8g6Z2I2VmINwTrFbtQ/jCxXKGiYLprot3ZjM+rXSVzl0TRMbCuFVElJlxpwEJMhDrAHTAelBjJIKIDqiZthvz+EwiTkxS4qeSgNF8uyUfRFkisUgERbCExHYW5Iv2GIxSUkyHBWphIz398NM0X56UjFTFlUgxE2a+2ISqI0QBv0wUYzl/IS2wCTA0UZEAMREoDUE17Mb+RDkHDxVl6YCyXxdMiGFZPJRUZTvTS9MXCXVwEx/aN8GLGi4sRLgLv1qI3jhqJBT1QPxg9bQb+lB834SGLzsBU0xU6/PowJAAA=') format('woff2'),
url('fonts/iconfont.woff?t=1600426546624') format('woff'),
url('fonts/iconfont.ttf?t=1600426546624') format('truetype'),
/* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('fonts/iconfont.svg?t=1600426546624#iconfont') format('svg');
/* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 32px;
color: #ccc;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
cursor: pointer;
}
.icon-pause:before {
content: "\e7d2";
}
.icon-loop:before {
content: "\e6a9";
}
.icon-volume:before {
content: "\e63a";
}
.icon-fullscreen:before {
content: "\e640";
}
.icon-player:before {
content: "\e62b";
}
div {
box-sizing: border-box;
}
video {
display: block;
width: 100%;
max-height: 100%;
}
.player {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
width: 638px;
height: 493px;
margin: 100px auto;
background-color: #000;
}
.player .hint {
position: absolute;
top: 0;
width: 100%;
height: 30px;
color: #fff;
text-align: center;
line-height: 30px;
font-size: 20px;
letter-spacing: .2em;
}
.player-screen {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
justify-content: center;
}
.player-controls {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
position: absolute;
bottom: 0;
width: 100%;
height: 60px;
}
.player-progress {
position: relative;
width: 100%;
height: 4px;
background-color: #ccc;
}
.player-progress:hover .progress-unit {
display: block;
}
.player-progress .progress-line {
position: relative;
width: 0;
height: 4px;
background-color: rgb(18, 177, 226);
}
.player-progress .progress-unit {
display: none;
position: absolute;
right: -18px;
top: 0;
bottom: 0;
margin: auto;
width: 18px;
height: 18px;
border-radius: 50%;
background-color: rgb(18, 177, 226);
cursor: pointer;
}
.player-time {
position: absolute;
right: 0;
top: -35px;
color: #fff;
}
.player-controls .player-operator {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
padding: 4px 20px;
}
.operator-right {
display: flex;
align-items: center;
}
.operator-right i {
padding: 0 6px;
}
.is-loop {
color: pink;
}
.is-muted {
color: red;
}
.volume-range {
width: 100px;
height: 5px;
}
</style>
</head>
<body>
<div id="my-player" class="player">
<p class="hint">暂未播放影片</p>
<div class="player-screen">
<video src="video/mp4.mp4">
<source src="video/mp4.mp4" type="video/mp4">
</video>
</div>
<div class="player-controls">
<div class="player-progress">
<p class="player-time"><span class="player-current">00:00:00</span> / <span class="player-count">00:00:00</span>
</p>
<div class="progress-line">
<div class="progress-unit"></div>
</div>
</div>
<div class="player-operator">
<i id="contr-play" class="iconfont icon-player icon-pause"></i>
<div class="operator-right">
<input type="range" value="0.1" id="volume-range" class="volume-range">
<i id="contr-volume" class=" iconfont icon-volume"></i>
<i id="contr-loop" class=" iconfont icon-loop"></i>
<i id="contr-full" class=" iconfont icon-fullscreen"></i>
</div>
</div>
</div>
</div>
<script src="js/common.js"></script>
<script src="js/Storage.js"></script>
<script>
class MyVideo {
static progressMaxWidth = $('.player-progress').offsetWidth;
constructor(player = $('video')) {
this.player = player;
this.canPlay = false; //视频可播放状态 视频加载到可以播放状态的时候 canPlay 为true 默认false
this.storage = new Storage('local');
this.$ele = {
$playBtn: $('#contr-play'), //播放|暂停按钮
$progress: $('.progress-line'), //进度条
$progressUnit: $('.progress-unit'), //进度条按钮
$currentTime: $('.player-current'), //当前播放时长
$countTime: $('.player-count'), //总时长
$fullScreen: $('#contr-full'),//全屏按钮
$loop: $('#contr-loop'), //切换循环loop状态
$volumeBtn: $('#contr-volume'),
$volumeRange: $('#volume-range'),
}
//进度条数据集
this.progress = {
isDown: false, //是否按下鼠标
startX: 0, //起始鼠标x方向位置
width: 0, //起始进度条宽度
};
//事件总控制
this.eventListener();
}
//设置触发 左查询 写入
set isPaused(val) {
this._paused = val;
if (val) {
this.myPlay();
return false;
}
this.myPause();
}
//获取触发 右查询 读取
get isPaused() {
return this._paused; //return的值会作为 isPaused属性的值
}
set volume(val) {
this._volume = val;
this.player.volume = val;
this.$ele.$volumeRange.value = val * 100;
}
get volume() {
return this._volume;
}
//初始化
initPlayer() {
if (this.canPlay) {
return false;
}
this.volume = 0.1;
this.player.volume = this.volume; //初始化音量
this.canPlay = true; //初始化可播放状态
this.checkPlayHistory();//检查本地存储该视频是否有播放时间戳 如果存在设置播放时间戳跳转
this.setCountTimeText()//初始化总播放时长
this.setCurrentText(); //初始化当前播放时长
}
checkPlayHistory() {
let current = ~~this.storage.getStorage(this.player.src);
if (!current) {
return false;
}
this.setCurrentTime(null, current);
}
eventListener() {
//事件分发映射表
const dragMap = {
'mousedown': (e) => {
const target = e.target;
//按下鼠标的时候 判断触发元素是否是progress-unit
if (e.target.className === 'progress-unit') {
//拖拽流程
if (this.progress.isDown) {
return false;
}
this.progress.startX = e.clientX;
this.progress.isDown = true;
this.progress.width = this.$ele.$progress.offsetWidth;
this.isPaused = false;
return false; //终止后续执行
}
//类名中是否包含progress 判断是否点击在 进度条上 => 进度跳转
if (/progress/g.test(e.target.className)) {
let _x = e.clientX - getPosition($('.player-progress')).left;
let ratio = _x / MyVideo.progressMaxWidth;
let time = ratio * this.player.duration;
this.setCurrentTime(null, time);
this.isPaused = true;
}
},
'mousemove': (e) => {
if (!this.progress.isDown) {
//如果没有开启拖拽
return false;
}
//_x 鼠标拖拽x方向位移增量
let _x = e.clientX - this.progress.startX;
//width 当前进度条宽度 = 增量+初始宽度
let width = _x + this.progress.width;
//ratio 比例 = 当前宽度/总宽度
let ratio = width / MyVideo.progressMaxWidth;
//time 当前播放市场 = 比例 * 总时长
let time = ratio * this.player.duration;
this.setCurrentTime(null, time);
},
'mouseup': (e) => {
if (!this.progress.isDown) {
//如果没有开启拖拽
return false;
}
this.isPaused = true; //set isPaused
this.progress.isDown = false;
}
}
//事件控制器 监听
this.$ele.$playBtn.addEventListener('click', () => {
//获取暂停当前状态 isPaused true暂停 false播放
this.isPaused = this.player.paused;
//监听播放/暂停按钮的点击
if (!this.canPlay) {
//判断是否可以播放 默认true
return false;
}
}, false);
//监听可播放状态 调用 initPlayer
this.player.addEventListener('canplay', this.initPlayer.bind(this));
//播放时间更新触发
this.player.addEventListener('timeupdate', this.setCurrentTime.bind(this));
//监听全屏切换按钮
this.$ele.$fullScreen.addEventListener('click', this.changeFullScreen.bind(this));
this.$ele.$loop.addEventListener('click', this.changeLoop.bind(this));
this.$ele.$volumeRange.addEventListener('change', this.changeVolume.bind(this), false);
this.$ele.$volumeBtn.addEventListener('click', this.chageMuted.bind(this), false);
//当页面完全不可见的时候 存储视频播放信息
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.isPaused = false; //暂停视频
this.setVideoInfo();
}
});
//事件分发
const drag = (e) => {
let type = e.type;
if (dragMap.hasOwnProperty(type)) {
dragMap[type](e);
}
}
//监听进度条的拖拽组
document.addEventListener('mousedown', drag, false);
document.addEventListener('mousemove', drag, false);
document.addEventListener('mouseup', drag, false);
}
//设置进度条
setProgress(done) {
//进度条比例 .progress-line 的宽度 = 当前播放(秒)/视频总时长(秒) * 100 + '%'
let ratio = this.player.currentTime / this.player.duration * 100;
if (done) {
this.progress.width = ratio * MyVideo.progressMaxWidth;
return false;
}
//设置进度条DOM的宽度样式
this.$ele.$progress.style.width = ratio + '%';
}
//设置当前播放时间反馈
setCurrentTime(e, time) {
if (time) {
this.player.currentTime = time;
}
//设置进度条
this.setProgress();
//设置当前播放时间text
this.setCurrentText();
}
//设置当前播放时间文本
setCurrentText() {
this.$ele.$currentTime.innerText = this.getCurrentTime();
}
//设置总时长文本
setCountTimeText() {
this.$ele.$countTime.innerText = this.getCountTime();
}
//获取总时间 s=> h:m:s
getCountTime() {
//duration 视频总时长
return MyVideo.fromatTime(this.player.duration);
}
//获取当前播放时长 s=> h:m:s
getCurrentTime() {
return MyVideo.fromatTime(this.player.currentTime);
}
//静态方法 内部的工具方法
static fromatTime(time) {
let [s, m, h] = [~~(time % 60), ~~(time / 60), ~~(time / 3600)];
[s, m, h] = [s, m, h].map(padLeft);
return `${h}:${m}:${s}`;
}
//播放动作
myPlay() {
this.$ele.$playBtn.classList.remove('icon-player');
this.player.play();
}
//暂停动作
myPause() {
this.$ele.$playBtn.classList.add('icon-player');
this.player.pause();
}
//全屏切换
changeFullScreen() {
if (!isFullScreen()) {
openFullScreen(this.player);
return false;
}
closeFullScreen();
}
//切换循环状态
changeLoop(e) {
this.player.loop = !this.player.loop;
e.target.classList.toggle('is-loop');
}
//音量调整
changeVolume(e) {
let ratio = Number(e.target.value) / 100;
this.volume = ratio;
}
chageMuted(e) {
this.player.muted = !this.player.muted;
e.target.classList.toggle('is-muted');
}
//存储观看信息 到本地存储
setVideoInfo() {
//视频src=>ID 播放时间
if (!this.canPlay) {
//视频准备好了播放的情况下才存储
return false;
}
this.storage.setStorage({
[this.player.src]: this.player.currentTime
})
}
}
function padLeft(num) {
return String(num)[1] && String(num) || '0' + num;
}
const myPlay = new MyVideo();
</script>
</body>
</html>
audio 音频
音频标签
<audio src="mp3/xxx.mp3">
您的浏览器不支持audio
</audio>
属性
audioTracks | 返回表示可用音频轨道的 AudioTrackList 对象。 |
---|---|
autoplay | 设置或返回是否在就绪(加载完成)后随即播放音频。 |
buffered | 返回表示音频已缓冲部分的 TimeRanges 对象。 |
controller | 返回表示音频当前媒体控制器的 MediaController 对象。 |
controls | 设置或返回音频是否应该显示控件(比如播放/暂停等)。 |
crossOrigin | 设置或返回音频的 CORS 设置。 |
currentSrc | 返回当前音频的 URL。 |
currentTime | 设置或返回音频中的当前播放位置(以秒计)。 |
defaultMuted | 设置或返回音频默认是否静音。 |
defaultPlaybackRate | 设置或返回音频的默认播放速度。 |
duration | 返回音频的长度(以秒计)。 |
ended | 返回音频的播放是否已结束。 |
error | 返回表示音频错误状态的 MediaError 对象。 |
loop | 设置或返回音频是否应在结束时再次播放。 |
mediaGroup | 设置或返回音频所属媒介组合的名称。 |
muted | 设置或返回是否关闭声音。 |
networkState | 返回音频的当前网络状态。 |
paused | 设置或返回音频是否暂停。 |
playbackRate | 设置或返回音频播放的速度。 |
played | 返回表示音频已播放部分的 TimeRanges 对象。 |
preload | 设置或返回音频的 preload 属性的值。 |
readyState | 返回音频当前的就绪状态。 |
seekable | 返回表示音频可寻址部分的 TimeRanges 对象。 |
seeking | 返回用户当前是否正在音频中进行查找。 |
src | 设置或返回音频的 src 属性的值。 |
textTracks | 返回表示可用文本轨道的 TextTrackList 对象。 |
volume | 设置或返回音频的音量。 |
方法
方法 | 描述 |
---|---|
addTextTrack() | 向音频添加新的文本轨道。 |
canPlayType() | 检查浏览器是否能够播放指定的音频类型。 |
load() | 重新加载音频元素。 |
play() | 开始播放音频。 |
pause() | 暂停当前播放的音频。 |
事件
方法 | 描述 |
---|---|
addTextTrack() | 向音频/视频添加新的文本轨道。 |
canPlayType() | 检测浏览器是否能播放指定的音频/视频类型。 |
load() | 重新加载音频/视频元素。 |
play() | 开始播放音频/视频。 |
pause() | 暂停当前播放的音频/视频。 |
fullScreen全屏
通过fullScreen方法接口包括 element全屏 退出全屏 判断全屏状态
fullscreenElement
当前是否为全屏状态
document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement
fullscreenEnabled
全屏功能是否可用
document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled ||document.msFullscreenEnabled
requestFullScreen()
开启全屏显示
可以使用局部具体ELement全屏
element.requestFullscreen(); //默认标准
element.webkitRequestFullScreen(); //Safari chrome Edge
element.mozRequestFullScreen(); //Firefox
element.msRequestFullscreen(); //IE
exitFullscreen()
退出全屏显示,退出全屏需要document处理
document.exitFullscreen(); //默认标准
document.webkitCancelFullScreen(); //Safari chrome
document.mozCancelFullScreen(); //Firefox
document.msExitFullscreen(); //IE
document.webkitExitFullscreen();//Safari chrome
全屏兼容
// 开启全屏
function openFullScreen (ele = document) {
const requestFullScreens = ['requestFullscreen', 'webkitRequestFullScreen', 'mozRequestFullScreen', 'msRequestFullscreen'];
for (let i = 0; i < requestFullScreens.length; i++) {
let key = requestFullScreens[i];
if (ele[key]) {
ele[key]();
return false;
}
}
}
// 退出全屏
function closeFullScreen () {
const exitFullScreens = ['exitFullscreen', 'webkitCancelFullScreen', 'mozCancelFullScreen', 'msExitFullscreen'];
for (let i = 0; i < exitFullScreens.length; i++) {
let key = exitFullScreens[i];
if (document[key]) {
document[key]();
return false;
}
}
}
// 是否是全屏状态
function isFullScreen () {
return !!(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)
}
全屏案例
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>fullScreen 全屏API</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div class="wrap">
<img src="images/m2.jpg" alt="m2">
<input id="open" type="button" value="开启全屏">
<input id="close" type="button" value="取消全屏">
<input id="is-full" type="button" value="是否全屏?">
</div>
<script>
const oWrap = document.querySelector('.wrap');
const openBtn = document.querySelector('#open');
const closeBtn = document.querySelector('#close');
const isFullBtn = document.querySelector('#is-full');
const requestFullScreens = ['requestFullscreen', 'webkitRequestFullScreen', 'mozRequestFullScreen', 'msRequestFullscreen'];
const exitFullScreens = ['exitFullscreen', 'webkitCancelFullScreen', 'mozCancelFullScreen', 'msExitFullscreen'];
openBtn.onclick = function () {
for (let key of requestFullScreens) {
if (oWrap[key]) {
oWrap[key]();
return false;
}
}
};
isFullBtn.onclick = function () {
console.log(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)
}
closeBtn.onclick = function () {
for (let key of exitFullScreens) {
if (document[key]) {
document[key]();
return false;
}
}
};
</script>
</body>
</html>
FileReader
FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用
File
或Blob
对象指定要读取的文件或数据。前端开发场景较少用到,一般用于图片的预览,流媒体,视频
其中File对象可以是来自用户在一个<input>元素上选择文件后返回的FileList对象,也可以来自拖放操作生成的 DataTransfer对象,还可以是来自在一个HTMLCanvasElement上执行mozGetAsFile()方法后返回结果。
重要提示: FileReader仅用于以安全的方式从用户(远程)系统读取文件内容 它不能用于从文件系统中按路径名简单地读取文件。 要在JavaScript中按路径名读取文件,应使用标准Ajax解决方案进行服务器端文件读取,如果读取跨域,则使用CORS权限。
属性
-
FileReader.error
只读一个
DOMException
,表示在读取文件时发生的错误 。 -
FileReader.readyState
只读表示
FileReader
状态的数字。取值如下:常量名值描述EMPTY``0
还没有加载任何数据.LOADING``1
数据正在被加载.DONE``2
已完成全部的读取请求. -
FileReader.result
只读存放文件转换的内容,当文件读取完成的时候结果会作为result属性的值。
该属性仅在读取操作完成后才有效,数据的格式取决于使用哪个方法来启动读取操作。
function createFileReader(file) { let fileReader = new FileReader();//通过FileReader的result可以拿到文件的url console.log(fileReader.readyState)//0 还没有加载任何数据 fileReader.readAsDataURL(file);//把文件转换成base64,存入fileReader中的result console.log(fileReader.result) //打印空,因为FileReader()是异步的加载,读取文件需要时间 console.log(fileReader.readyState)//1 数据正在被加载 //所以需要给fileReader加onload,读取成功的时候触发 fileReader.onload = function () { console.log(fileReader.readyState)//2 已完成全部的读取请求 console.log(fileReader.result) //注:FileReader.result拿到的是文件的base64码 //base64是新的文件协议格式,传输8bit字节码的编码方式,借助64个可以表示字符[A,B,C...1,2,3] 转换二进制 //base64 字符串通用性很强 不依赖平台 不依赖环境 可持续 ,可以以字符串的形式传输图片、文档等 //可以很方便的用来展示图片,下载文档 } }
如果直接
input.files[0]
,是拿不到当前上传的文件的url,如document.querySelector('input[type="file"]').files[0]
事件处理
FileReader.onabort
处理abort
事件。该事件在读取操作被中断时触发。
FileReader.onerror
处理error
事件。该事件在读取操作发生错误时触发。
FileReader.onload
处理load
事件。该事件在读取操作完成时触发。
FileReader.onloadstart
处理loadstart
事件。该事件在读取操作开始时触发。
FileReader.onloadend
处理loadend
事件。该事件在读取操作结束时(要么成功,要么失败)触发。
FileReader.onprogress
处理progress
事件。该事件在读取Blob
时触发。读取文件进行中定时触发,可以用来做读取进度
fileReader.onprogress = function(e){
//事件一般只有total和loaded有用,用来做读取文件进度条
console.log(e.total,e.loaded);//e.total 文件的总量 e.loaded 已加载的大小
}
读取文件进度条案例
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>fileReader</title>
</head>
<body>
<input type="file" multiple>
<progress max="100" min="100" value="0"></progress>
<script>
const fileBtn = document.querySelector('input[type="file"]');
const progress = document.querySelector('progress');
fileBtn.addEventListener('change', function (e) {
let file = this.files[0];
let fileReader = new FileReader();
let total = 0;
fileReader.readAsDataURL(file); //文件转化为 Base64
fileReader.onloadstart = function (e) {
total = e.total;//文件总大小
}
fileReader.onprogress = function (e) {//加载中持续触发
console.log(e.loaded)//已加载大小
progress.value = e.loaded / total * 100;
}
fileReader.onload = function() {
//为了保证安全,建议每次上传结束要清理input文件
//清楚方法:在上传等操作回调中把file input的 类型进行一次切换 用于清空里面的files存留
//一般不放在onload里面,一般用于ajax上传表单,回调完成时再清空。
//如果不清除,当多次提交时,后面重新选择的提交文件,提交时有可能还是上一次选择的文件,用于防范此类bug。
fileBtn.files = null
fileBtn.type = 'text'
fileBtn.type = 'file'
}
}, false);
</script>
</body>
</html>
方法
FileReader.abort()
中止读取操作。在返回时,readyState
属性为DONE
。
FileReader.readAsArrayBuffer()
开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象,是一个数组对象,也是二进制数据,一般不用 。
FileReader.readAsBinaryString()
开始读取指定的Blob中的内容。一旦完成,result
属性中将包含所读取文件的原始二进制数据。一般不用,很少用前端做文件的二进制操作。
FileReader.readAsDataURL()
开始读取指定的Blob
中的内容。一旦完成,result
属性中将包含一个data:
URL格式的Base64字符串以表示所读取文件的内容,一般用来与后端做传输,如上传图片、视频、音频,解析拿到的base64字符串可以直接用。
FileReader.readAsText()
开始读取指定的Blob
中的内容。一旦完成,result
属性中将包含一个给定编码(默认为 utf-8 编码)的文本字符串以表示所读取的文件内容。一般用来读取文本型文件,如html文件(本质也是一串字符串文件,由浏览器来解析)、lrc文件(歌词文件),如果读取图片、音频等则会乱码,没有意义。
Blob
Blob
对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream
来用于数据操作。
Blob 表示的不一定是JavaScript原生格式的数据。File
接口基于Blob
,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
要从其他非blob对象和数据构造一个 Blob
,请使用 Blob()
构造函数。要创建一个 blob 数据的子集 blob,请使用 slice()
方法。要获取用户文件系统上的文件对应的 Blob
对象,请参阅 File
文档。
构造函数
-
Blob(blobParts[, options\])
返回一个新创建的
Blob
对象,其内容由参数中给定的数组串联组成。
属性
-
Blob.size
只读Blob
对象中所包含数据的大小(字节)。 -
Blob.type
只读一个字符串,表明该
Blob
对象所包含数据的 MIME 类型。如果类型未知,则该值为空字符串。
方法
-
Blob.slice([start[, end[, contentType\]]])
返回一个新的
Blob
对象,包含了源Blob
对象中指定范围内的数据。 -
Blob.stream()
返回一个能读取blob内容的
ReadableStream
。 -
Blob.text()
返回一个promise且包含blob所有内容的UTF-8格式的
USVString
。 -
Blob.arrayBuffer()
返回一个promise且包含blob所有内容的二进制格式的
ArrayBuffer
拖拽上传案例
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片文件拖拽上传</title>
<style>
* {
margin: 0;
padding: 0;
}
img {
display: block;
width: 200px;
height: auto;
}
.show-img {
display: flex;
justify-content: center;
align-items: center;
width: 800px;
height: 320px;
margin: 100px auto;
border: 1px solid red;
}
.container {
display: flex;
}
.upload {
position: relative;
width: 100px;
height: 100px;
border: 2px dashed #ccc;
}
.upload::before,
.upload::after {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
margin: auto;
background-color: black;
}
.upload::before {
width: 20px;
height: 4px;
}
.upload::after {
width: 4px;
height: 20px;
}
/* 触发file类型的Input上传文件不一定要点击上传按钮,点在Input身上即可 */
#file-input {
opacity: 0;
box-sizing: border-box;
width: 100px;
height: 100px;
}
.preview {
position: relative;
width: 100px;
height: 100px;
margin: 0 20px;
border-radius: 5px;
}
.preview-img {
width: 100%;
height: 100%;
}
.delete {
position: absolute;
width: 10px;
height: 10px;
top: -5px;
right: -5px;
color: red;
}
</style>
</head>
<body>
<form class="container" action="">
<div class="upload">
<input type="file" id="file-input" hidden>
</div>
<div class="show-box"></div>
</form>
<div class="show-img"></div>
<script>
let input = document.querySelector('input[type="file"]');
let showImg = document.querySelector('.show-img');
let imgInput = document.querySelector('#file-input');
let uploadBox = document.querySelector('.upload');
//点击别的位置调用inputFile功能
uploadBox.addEventListener('click',function(e){
imgInput.click();
},false);
//拖拽上传
showImg.addEventListener('dragover',function(e){
e.preventDefault();
},false);
showImg.addEventListener('drop',function(e){
e.preventDefault();
let file = e.dataTransfer.files[0];
createBolbFile(file)
},false);
//点击input上传
input.addEventListener('change', function (e) {
console.log(input.files[0]);//拿到input当前上传的文件,拿不到url,想在页面展示必须有url
let file = this.files[0];
// createFileReader(file);
createBolbFile(file)
}, false);
function createImg(src){
let img = document.createElement('img');
img.src = src;
showImg.append(img);
}
//生成临时blob存储
function createBolbFile(file){
let url = URL.createObjectURL(file); //生成一个临时地址blob,不可持续
//创建一个url的blob ,在当前会话下生效,不像base64那样在哪里都能用,如在浏览器中用,当你关掉浏览器再开浏览器用则不生效,是一种临时文件
createImg(url)
}
//base64存储
function createFileReader(file) {
let fileReader = new FileReader();//通过FileReader的result可以拿到文件的url
fileReader.readAsDataURL(file);//把文件创建成url的data对象
fileReader.onload = function () { //file加载完成才可以拿到url
let src = this.result //base64存储
createImg(src);
}
}
/*
base64存储
base64是新的文件协议格式,传输8bit字节码的编码方式,借助64个可以表示字符[A,B,C...1,2,3] 转换二进制
base64 字符串通用性很强 不依赖平台 不依赖环境 可持续 ,可以以字符串的形式传输图片、文档等
可以很方便的用来展示图片,下载文档
-----------------------------------------------
blob存储
如果要持续存储用base64,只是临时调用用blob
blob网址URL
只能在浏览器内部生成
URL/Object 允许Blob 和 file对象用作图像上 二进制数据连接URL源
URL.createObjectURL(file) 生成地址
同一个会话中 同一个浏览器实例中可以使用 临时的对象地址 不可持续
*/
</script>
</body>
</html>
Storage存储
sessionStorage 会话存储
sessionStorage可以存储数据到会话内存 存储容量5m左右 根据浏览器不同可使用空间有略微差别
window.sessionStorage.setItem('time','20:20'); //设置
let time = window.sessionStorage.getItem('time'); //读取
console.log(time); //'20:20'
window.sessionStorage.removeItem('time'); //删除指定key
window.sessionStorage.clear(); //清空
console.log(window.sessionStorage.getItem('time')); //undefined
注意
- 数据本质上存储在当前页面分配内存中, 当关闭当前会话页面 数据会擦除
- 声明周期与页面的会话周期相同
localStorage 本地存储
localStorage 可以存储数据到本地 存储容量20m左右 根据浏览器不同可使用空间有略微差别
window.localStorage.setItem('time','20:20'); //设置
let time = window.localStorage.getItem('time'); //读取
console.log(time); //'20:20'
window.localStorage.removeItem('time'); //删除指定key
window.localStorage.clear(); //清空
console.log(window.localStorage .getItem('time')); //undefined
注意
- 存储内容会放在浏览器程式的文件中(xml)
- 永久生效 数据存储在硬盘上 不会随着页面或者浏览器关闭而清除 如果需要清除 需要手动调用方法
Storage封装
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>localStorage 和 sessionStorage 封装</title>
</head>
<body>
3123123213123
<script>
(function (w) {
class Storage {
constructor(type = "local") {
this.storage = w[`${type}Storage`]
this.history = {}
}
//设置存储项
setStorage(...args) {
const len = args.length;
if (len === 0) {
//如果无参数 return false终止后续
return false;
}
if (len === 2) {
//如果两个参数 并且两个参数都不是对象或者数组 以字符串形式setItem
if (args.every(item => typeof item !== 'object')) {
this.history[args[0]] = args[1];
this.storage.setItem(String(args[0]), String(args[1]));
}
//反馈设置项目 提供check
return {
[args[0]]: args[1]
}
}
if (len === 1) {
//如果1个参数 并且是对象 合并到this.history中并且 迭代设置每一项
if (Object.prototype.toString.call(args[0]) === "[object Object]") {
let obj = args[0];
Object.assign(this.history, obj);
//返回给调用者一个 Array 方便check存储项目
return Object.entries(obj).map(([key, value]) => {
this.storage.setItem(key, value);
return key;
})
}
}
//返回所有设置历史 供check
return this.history;
}
//获取存储项目
getStorage(...args) {
const len = args.length;
if (len === 0) {
//如果不传参数 返回所有的存储项目
return this.history;
}
if (len === 1 && Object.prototype.toString.call(args[0]) === "[object Array]" && args[0].length) {
//如果只有一个参数并且参数维数组并且数组长度不为0
let arr = args[0].slice();
//循环数组获取数组每一项对应的值 返回一个json key(数组每一项):value(数组项对应值)
return arr.reduce((acc, curr) => {
acc[curr] = this.storage.getItem(curr);
return acc;
}, {});
}
if (len === 1 && Object.prototype.toString.call(args[0]) === "[object String]" && args[0].length) {
//如果参数只有一个 并且类型是字符串 并且字符串长度不为0 获取对应项的值
//如果不传参数 返回所有的存储项目
return sessionStorage.getItem(args[0]);
}
}
//参数可以是字符串 数组
removeStorage(...args) {
//降维args参数数组 扁平化处理
args = args.flat(Infinity);
//循环删除每一项
args.forEach(item => {
if (typeof item !== 'object') {
if (this.history[item]) {
delete this.history[item]
}
this.setStorage.removeItem(String(item));
}
})
}
//清空所有的storage 并且清空history
clearStorage() {
this.history = {};
this.storage.clear();
}
}
w.Storage = Storage;
})(window);
let storage = new Storage('local');
// storage.setStorage('name', '海牙')
</script>
</body>
</html>
网络状态
H5 支持网络状态监听事件
online 联网
window.addEventListener('online',function(){
console.log('联网');
},false)
offline断网
window.addEventListener('offline',function(){
console.log('断网');
},false)
页面状态监听
页面生命周期状态监听
window对象提供给我们可以监听页面当前状态事件 : onload、onpageshow、onpagehide、onbeforeunload、onunload 浏览器跳转、关闭、刷新时都按beforeunload,pagehide,unload,load,pageshow的顺序执行
window.location.url = 'http://www.baidu.com'
// 页面跳转之前执行了beforeunload事件
window.onbeforeunload = function () {
localStorage.setItem('beforeunload', 'check');
return false;
}
// 页面隐藏的时候执行了pagehide事件
window.onpagehide = function () {
localStorage.setItem('pagehide', 'check')
}
// 当用户离开页面时触发了unload事件
window.onunload = function () {
localStorage.setItem('unload', 'check')
}
// 页面完成加载时执行了load事件
window.onload = function () {
localStorage.setItem('load', 'check')
}
// 页面显示的时候执行了pageshow事件
window.onpageshow = function () {
localStorage.setItem('pageshow', 'check')
}
有时候,开发者需要知道,用户正在离开页面。常用的方法是监听下面三个事件
pagehide
beforeunload
unload
但是,这些事件在手机上可能不会触发,页面就直接关闭了。因为手机系统可以将一个进程直接转入后台,然后杀死。
- 用户点击了一条系统通知,切换到另一个 App。
- 用户进入任务切换窗口,切换到另一个 App。
- 用户点击了 Home 按钮,切换回主屏幕。
- 操作系统自动切换到另一个 App(比如,收到一个电话)。
上面这些情况,都会导致手机将浏览器进程切换到后台,然后为了节省资源,可能就会杀死浏览器进程。为了解决这个问题,就诞生了 Page Visibility API。不管手机或桌面电脑,所有情况下,这个 API 都会监听到页面的可见性发生变化。
这个新的 API 的意义在于,通过监听网页的可见性,可以预判网页的卸载,还可以用来节省资源,减缓电能的消耗。比如,一旦用户不看网页,下面这些网页行为都是可以暂停的。
- 对服务器的轮询
- 网页动画
- 正在播放的音频或视频
页面可见状态(visibility)监听
document.visibilityState可见状态属性
API 主要在
document
对象上,新增了一个document.visibilityState
属性。该属性返回一个字符串,表示页面当前的可见性状态,共有三个可能的值。
hidden:页面彻底不可见。
visible:页面至少一部分可见。
prerender:页面即将或正在渲染,处于不可见状态。
其中,hidden
状态和visible
状态是所有浏览器都必须支持的。prerender
状态只在支持"预渲染"的浏览器上才会出现,比如 Chrome 浏览器就有预渲染功能,可以在用户不可见的状态下,预先把页面渲染出来,等到用户要浏览的时候,直接展示渲染好的网页。
只要页面可见,哪怕只露出一个角,document.visibilityState
属性就返回visible
。只有以下四种情况,才会返回hidden
。
浏览器最小化。
浏览器没有最小化,但是当前页面切换成了背景页。
浏览器将要卸载(unload)页面。
操作系统触发锁屏屏幕。
visibilitychange可见状态事件
只要
document.visibilityState
属性发生变化,就会触发visibilitychange
事件。因此,可以通过监听这个事件(通过document.addEventListener()
方法或document.onvisibilitychange
属性),跟踪页面可见性的变化。
document.addEventListener('visibilitychange', function () {
if (document.visibilityState == 'hidden') {
document.title = '页面不可见';
} else {
document.title = '页面可见';
}
});
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
document.title = '页面不可见';
}
else {
document.title = '页面可见';
}
});
onblur && onfocus
在你浏览其他窗口页面、或是浏览器最小化、又或是点击了其他程序等等,都算是浏览器窗口失去焦点,那么
window.onblur
事件就会触发。当你浏览别的窗口或者别的程序,直接点就是当你的窗口失去焦点的时候,就会触发window.onblur
当你的页面获得焦点的时候一样也会触发window.onfocus
window.onblur = function () {
document.title = "页面失去焦点";
}
window.onfocus = function () {
document.title = "页面聚焦";
}
主要用于网页答题,只能在当前焦点下答题,失去焦点则可以判定答题失败。
留言板案例
离开页面后重新进入页面要保留上次输入的内容
使用localStorage 及 页面生命周期事件
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 400px;
height: 180px;
padding: 10px;
box-shadow: 0 0 4px #222;
}
</style>
</head>
<body>
<h3>留言板</h3>
<div contenteditable></div>
<a href="http://www.baidu.com">跳转</a>
<script>
let commitWrap = document.querySelector('div');
// 页面跳转之前执行了beforeunload事件
window.onbeforeunload = function () {
let commit = commitWrap.innerText;
if (commit.trim()) {
localStorage.setItem('commit', commit);
}
localStorage.setItem('beforeunload', 'check');
}
// 页面隐藏的时候执行了pagehide事件
window.onpagehide = function () {
localStorage.setItem('pagehide', 'check')
}
// 当用户离开页面时触发了unload事件
window.onunload = function () {
localStorage.setItem('unload', 'check');
}
// 页面完成加载时执行了load事件
window.onload = function () {
let commit = localStorage.getItem('commit');
if (commit.trim()) {
commitWrap.innerText = commit;
}
localStorage.setItem('load', 'check')
}
// 页面显示的时候执行了pageshow事件
window.onpageshow = function () {
localStorage.setItem('pageshow', 'check')
}
</script>
</body>
</html>
地理信息
h5提供了地理位置功能(Geolocation API),能确定用户位置,我们可以借助h5的该特性开发基于地理位置信息的应用,本文集合实力给大家分享下如何使用h5,借助百度,谷歌地图接口来获取用户准确的地理位置信息。
基础概念
地理位置
• 经度 : 南北极的连接线
• 纬度 : 东西连接的线
位置信息从何而来
• IP地址
• GPS全球定位系统
• Wi-Fi无线网络
• 基站
API方法
地理位置对象
navigator.geolocation
单次定位请求 :getCurrentPosition(请求成功,请求失败,数据收集方式)
if (window.navigator.geolocation) {
navigator.geolocation.getCurrentPosition(successCallback, errorCallback, options);
}
请求成功函数 successCallback
function successCallback(position) {
var output = '';
output += "您的位置已经确定下来\n\n";
output += '经度:' + position.coords.longtitude + "\n\n";
output += '纬度:' + position.coords.latitude + "\n\n";
output += '精度:' + position.coords.accuracy + " 米\n";
if (position.coords.altitudeAccuracy) {
output += '海拔精度:' + position.coords.altitudeAccuracy + " 米\n";
};
if (position.coords.heading) {
output += '速度:' + position.coords.Speed + "m/s\n";
};
output += '时间戳:' + position.timestamp;
console.log(output);
}
经度 : coords.longitude
纬度 : coords.latitude
准确度 : coords.accuracy
海拔 : coords.altitude
海拔准确度 : coords.altitudeAcuracy
行进方向 : coords.heading
地面速度 : coords.speed
时间戳 : new Date(position.timestamp)
请求失败函数 errorCallback
function errorCallback(error) {
console.log(error);
}
// GeolocationPositionError {code: 3, message: "Timeout expired"}
失败编号 :code
0 : 不包括其他错误编号中的错误
1 : 用户拒绝浏览器获取位置信息
2 : 尝试获取用户信息,但失败了
3 : 设置了timeout值,获取位置超时了
数据收集 : options (json的形式)
const options = {
enableHighAccuracy: true,
maximumAge: 1000,
timeout: 10000,
}
enableHighAcuracy : 更精确的查找,默认false
timeout : 获取位置允许最长时间,默认infinity
maximumAge : 位置可以缓存的最大时间,默认0
Canvas 画布
HTML5 元素用于图形的绘制,通过javascript等脚本语言操作API实现绘制
元素
canvas看起来和 img元素很相像,唯一的不同就是它并没有 src 和 alt 属性。实际上, 标签只有两个属性 width和height。
<canvas id="myCanvas" width="200" height="100">
您的浏览器不支持canvas画布 请使用ie9+版本浏览器
</canvas>
绘制直线
//先获取画布
const canvas = document.querySelector('canvas');
//配置绘制的环境
const context = canvas.getContext('2d');
//在绘制之前,加上
context.beginPath(); // 意思是开始绘制
//设置起点
context.moveTo(10,10); // 这里的第一个参数是x轴,第二个参数是y轴
//设置下一个点,
context.lineTo(x,y); // 下个一个点坐标
//结束绘制
context.closePath();
//画线条
context.stroke() ;
方法
getContext()
getContext方法返回canvas的绘制上下文, 通过上下文环境我们才可以进行绘制
基本上,只要我们使用canvas实现功能,getContext()方法调用100%需要使用。甚至可以这么说,只要你想用canvas实现任何平面效果,首先不管三七二十一,先把下面2行写上:
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
// 2d是canvas最常用的绘制环境
//WebGL是3d,一般不是专业图像开发用不上
toBlob()
toBlob()方法可以Canvas图像对应的Blob对象(binary large object)。此方法可以把Canvas图像缓存在磁盘上,或者存储在内存中,这个往往由浏览器决定。
canvas.toBlob(callback, mimeType, quality);
参数
callbackFunction
toBlob()方法执行成功后的回调方法,支持一个参数,表示当前转换的Blob对象。
mimeType(可选)String
mimeType表示需要转换的图像的mimeType类型。默认值是image/png,还可以是image/jpeg,甚至image/webp(前提浏览器支持)等。
quality(可选)Number
quality表示转换的图片质量。范围是0到1。由于Canvas的toBlob()方法转PNG是无损的,因此,此参数默认是没有效的,除非,指定图片mimeType是image/jpeg或者image/webp,此时默认压缩值是0.92。
toDataURL()
Canvas本质上就是一个位图图像,因此,浏览器提供了若干API可以将Canvas图像转换成可以作为IMG呈现的数据,其中最老牌的方法就是
HTMLCanvasElement.toDataURL()
,此方法可以返回Canvas图像对应的data URI,也就是平常我们所说的base64地址
canvas.toDataURL(mimeType, quality);
参数
mimeType(可选)String
mimeType表示需要转换的图像的mimeType类型。默认值是image/png,还可以是image/jpeg,甚至image/webp(前提浏览器支持)等。
quality(可选)Number
quality表示转换的图片质量。范围是0到1。此参数要想有效,图片的mimeType需要是image/jpeg或者image/webp,其他mimeType值无效。默认压缩质量是0.92。
根据自己的肉眼分辨,如果使用toDataURL()的quality参数对图片进行压缩,同样的压缩百分比呈现效果要比Adobe Photoshop差一些。
。
返回值
返回base64 data图片数 据
Context2D 上下文
canvas的绘制API 属性和方法主要集中在 2D上下文中
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d'); //Context2D
Context2D属性
fillStyle
设置填充样式
context.fillStyle = color; //使用纯色填充,支持RGB,HSL,RGBA,HSLA以及HEX色值。
context.fillStyle = gradient; //使用渐变填充,可以是线性渐变或者径向渐变。
context.fillStyle = pattern; //使用纹理填充。由于图片也能作为纹理,因此fillStyle也能填充普通的位图,可参见下面的案例。
stockStyle
设置描边样式
context.strokeStyle = 'red';
context.fillStyle = 'blue';
context.lineWidth = 10;
context.strokeRect(40, 20, 160, 80);
context.fillRect(40, 20, 160, 80);
font
设置canvas中文本的字体字号 默认值: 10px sans-serif
context.font = '24px SimSun, Songti SC';
textAlign
和CSS的
text-align
属性值类似,支持left
,right
,centerstart
,end
这些值,具体含义参见下面的语法。其中
value
支持如下几个关键字:
left
文本左对齐。也就是最终绘制的文本内容最左侧位置就是设定的
x
坐标值。right
文本右对齐。也就是最终绘制的文本内容最右侧位置就是设定的
x
坐标值。center
文本居中对齐。也就是最终绘制的文本内容的水平中心位置就是设定的
x
坐标值。start
文本起始方位对齐。如果文本是从左往右排列的,则表示左对齐;如果文本是从右往左排列的(例如设置
context.direction
为rtl
),则表示右对齐。end
文本结束方位对齐。如果文本是从左往右排列的,则表示右对齐;如果文本是从右往左排列的(例如设置
context.direction
为rtl
),则表示左对齐。
lineWidth
表示绘制的线条粗细 默认值 1.0
context.lineWidth = value; //value 表示线的宽度。数值类型,默认值是1.0。如果是负数,0,NaN,或者Infinity都会忽略。
shadow阴影系列
shadowColor
阴影颜色 HEX RGB RGBA
shadowBlur
阴影模糊度 默认值是
0
,表示不模糊。
shadowOffsetX
阴影X偏移
shadowOffsetY
阴影Y偏移
// 上阴影
context.shadowColor = 'rgb(50, 50, 50)';
context.shadowBlur = 5;
context.shadowOffsetY = -5;
// 文字80像素,黑体,红色
context.fillStyle = 'red';
context.font = '70px STheiti, simHei';
context.fillText('上偏移', 10, 88);
globalCompositeOperation
混合模式
ctx.globalCompositeOperation = model;
模式详情
Context2D方法
moveTo()
绘制点移动到指定位置 通常表示起始点
context.moveTo(x, y);
x Number 绘制的直线的落点的横坐标。
y Number 绘制的直线的落点的纵坐标。
lineTo()
直线连接当前最后子路径点与lineTo指定点
context.lineTo(x, y);
x Number 绘制的直线的落点的横坐标。
y Number 绘制的直线的落点的纵坐标。
stroke()
对路径进行描边
context.stroke();
例:
context.moveTo(50, 20);
context.lineTo(200, 100);
context.stroke();
arc()
绘制圆弧
context.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
x Number 圆弧对应的圆心横坐标。
y Number圆弧对应的圆心纵坐标。
radius Number圆弧的半径大小。
startAngle Number圆弧开始的角度,单位是弧度。
endAngle Number圆弧结束的角度,单位是弧度。
anticlockwise(可选)Boolean 弧度的开始到结束的绘制是按照顺时针来算,还是按时逆时针来算。如何设置为true,则表示按照逆时针方向从startAngle绘制到endAngle。
// 逆时针绘制0到1/4弧度圆弧
context.beginPath();
context.arc(150, 75, 50, 0, Math.PI / 2, true);
context.stroke();
arcTo()
给路径添加圆弧,需要指定控制点和半径
context.arcTo(x1, y1, x2, y2, radius);
x1 Number 第1个控制点的横坐标。
y1 Number 第1个控制点的纵坐标。
x2 Number 第2个控制点的横坐标。
y2 Number 第2个控制点的纵坐标。
radius Number 圆弧的半径大小。
context.beginPath();
context.moveTo(50, 50);
context.arcTo(150, 100, 200, 40, 40);
context.lineTo(200, 40);
context.stroke();
beginPath()
绘制新路径
context.beginPath();
clearRect()
擦除指定区域的画布内容
context.clearRect(x, y, width, height);
x Number 矩形左上角x坐标。
y Number 矩形左上角y坐标。
width Number 被清除的矩形区域的高度。
height Number 被清除的矩形区域的宽度度。
clip()
根据路径剪裁
context.clip();
context.clip(fillRule);
context.clip(path, fillRule);
fillRule String
填充规则。用来确定一个点实在路径内还是路径外。可选值包括:
nonzero:非零规则。此乃默认规则。
evenodd:奇偶规则。
关于'nonzero'和'evenodd'规则可参见这篇文章。
path Object
指Path2D对象。
//裁剪步骤
// 1. 创建路径
// 2. 调用剪裁方法
// 3. 后续所有的绘制只有剪裁区域内会展示
var context = canvas.getContext('2d');
// 需要图片先加载完毕
var img = new Image();
img.onload = function () {
// 剪裁路径是三角形
context.beginPath();
context.moveTo(20, 20);
context.lineTo(200, 80);
context.lineTo(110, 150);
// 剪裁
context.clip();
// 填充图片
context.drawImage(img, 0, 0, 250, 167);
};
img.src = './1.jpg';
closePath()
闭合路径 连接起始点与结束点形成闭合区域
context.closePath();
// 绘制三角
context.beginPath();
context.moveTo(10, 10);
context.lineTo(140, 70);
context.lineTo(70, 140);
// 不执行闭合,直接描边
context.stroke();
// 绘制另外一个三角
context.beginPath();
context.moveTo(160, 10);
context.lineTo(290, 70);
context.lineTo(220, 140);
// 执行闭合,然后描边
context.closePath();
context.stroke();
渐变模式createLinearGradient()
创建线性渐变对象createLinearGradient(x0, y0, x1, y1) ,参数(渐变起点 x0 y0 渐变终点 x1 y1)
addColorStop()渐变颜色阶段
context.createLinearGradient(x0, y0, x1, y1);
x0 Number 渐变起始点横坐标。
y0 Number 渐变起始点纵坐标。
x1 Number 渐变结束点横坐标。
y1 Number 渐变结束点纵坐标。
线性渐变效果比较好脑补,就是从坐标点[x0, y0]到坐标点[x1, y1]的位置画一条线,然后整个渐变色带与与这条线垂直。
var context = canvas.getContext('2d');
// 创建渐变
var gradient = context.createLinearGradient(0, 0, 300, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'green');
// 设置填充样式为渐变
context.fillStyle = gradient;
// 左上角和右下角分别填充2个矩形
context.fillRect(10, 10, 160, 60);
context.fillRect(120, 80, 160, 60);
纹理模式createPattern()
createPattern()
方法在指定的方向内重复指定的元素。元素可以是图片、视频,或者其他 元素。
被重复的元素可用于绘制/填充矩形、圆形或线条等等。
ctx.createPattern(img,direction)
img 纹理图片
direction 填充方向 repeat repeat-x repeat-y no-repeat
var context = canvas.getContext('2d');
var img=document.getElementById("lamp")
var pat=context.createPattern(img,repeat);
context.rect(0,0,150,100);
context.fillStyle=pat;
context.fill();
绘制图片drawImage()
绘制图片 用于实现图像压缩 水印添加 合成图像等操作
context.drawImage(image, dx, dy); 图片对象, 绘制起点X, 绘制起点Y
context.drawImage(image, dx, dy, dWidth, dHeight); 图片对象, 画布绘制起点dx,起点dy,图像尺寸dw,图像尺寸dh
context.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
各个参数含义和作用如下:
image Object 图片对象,可以是各类Canvas图片资源,如<img>图片,SVG图像,Canvas元素本身等。
dx Number 画布绘制起点的左上角横坐标。
dy Number 画布绘制起点的左上角纵坐标。
dWidth Number 图像尺寸宽度
dHeight Number 图像尺寸高度
sx Number 原图剪裁起点x横坐标
sy Number 原图剪裁起点y纵坐标
sWidth Number 原图剪裁宽度
sHeight Number 原图剪裁高度
- 第1类 就是image,同上,没什么好说的;
- 第2类 就是dx,dy,dWidth和dHeight,表示在Canvas画布上划出一片区域用来放置图片,dx,dy为canvas元素的左上角坐标,dWidth,dHeight指Canvas元素上用在显示图片的区域大小。如果没有指定dx,dy,dWidth和dHeight这4个参数,则图片会被拉伸或缩放在这片区域内。
- 第3类 就是sx,sy,sWidth和sHeight,你可以理解为对原始图片的提前剪裁。sx,sy表示图片上sx,sy这个坐标作为剪裁的左上角,sWidth和sHeight尺寸范围是最终剪裁出来的图片尺寸。sx,sy,sWidth和sHeight这4个参数就可以得到一个剪裁好的新图,然后我们把这个新图放在dx,dy,dWidth和dHeight这个Canvas画布区域中,就是最终的效果。另外,此处的所有坐标和宽高值都是相对于图片的原始尺寸而言的。
drawImage()方法有一个非常怪异的地方,大家一定要注意,那就是5参数和9参数用法的参数位置是不一样的,这个和一般的API有所不同。一般API可选参数是放在后面。但是,这里的drawImage()使用9个参数时候,可选参数sx,sy,sWidth和sHeight是在前面的。如果不注意这一点,有些表现会让你无法理解。
拉伸图片同时保持图片比例
//如何填满Canvas画布,同时保持图片的原始比例呢?这个就需要用到sx,sy,sWidth和sHeight这几个参数,注意,这4个参数是要写在dx,dy,dWidth和dHeight前面的,和一般的API不一样。
context.drawImage(image, 0, 42, 500, 250, 0, 0, 300, 150);
0200921195459851.png&pos_id=img-R1JfE2nm-1721612905230)
fill()
填充路径
context.fill();
context.fill(fillRule);
context.fill(path, fillRule);
fillRule String
填充规则。用来确定一个点实在路径内还是路径外。可选值包括:
nonzero:非零规则,此乃默认规则。
evenodd:奇偶规则。
关于'nonzero'和'evenodd'规则可参见这篇文章。
path Object
指Path2D对象。
context.beginPath();
context.arc(60, 60, 40, 0, Math.PI * 2);
context.fillStyle = '#368';
context.fill();
绘制矩形rect
rect()
绘制矩形路径
context.rect(x, y, width, height);
x Number 矩形路径的起点横坐标。
y Number 矩形路径的起点纵坐标。
width Number 矩形的宽度。
height Number 矩形的高度。
context.rect(100, 25, 100, 100);
context.stroke();
strokeRect()
矩形描边 其他图形没有的现成填充方法,不需要context.stroke()
context.strokeRect(x, y, width, height);
x Number 矩形路径的起点横坐标。
y Number 矩形路径的起点纵坐标。
width Number 矩形的宽度。
height Number 矩形的高度。
context.lineWidth = 2;
context.strokeRect(75, 25, 150, 100);
fillRect()
图形填充(矩形) 其他图形没有的现成填充方法
context.fillRect(x, y, width, height);
x Number 填充矩形的起点横坐标。
y Number 填充矩形的起点纵坐标。
width Number 填充矩形的宽度。
height Number 填充矩形的高度。
context.fillStyle = '#333'
context.fillRect(200,200,200,200)
// 中心点坐标
let centerX = canvas.width / 2;
let centerY = canvas.height / 2;
// 矩形填充
context.fillRect(centerX - 30, centerY - 4, 60, 8);
context.fillRect(centerX - 4, centerY - 30, 8, 60);
save() & restore()
save存储状态 restore还原状态
save()保存当前Canvas画布状态并放在栈的最上面,可以使用restore()方法依次取出。
// 保存初始Canvas状态
context.save();
// 设置红色填充
context.fillStyle = 'red';
// 矩形填充
context.fillRect(20, 20, 100, 60);
// 还原在绘制
context.restore();
// 矩形填充again
context.fillRect(180, 60, 100, 60);
fillText()
用来填充文字,是Canvas绘制文本的主力方法。
context.fillText(text, x, y [, maxWidth]);
text String 用来填充的文本信息。
x Number 填充文本的起点横坐标。
y Number 填充文本的起点纵坐标。
maxWidth(可选)Number 填充文本占据的最大宽度 超过此宽度的时候文本会压缩 不会换行
context.font = '24px STheiti, SimHei';
context.wrapText('Canvas H5画布', 24, 56, 200);
measureText()
用来测量文本的一些数据,返回TextMetrics对象,包含字符宽度等信息。
context.measureText(text);
text String 被测量的文本。
// 设置字体字号
context.font = '24px STHeiTi, SimHei';
// 文本信息对象就有了
var textZh = context.measureText('帅');
var textEn = context.measureText('handsome');
// 文字绘制
context.fillText('帅', 60, 50);
context.fillText('handsome', 60, 90);
// 显示宽度
context.font = '12px Arial';
context.fillStyle = 'red';
context.fillText('宽' + textZh.width, 62 + textZh.width, 40);
context.fillText('宽' + textEn.width, 62 + textEn.width, 80);
改变绘制环境
scale()
来缩放Canvas画布的坐标系,只是影响坐标系,之后的绘制会受此方法影响,但之前已经绘制好的效果不会有任何变化。
context.scale(x, y);
x Number
Canvas坐标系水平缩放的比例。支持小数.
y Number
Canvas坐标系垂直缩放的比例。支持小数.
// 显示绘制个正方形用来对比
context.fillRect(10, 10, 10, 10);
// 缩放
context.scale(10, 3);
// 再次绘制
context.fillRect(10, 10, 10, 10);
// 恢复坐标系
context.setTransform(1, 0, 0, 1, 0, 0);
// 记住Canvas状态
context.save();
// 来来来,垂直翻转下
context.scale(1, -1);
// 填充文字
context.font = '32px STHeiti, SimHei';
context.fillText('换个角度看世界', 36, -64);
// 恢复状态,不要影响接下来的绘制
context.restore();
rotate()
添加旋转 顺时针方向 单位弧度
默认旋转中心点是Canvas的左上角(0, 0)坐标点,如果希望改变旋转中心点,例如以Canvas画布的中心旋转,需要先使用translate()位移旋转中心点。
角度转弧度计算公式是:radian = degree * Math.PI / 180。例如,旋转45°,旋转弧度就是45 * Math.PI / 180。
context.rotate(angle);
angle Number
Canvas画布坐标系旋转的角度,单位是弧度。注意,此旋转和CSS3的旋转变换不一样,旋转的是坐标系,而非元素。因此,实际开发的时候,旋转完毕,需要将坐标系再还原。
// 旋转45度
context.rotate(45 * Math.PI / 180);
// 字体填充
context.font = '20px STHeiti, SimHei';
context.fillText('旋转,跳跃,我闭着眼', 60, -40, 188);
// 重置当前的变换矩阵为初始态
context.setTransform(1, 0, 0, 1, 0, 0);
transform()
方法对Canvas坐标系进行整体移动, 用于改变其他变化方式的变换中心
注意:这是整个画布、即绘制环境都进行了移动
context.translate(x, y);
x Number 坐标系水平位移的距离。
y Number 坐标系垂直位移的距离。
黑色为原始坐标系,红色为移动后的坐标系。
var img = new Image();
img.onload = function () {
var context = canvas.getContext('2d');
// 坐标位移
context.translate(150, 100);
// 旋转45度
context.rotate(45 * Math.PI / 180);
// 再位移回来
context.translate(-150, -100);
// 此时绘制图片就是中心旋转了
context.drawImage(this, 0, 0, 300, 200);
// 坐标系还原
context.setTransform(1, 0, 0, 1, 0, 0);
};
img.src = './1.jpg';
setTransform()
方法通过矩阵变换重置当前坐标系
此方法和transform()方法的区别在于,后者不会完全重置已有的变换,而是累加。
context.setTransform(a, b, c, d, e, f);
a Number 水平缩放。
b Number 水平斜切。
c Number 垂直斜切。
d Number 垂直缩放。
e Number 水平位移。
f Number 垂直位移。
//还原坐标系
context.translate(150, 100);
// 旋转45度
context.rotate(45 * Math.PI / 180);
// 再位移回来
context.translate(-150, -100);
// 此时绘制图片就是中心旋转了
context.drawImage(this, 0, 0, 300, 200);
// 还原绘制环境坐标系的变化
context.setTransform(1, 0, 0, 1, 0, 0);
createImageData()
方法可以创建一个全新的空的ImageData对象。该对象中的所有像素信息都是透明黑。
context.createImageData(width, height);
context.createImageData(imagedata);
返回值是ImageData对象,包含width,height和data这3个只读属性。参数具体含义如下:
width Number
ImageData对象包含的width值。如果ImageData对象转换成图像,则此width也是最终图像呈现的宽度。
height Number
ImageData对象包含的height值。如果ImageData对象转换成图像,则此height也是最终图像呈现的高度。
imagedata Object
一个存在的ImageData对象,只会使用该ImageData对象中的width和height值,包含的像素信息会全部转换为透明黑。
如下,是在canvas画布中左上角拉出了一个4*4的区域,
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let imageData = context.createImageData(4,4);
console.log(imageData);
返回全新的空的ImageData对象,如下:
数组内一共有64个数据,即4*4*4=64
,4*4
即16个像素点,最后一个4
是像素点的rbga,即 `[r1,g1,b1,a1,r2,g2,b2,a2…rN,gN,bN,aN]
getImageData()
方法可以根据参数获取画布上对应位置的图像对象
ImageData = ctx.getImageData(sx, sy, sw, sh);
参数
sx 要被提取的图像数据矩形区域的左上角 x 坐标。
sy 将要被提取的图像数据矩形区域的左上角 y 坐标。
sw 将要被提取的图像数据矩形区域的宽度。
sh 将要被提取的图像数据矩形区域的高度。
返回值
一个ImageData 对象,包含canvas给定的矩形图像数据。
错误抛出
IndexSizeError
如果高度或者宽度变量为0,则抛出错误。
putImageData()
将数据从已有的 ImageData对象绘制到位图的方法
ctx.putImageData(imagedata, dx, dy);
ctx.putImageData(imagedata, dx, dy, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
参数
imageData
ImageData ,包含像素值的数组对象。
dx
源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量)。
dy
源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量)。
dirtyX 可选
在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(x 坐标)。
dirtyY 可选
在源图像数据中,矩形区域左上角的位置。默认是整个图像数据的左上角(y 坐标)。
dirtyWidth 可选
在源图像数据中,矩形区域的宽度。默认是图像数据的宽度。
dirtyHeight 可选
在源图像数据中,矩形区域的高度。默认是图像数据的高度。
全屏画布
创建全屏黑色画布,画布宽高与浏览器宽高保持一致,随着浏览器窗口大小的变化跟随着变化,不出现滚动条,始终沾满整个浏览器,实现画布内容等比例缩放。
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas 画布</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
overflow: hidden;
}
canvas {
background-color: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script>
const canvas = document.querySelector('canvas')
const context = canvas.getContext('2d');
init();
function init() {
window.addEventListener('resize', resizeView(), false)
}
function resizeView() {
const winWidth = window.innerWidth;
const winHeight = window.innerHeight;
canvas.width = winWidth;
canvas.height = winHeight;
return resizeView;
}
</script>
</body>
</html>
移动泡泡案例
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas移动泡泡</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
overflow: hidden;
height: 100%;
}
canvas {
background-color: #222;
}
</style>
</head>
<body>
<canvas></canvas>
<script>
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
init();
window.addEventListener('resize', resize, false);
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
function init() {
resize();
}
const COLORS = ['#69D2E7', '#A7DBD8', '#E0E4CC', '#F38630', '#FA6900', '#FF4E50', '#F9D423']; //彩色池子
let particles = [];
(function (context) {
class Particle {
static context = context;
constructor({ x = 0, y = 0, r = 3, color = '#F9D423' } = {}) {
this.init({ x, y, r, color })
}
//初始化粒子的 位置 半径 颜色 移动方向 移动方向增量
init({ x, y, r, color }) {
this.x = x;
this.y = y;
this.r = r;
this.color = color;
this.th = random(0, Math.PI * 2); //初始移动方向
this.vx = Math.sin(this.th) * 4; //初始方向上x坐标
this.vy = Math.cos(this.th) * 4; //初始方向上y坐标
}
//改变小球的x y r
update() {
//x += vx x位置 = x+偏移量
this.x += this.vx;
this.y += this.vy;
//x1 = x0 + r * cos(th) x方向顺着th角度运动的路径
//每次相对x0 顺着th方向移动 0.1距离
this.vx += Math.cos(this.th) * .1;
this.vy += Math.sin(this.th) * .1;
//增加摩擦系数 增量衰减
this.vx *= .92;
this.vy *= .92;
//半径衰减
this.r *= .96;
this.draw();
}
//在画布上绘制粒子
draw() {
context.beginPath();
//设置绘画环境填充颜色
context.fillStyle = this.color;
//开启叠加模式为 lighter
context.globalCompositeOperation = 'lighter';
//绘制球 圆心位置为 鼠标位置 半径40 整圆
context.arc(this.x, this.y, this.r, Math.PI * 2, false);
//填充路径 圆
context.fill();
}
}
window.Particle = Particle;
})(context);
canvas.addEventListener('mousemove', function (e) {
for (let i = 0; i < random(1, 7); i++) {
particles.push(new Particle({
x: e.clientX,
y: e.clientY,
color: random(COLORS),
r: random(8, 40),
}));
}
if (particles.length > 800) {
particles.shift();
}
}, false);
render();
//帧动画定时器循环执行每个粒子的update方法
function render() {
// console.log(particles.length)
context.clearRect(0, 0, canvas.width, canvas.height);
//粒子数组中获取每个小球执行小球的update方法
particles.forEach(item => {
item.update();
})
requestAnimationFrame(render);
}
function random(min, max) {
if (Array.isArray(min)) {
return min[~~(Math.random() * min.length)]
}
return min + ~~(Math.random() * (max - min));
}
</script>
</body>
</html>
requestAnimationFrame 帧动画
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行
window.requestAnimationFrame(callback);
参数
callback
下一次重绘之前更新动画帧所调用的函数(即上面所说的回调函数)。该回调函数会被传入DOMHighResTimeStamp
参数,该参数与performance.now()
的返回值相同,它表示requestAnimationFrame()
开始去执行回调函数的时刻。
返回值
一个 long
整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给 window.cancelAnimationFrame()
以取消回调函数。
requestAnimationFrame还有以下两个优势:
-
CPU节能:使用setTimeout实现的动画,当页面被隐藏或最小化时,setTimeout 仍然在后台执行动画任务,由于此时页面处于不可见或不可用状态,刷新动画是没有意义的,完全是浪费CPU资源。而requestAnimationFrame则完全不同,当页面处理未激活的状态下,该页面的屏幕刷新任务也会被系统暂停,因此跟着系统步伐走的requestAnimationFrame也会停止渲染,当页面被激活时,动画就从上次停留的地方继续执行,有效节省了CPU开销。
-
函数节流:在高频率事件(resize,scroll等)中,为了防止在一个刷新间隔内发生多次函数执行,使用requestAnimationFrame可保证每个刷新间隔内,函数只被执行一次,这样既能保证流畅性,也能更好的节省函数执行的开销。一个刷新间隔内函数执行多次时没有意义的,因为显示器每16.7ms刷新一次,多次绘制并不会在屏幕上体现出来。
针节流
var locked = false;
window.addEventListenser('scroll',function(){
if(!locked){
locked = true;
window.requestAnimationFrame(fAnim);
}
});
function fAnim(){
locked = false;
//code
}
兼容封装
if (!Date.now)
Date.now = function() { return new Date().getTime(); };
(function() {
'use strict';
var vendors = ['webkit', 'moz'];
for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
var vp = vendors[i];
window.requestAnimationFrame = window[vp+'RequestAnimationFrame'];
window.cancelAnimationFrame = (window[vp+'CancelAnimationFrame']
|| window[vp+'CancelRequestAnimationFrame']);
}
if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
|| !window.requestAnimationFrame || !window.cancelAnimationFrame) {
var lastTime = 0;
window.requestAnimationFrame = function(callback) {
var now = Date.now();
var nextTime = Math.max(lastTime + 16, now);
return setTimeout(function() { callback(lastTime = nextTime); },
nextTime - now);
};
window.cancelAnimationFrame = clearTimeout;
}
}());
worker 多线程
JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个线程上完成,一次只能做一件事。前面的任务没做完,后面的任务只能等着。随着电脑计算能力的增强,尤其是多核 CPU 的出现,单线程带来很大的不便,无法充分发挥计算机的计算能力。
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。
Web Worker 限制
(1)同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。
(2)DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document
、window
、parent
这些对象。但是,Worker 线程可以navigator
对象和location
对象。
(3)通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。
(4)脚本限制
Worker 线程不能执行alert()
方法和confirm()
方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。
(5)文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://
),它所加载的脚本,必须来自网络。
写法
let worker = new Worker('worker.js');
创建一个专用Web worker,它只执行URL指定的脚本。使用 Blob URL 作为参数亦可。
Worker()
构造函数,可以接受两个参数。第一个参数是脚本的网址(必须遵守同源政策),该参数是必需的,且只能加载 JS 脚本,否则会报错。第二个参数是配置对象,该对象可选。它的一个作用就是指定 Worker 的名称,用来区分多个 Worker 线程。
Worker()
构造函数返回一个 Worker 线程对象,用来供主线程操作 Worker。Worker 线程对象的属性和方法如下。
属性和方法
Worker.onerror:指定 error 事件的监听函数。
Worker.onmessage:指定 message 事件的监听函数,发送过来的数据在Event.data属性中。
Worker.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
Worker.postMessage():向 Worker 线程发送消息。
Worker.terminate():立即终止 Worker 线程。
Worker 线程
Web Worker 有自己的全局对象,不是主线程的window
,而是一个专门为 Worker 定制的全局对象。因此定义在window
上面的对象和方法不是全部都可以使用。
Worker 线程有一些自己的全局属性和方法。
- self.name: Worker 的名字。该属性只读,由构造函数指定。
- self.onmessage:指定
message
事件的监听函数。- self.onmessageerror:指定 messageerror 事件的监听函数。发送的数据无法序列化成字符串时,会触发这个事件。
- self.close():关闭 Worker 线程。
- self.postMessage():向产生这个 Worker 线程发送消息。
- self.importScripts():加载 JS 脚本。
用例
主线程采用new
命令,调用Worker()
构造函数,新建一个 Worker 线程。
var worker = new Worker('work.js');
Worker()
构造函数的参数是一个脚本文件,该文件就是 Worker 线程所要执行的任务。由于 Worker 不能读取本地文件,所以这个脚本必须来自网络。如果下载没有成功(比如404错误),Worker 就会默默地失败。
然后,主线程调用worker.postMessage()
方法,向 Worker 发消息。
worker.postMessage('Hello World'); worker.postMessage({method: 'echo', args: ['Work']});
worker.postMessage()
方法的参数,就是主线程传给 Worker 的数据。它可以是各种数据类型,包括二进制数据。
接着,主线程通过worker.onmessage
指定监听函数,接收子线程发回来的消息。
worker.onmessage = function (event) { console.log('Received message ' + event.data); doSomething(); } function doSomething() { // 执行任务 worker.postMessage('Work done!'); }
上面代码中,事件对象的data
属性可以获取 Worker 发来的数据。
Worker 完成任务以后,主线程就可以把它关掉。
worker.terminate();
Worker 线程文件
Worker 线程内部需要有一个监听函数,监听message
事件。
self.addEventListener('message', function (e) { self.postMessage('You said: ' + e.data); }, false);
上面代码中,self
代表子线程自身,即子线程的全局对象。因此,等同于下面两种写法。
// 写法一 this.addEventListener('message', function (e) { this.postMessage('You said: ' + e.data); }, false); // 写法二 addEventListener('message', function (e) { postMessage('You said: ' + e.data); }, false);
除了使用self.addEventListener()
指定监听函数,也可以使用self.onmessage
指定。监听函数的参数是一个事件对象,它的data
属性包含主线程发来的数据。self.postMessage()
方法用来向主线程发送消息。
根据主线程发来的数据,Worker 线程可以调用不同的方法,下面是一个例子。
self.addEventListener('message', function (e) { var data = e.data; switch (data.cmd) { case 'start': self.postMessage('WORKER STARTED: ' + data.msg); break; case 'stop': self.postMessage('WORKER STOPPED: ' + data.msg); self.close(); // Terminates the worker. break; default: self.postMessage('Unknown command: ' + data.msg); }; }, false);
上面代码中,self.close()
用于在 Worker 内部关闭自身。
Worker 加载脚本
Worker 内部如果要加载其他脚本,有一个专门的方法importScripts()
。
importScripts('script1.js');
该方法可以同时加载多个脚本。
importScripts('script1.js', 'script2.js');
错误处理
主线程可以监听 Worker 是否发生错误。如果发生错误,Worker 会触发主线程的error
事件。
worker.onerror(function (event) { console.log([ 'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message ].join('')); }); // 或者 worker.addEventListener('error', function (event) { // ... });
Worker 内部也可以监听error
事件。
关闭 Worker
使用完毕,为了节省系统资源,必须关闭 Worker。
// 主线程 worker.terminate(); // Worker 线程 self.close();
同页面的 Web Worker
通常情况下,Worker 载入的是一个单独的 JavaScript 脚本文件,但是也可以载入与主线程在同一个网页的代码。
<!DOCTYPE html> <body> <script id="worker" type="app/worker"> addEventListener('message', function () { postMessage('some message'); }, false); </script> </body> </html>
上面是一段嵌入网页的脚本,注意必须指定``标签的type
属性是一个浏览器不认识的值,上例是app/worker
。
然后,读取这一段嵌入页面的脚本,用 Worker 来处理。
var blob = new Blob([document.querySelector('#worker').textContent]); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); worker.onmessage = function (e) { // e.data === 'some message' }; ---------------------------------------- function createWorker(f) { var blob = new Blob(['(' + f.toString() + ')()']); var url = window.URL.createObjectURL(blob); var worker = new Worker(url); return worker; }
上面代码中,先将嵌入网页的脚本代码,转成一个二进制对象,然后为这个二进制对象生成 URL,再让 Worker 加载这个 URL。这样就做到了,主线程和 Worker 的代码都在同一个网页上面。n
Notifications
通知接口用于向用户配置和显示桌面通知。
let notification = new Notification(title, options)
参数
-
title
一定会被显示的通知标题
-
options
可选一个被允许用来设置通知的对象。它包含以下属性:
dir
: 文字的方向;它的值可以是auto(自动)
,ltr(从左到右)
, orrtl
(从右到左)lang
: 指定通知中所使用的语言。这个字符串必须在 BCP 47 language tag 文档中是有效的。body
: 通知内容。tag
:通知的ID,可以通过ID修改通知内容 ,删除通知内容icon
: 一个图片的URL,将被用于显示通知的图标。image
:作为通知的一部分显示的图像 URL
静态属性
这些属性仅在 Notification
对象上有效。
-
Notification.permission
只读一个用于表明当前通知显示授权状态的字符串。可能的值包括:
denied
(用户拒绝了通知的显示)。granted
(用户允许了通知的显示)。default
(因为不知道用户的选择,所以浏览器的行为与 denied 时相同)。 -
Notification.maxActions
只读设备和用户代理支持的最大的操作数量。
方法
静态方法
这些方法仅在 Notification
对象中有效。
-
Notification.requestPermission()
用于当前页面向用户申请显示通知的权限。这个方法只能被用户行为调用(比如:onclick 事件),并且不能被其他的方式调用。
实例方法
这些方法仅在 Notification
实例或其 prototype
中有效。
-
Notification.close()
用于关闭通知。
事件
click
:当用户点击通知时触发。
close:当用户关闭通知时触发。
error:当通知发生错误时触发。
show:当通知显示时触发。
案例
const request = document.querySelector('#request-role');
const sendMsg = document.querySelector('#send-msg');
request.onclick = function () {
//发起通知权限请求
Notification.requestPermission()
}
sendMsg.onclick = function () {
let notif = new Notification('海牙的通知', { lan: 'zh-CN', body: '你好,我是海牙,你中了500w大奖,请来欢乐豆大厅兑换', icon: 'images/bird.png' });
notif.onshow = function () {
console.log('通知')
}
notif.onclick = function () {
console.log('用户点击了通知');
}
notif.onclose = function () {
console.log('用户关闭了通知')
}
}
网页特效
分类
- 导航类
- banner类
- 选项卡
- 加载类
实现
一般特效交互用到的事件
- 点击
- 移入移出
- 滚动
- 拖拽(按下 移动 抬起)
- 输入
特效数据变化
- css样式数据变化
- 位置数据变化
- 背景变化
- 显示隐藏
- DOM数据的变化
- 增删改查
注:一般每一个特效功能用一个IIFE包裹实现,避免变量污染,安全。
案例
楼梯导航
使用jquery
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>楼梯导航</title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
}
.w980 {
width: 980px;
margin-right: auto;
margin-left: auto;
}
header,
footer {
width: 980px;
height: 220px;
background-color: #ccc;
}
section {
width: 980px;
margin: 10px 0;
font-size: 50px;
text-align: center;
color: #fff;
line-height: 300px;
}
section:nth-of-type(odd) {
height: 400px;
background-color: rgb(23, 102, 175);
}
section:nth-of-type(even) {
height: 380px;
background-color: rgb(228, 105, 35);
}
.slide-nav {
position: absolute;
top: 220px;
left: 50%;
width: 46px;
height: 400px;
margin: auto;
margin-left: 500px;
padding: 20px 0;
box-shadow: 0 0 1px #333;
border-radius: 8px;
}
.slide-nav li {
user-select: none;
padding: 10px 4px;
text-align: center;
font-size: 14px;
color: #505050;
cursor: pointer;
}
.slide-nav li:hover {
background-color: #00a1d6;
color: #fff
}
.slide-nav li.active {
background-color: #00a1d6;
color: #fff
}
</style>
</head>
<body>
<header class="w980">header</header>
<section class="w980">动画</section>
<section class="w980">游戏</section>
<section class="w980">音乐</section>
<section class="w980" style="height: 1000px;">知识</section>
<section class="w980">国创</section>
<section class="w980">漫画</section>
<section class="w980">科技</section>
<section class="w980">户外</section>
<section class="w980">记录</section>
<div class="slide-nav">
<ul>
<li class="active">动画</li>
<li>游戏</li>
<li>音乐</li>
<li>知识</li>
<li>国创</li>
<li>漫画</li>
<li>科技</li>
<li>户外</li>
<li>记录</li>
</ul>
</div>
<footer class="w980">footer</footer>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.1/jquery.min.js"></script>
<script>
//楼梯导航特效
(function () {
let $slideNav = $('.slide-nav');
let $section = $('section');
let hH = $('header').outerHeight();
let idx = 0;
/*
1. 吸顶跟随效果 滚动高度>header高度的时候 变为fixed 否则是absolute
2. 跟随内容区块指示高亮
1. 标准是 section是否能在windowView中完全展示
section.offset().top => 200
section.outerHeight() => 380
window.scrollTop => 250
window.innerHeight = 600
1. $section.offset().top >= $window.scrollTop &&
($section.offset().top + $section.outerHeight()) < ($window.scrollTop + window.innerHeight)
2. $section.offset().top <= $window.scrollTop && ($section.offset().top + $section.outerHeight()) >= ($window.scrollTop + window.innerHeight)
//$section.offset().top + $section.outerHeight() > $window.scrollTop
3. 点击导航索引跳转窗滚动至对应区域
*/
$slideNav.find('li').click(function () {
idx = $(this).index();
let top = $section.eq(idx).offset().top - $section.eq(idx).outerHeight() / 3;
$('html,body').animate({
scrollTop: top
}, 500)
})
$(window).scroll(function (e) {
let scrollTop = $(window).scrollTop();
toFixed(scrollTop);
setSlideIdx(scrollTop);
});
function setSlideIdx(scrollTop) {
//当当前section的三分之一高处 < 滚动高度的时候
if ($section.eq(idx).offset().top + ($section.eq(idx).outerHeight() / 3) < scrollTop) {
idx++;
}
//当当前section的顶部位于页面中线下方的时候
if ($section.eq(idx).offset().top > scrollTop + window.innerHeight / 2) {
idx--;
}
idx = Math.max(0, Math.min($section.length, idx));
$slideNav.find('li').eq(idx).addClass('active').siblings().removeClass('active');
}
function toFixed(scrollTop) {
if (scrollTop < hH) {
$slideNav.css({
position: 'absolute',
top: hH + 'px'
})
return false;
}
$slideNav.css({
position: 'fixed',
top: '30%',
})
}
})();
</script>
</body>
</html>