H5新特性
- geolocation 地理定位,获取用户设备的经纬度
- video,audio 视、音频 WebSocket推送video , canvas+video 视频上做图像处理,如特效
- canvas 绘图
- WebSocket 双工通信
- localStorage 本地存储,代替cookie,cookie只能存储4k,而localStorage能存储5M
- sessionStorage 关闭浏览器后就会消息,用于存储敏感信息,容量=5M,用的少
- WebSQL 、IndexedDB 前端数据库,不安全,用的少(目前取消了)
- WebWorker 把JS从单线程变成多线程(单线程,一次只能做一件事),使用场景缺少
- 文件操作 拖拽、读取
- manifest 离线应用文件,决定离线缓存什么文件;不实用,被APP取代;
- …
- …
geolocation
- 多用在M端;
软件并不能实时定位,需要硬件支撑。
- PC与M端的定位方式不同;
M端使用GPS;PC使用IP地址:精度低;IP经常会变,需要一个IP库(网上搜有,如ip.taobao.com)
window.navigator.geolocation 只问一次 单次:getCurrentPosition(成功,失败,参数) 获取当前位置 监听:watchPosition(成功,失败,参数) 在Ghrome浏览器上,它会向Google发出请求获取结果,通常情况下会失败;
- 高精度定位,需要使用框架
- enableHighAccuracy //高精度模式,慢、费电
- timeout //超时时间,ms 单位
- maximumAge //缓存获取位置的结果
结果:
- accuracy 精确度
- altitudeAccuracy 高度精确度
- heading 朝向
- speed 速度,不太准;可以使用计算(前一次与这次)获取的经纬度计算运动距离与时间差,获取速度
video && audio
- 兼容video:
<video> <object type ="" params=""> </object> //flash </video>
- 支持格式(不同浏览器不同,版权原因)IE:wmv、mp4;Chrome: webq、mp4;FireFox:ogv、mp4
- 点播:防止视频被抓取 src:blob
- 可以用js 操作video、audio
- 进度:parseInt(100*v.currentTime/v.duration)+’%’
- autoplay 在很多浏览器会被自动禁用,但可以使用js 进行控制
- 播放速度 1:1,1:1.05… 方法1:压制成不同版本的文件、方法2:点播时,调整码率;都需要服务器处理;自带属性playbackRate
- 宽高适配:先判断比例
如适配W=600,H=400的容器
w = 600 H = 400 if(w/h>W/H){ //偏宽 //让宽度适应容器宽度 h = H*w/W w = w } else { h = H w = W*h/H }
- 试看:不能用客户端实现;使用服务器控制:服务器生成两段视频,如没登陆的用户生成试看;登陆的生成完整的;
- 如何去掉video/audio 自带的control 的下载按钮:使用流视频文件;
Web Workers 浏览器上实现的多进程
主机 > 程序 > 进程 > 线程 > 纤程
- 多进程与多线程
多进程:性能低、编写简单;进程之间是隔离的;共享资源麻烦;开销大;
多线程:性能高、编写复杂;线程之间是共享的;共享资源方便;开销小;
多任务:多个程序执行
java \ C 多进程、多线程
PHP 多进程
JS 多进程 - 主进程(UI进程):只有一个。
- 子进程(工作进程) :只能完成计算、数据请求等操作。
每个Web Workers都有自己的全局运行环境,其功能只是Javascript特性的一个子集。Worker运行环境由如下部分组成:
- 一个
navigator对象
,只包括四个属性
:appName、appVersion、user Agent 、platform
- 一个
location对象
,与window.location相同,不过其所有属性是只读的
- 一个
self
对象,指向全局worker
对象 - 一个
importScripts()
方法,加载Worker所用到的外部Javascript文件 - 所有的ECMAScript对象,如:
Object、Array、Date
等 XMLHTTPRequest
构造器setTimeout()和setInterval()
方法- 一个
close()
方法,能立刻停止Worker运行
//1、创建子进程 let w = new Worker('w1.js');//不能跨域 //2 发送 w.postMessage({}); //w1.js //3、接收 //4、处理任务 //5、返回 //6、接收结果
- 多个进程能同时工作,充分利用资源
- 防止主进程卡机
- 不能执行任何UI操作;子进程只能执行计算型任务;
- Web Workers在工作中用的很少——Web中计算型任务不多;
我们知道,js是单线程运行的,这个特点可能会造成当某个操作特别耗时的时候,页面出现崩溃或无响应的状态。
Web Workers 能够赋予js多线程的能力,实质上是开启一个Web Workers线程,用于处理这些耗时的计算。
下面,通过一个示例说明——计算斐波那契数列:
知识:斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368…这个数列从第3项开始,每一项都等于前两项之和。
在数学上,斐波纳契数列以如下被以递推的方法定义:
F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)
下面,我们通过Web Workers 计算它。
我们将线程分为UI线程和Web Workers线程:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width= , initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>web workers</title> </head> <body> <div class="calc"> <input type="text" id="text" /> <input type="button" value="获取结果" id="btnCal"/> </div> <p>结果:</p> <p class="result" id="result"></p> <script> //UI 线程,用以处理DOM操作 var input = document.getElementById("text"), cal = document.getElementById("btnCal"), result = document.getElementById("result"); var getResult = function(){ var initVal = input.value; var w = new Worker("./worker.js"); //创建web worker线程 w.postMessage(initVal); //向web worker 发送消息 w.addEventListener("message",function(event){ //接收Web workers 线程传回的消息 result.innerHTML = result.innerHTML + initVal + "斐波那契数列:"+event.data ; },false) } cal.addEventListener("click",getResult,false); </script> </body> </html>
web workers线程 worker.js
//web workers线程 ,用以处理耗时计算操作 function fibonacci(n){ return n<2 ? n : fibonacci(n-1) + fibonacci(n-2); } this.addEventListener("message",function(event){ //接收UI线程传递过来的消息 var resultVal = fibonacci(event.data);//计算对应的值 this.postMessage(resultVal); //将计算结果发送给UI线程 })
从上述,我们知道,Web Workers适合处理纯数据,或者与浏览器UI无关的长时间运行脚本。
我们在来看一个例子:用Web Workers 处理大的JSON字符串:
假设这个JSON字符串的数据量足够大,至少需要500ms才能解析完成,至于干扰了用户体验。
var worker = new Worker("jsonparse.js"); //数据就位时,调用事件处理器 worker.onmessage = function(event) { var jsonData = event.data;//JSON结构 evaluateDate(jsonData);//使用JSON结构 } worker.postMessage(jsonText);//传入要解析的大段JSON字符串
- web workers 脚本:jsonparse.js
//当JSON数据存在时,该事件处理器会被调用 self.onmessage = function(event) { //JSON字符串由event.data传入 var jsonText = event.data; //解析JSON字符串 var jsonData = JSON.parse(jsonText); //回传解析结果 self.postMessage(jsonData); }
拖拽
<div id="div1">将文件拖拽至此区域</div> window.onload = function(){ let oDiv = document.getElementById('div1'); //ondragenter 进入 //ondragleave 离开 //ondragover 悬停 //ondrop 松开鼠标,如果dragover 不阻止默认事件,drop事件不会发生 oDiv.addEventListener('dragenter',function() { oDiv.innerHTML = '释放鼠标'; },false); oDiv.addEventListener('dragleave',function() { oDiv.innerHTML = '将文件拖放至此区域'; },false); oDiv.addEventListener('dragover',function(e) { e.preventDefault(); //阻止默认事件 },false); oDiv.addEventListener('drop',function(e) { console.log(e.dataTransfer.files) e.preventDefault(); },false); }
富文本编辑器
- contenteditable H5属性
- tinymce 编辑器
FileReader
let reader = new FileReader();
- reader.onload = function(){}
- reader.οnerrοr= function(){}
- reader.readAsText(param) //文本 --适合文本文件
- reader.readAsDataURL(param) //base64 – 适合图片
- reader.readAsArrayBuffer(param) //把文件内容保存为字节,原始的二进制数据 – 适合编辑,不实用
- reader.readAsBinaryString(param) //把二进制数据转为字符串 – 适合上传
canvas 画布
- 什么东西都能画
- 宽高必须用属性的方式写
- 性能很高——适合大型动画、游戏
- getContext() 图像上下文,绘图接口
- 路径操作:一个范围,没有图形 moveTo LineTo
- 描边、填充、颜色 stroke strokeStyle fill fillStyle
- 清除之前所有的路径 beginPath
- 闭合路径 closePath
- gd.rect(x,y,w,h) //矩形路径
- gd.arc(cx,cy,r,startAng,endAng,是否逆时针) 弧路径 区分角度(度)与弧度(PI),角度到弧度的换算:360度=2PI弧度
<canvas id="c1" width="100" height="100"> 浏览器不支持canvas </canvas> window.onload = function(){ let oC = document.getElementById('c1'); let gd = oC.getContext('2d');//获取图形上下文,绘图用的接口(环境) //路径操作---**与PS中的选区一样,框定一个操作的范围** gd.moveTo(100,100); gd.lineTo(300,200); gd.lineTo(130,350); gd.closePath(); //封口、闭合 //描边 gd.stroke(); }
- 画多条线
画之前先清除之前所有的路径
gd.beginPath();
- 鼠标画图
<canvas id="c1" width="100" height="100"> 浏览器不支持canvas </canvas> window.onload = function(){ let oC = document.getElementById('c1'); let gd = oC.getContext('2d'); let lastX,lastY; oC.onmousedown = function(ev){ lastX = ev.offsetX; //当前位置 lsstY = ev.offsetY; oC.onmousemove = functioin(ev) { gd.beginPath(); gd.moveTo(lastX,lastY); gd.lineTo(ev.offsetX,ev.offsetY); lastX=ev.offsetX; //前一次位置 lastY=ev.offsetY; gd.stoke(); }, oC.onmouseup = function() { oC.onmousemove = null; oC.onmouseup = null; } } }
- 画饼图
- 1、数据
- 2、总和
- 3、数据占比->转为 角度占比
- 4、画–以上一个pie的结束角作为下一个pie的开始
function pie(startAng,endAng,color){ gd.beginPath(); let data= [100,300,200,150]; let colors = ['#000','#ccc','red','yellow'] let sum = data.reduce((tmp,item,index)=>tmp+item); let angs = data.map(item=>360*item/sum); let last = 0; angs.forEach(ang=>{ pie(last,last+ang,colors[index]); last+=ang; }) }
- 如何让图形动起来(联系gif动画,帧)
1、canvas里的图形,只要画完了,就不能修改,因为canvas不会保存任何的图形信息;
2、gd.clearRect(x,y,w,h) 擦除一块区域,一般擦去整个画布:
gd.clearRect(0,0,oC.width,oCheight) - 如何给图形增加事件
1、canvas本身没有事件
2、canvas中的事件操作要自己定义;
let 1=50,t=50,w=100,h=70; gd.strokeRect(l,t,w,h); oC.onclick = function(ev){//确定鼠标点击在区域内 if(ev.offsetX>=1 && ev.offsetX<=l+w &&ev.offsetY>=t && ev.offsetY<=t+h){ }else {} }
- //计算点至圆心的距离
oC.mousemove = function(ev){ let a = ev.offsetX - cx; //横向距离 let b = ev.offsetY - cy; //纵向距离 let dis = Math.sqrt(a*a+b*b); //实际距离,利用勾股定理 gd.beginPath(); gd.arc(cx,cy,r,0,2*Math.PI,true); if(dis<=r){ //距离小于半径 gd.fillStyle = 'yellow'; }else {//距离大于半径 gd.fillStyle = '#ccc'; } gd.fill(); }
- 碰撞检测:矩形、圆形
- 高维检测
- canvas变换 rotate translate scale
使用变换之前,先save()后restore()
角度转弧度/弧度转角度
function d2a(n){ return n*Math.PI/180; } function a2d(n){ return n*180/Math.PI; } window.onload = function(){ let oC = document.getElementById('c1'); let gd = oC.getContext('2d'); let rotate = 0; gd.strokeRect(100,100,300,200); setInterval(function(){ gd.clearRect(0,0,oC.width,oC.height); rotate++; gd.save(); //保存当前canvas的状态,如颜色、线宽、旋转、translate,但不包括图形本身 gd.translate(250,200); //先translate再rotate gd.rotate(d2a(30)); //旋转单位:弧度;旋转以画布左上角为中心点旋转,而不是图形的中心 gd.strokeRect(-150,-100,300,200); gd.restore();//恢复状态 },16); }
- canvas中处理图片
<canvas id="c1" width="800" height="600"></canvas> window.onload = function(){ let oC = document.getElementById('c1'); let gd = oC.getContext('2d'); let oImg = new Image(); oImg.src = 'logo.png'; oImg.onload = function() {//需要等到img加载完成才能drawImage gd.clearRect(0,0,oC.width,oC.height); //1、简易版 3个参数 //drawImage(img,x,y); //img要是真正的图片 gd.drawImage(oImg,0,0); //2、完整版 9个参数,**适合做帧动画** /*drawImage( img, sx,sy,sw,sh, s-source 源 :从哪取 dx,dy,dw,dh d-desitination 目标 :放到哪 ) gd.drawImage( oImg, 50,50,32,32, 200,200,32,32 ) */ } }
- canvas 像素级操作
每个像素占4位 : r - g - b - a
window.onload = function(){ let oC = document.getElementById('c1'); let gd = oC.getContext('2d'); let oImg = new Image(); oImg.src= '1.jpg'; oImg.onload = function(){ gd.drawImage(oImg,0,0); //获取一块像素区 let imageData = gd.getImageData(0,0,oC.width,oC.height); //一个数组,每个像素的信息 imageData.data alert( r: ${imageData.data[0]}\n g: ${imageData.data[1]}\n b: ${imageData.data[2]}\n a: ${imageData.data[3]}\n ) //**操作像素点的颜色** for(let r=0;r<oC.height;r++){ for(let c=0;c<oC.width;c++){ let avg = (imageData.data[(r*oC.width+c)*4]+imageData.data[(r*oC.width+c)*4+1]+imageData.data[(r*oC.width+c)*4+2])/3; imageData.data[(r*oC.width+c)*4]+imageData.data[(r*oC.width+c)*4+1]+imageData.data[(r*oC.width+c)*4+2] = avg; } } gd.putImageData(imageData,0,0); } }
若有800列 W)600行(H)的数组像素,要获取r行c列:
- arr[(r*800+c)*4]
- arr[(r*W+c)*4]