HTML5 中的音频/视频,canvas,地理定位,拖放,WebSocket,WebWorker,SVG
音频与视频-
音频视频标签
音频标签
浏览器支持的音频格式:MP3
, wav
, ogg
简写方式:
<audio src="音频文件路径" controls></audio>
标准方式:
<audio controls> <source src="letitgo.mp3" type="audio/mpeg"/> <source src="letitgo.ogg" type="audio/ogg"/> ..... 什么破浏览器,建议你下一个吧。 </audio>
常用属性
<audio controls // 是否在网页中添加音频播放控件 boolean autoplay // 是否在标签加载后自动播放 boolean muted // 是否设置音频静音 boolean loop // 是否单曲循环播放 boolean preload // 预加载 ></audio>
preload
属性用于设置音频的加载方式,可选值有:
none
: 不进行任何音频的预加载
auto
: 尽可能的加载音频资源
metadata
: 只加载元数据(音频的总时长、格式、视频的宽度、高度等...)
视频标签
<video src="...mp4" controls></video>
<video src="...mp4" controls></video> <video src="" controls autoplay muted loop preload="auto" poster="../xxx.jpg" // 设置视频的海报帧图片路径 width="640" // 播放器的宽度 height="320" // 播放器的高度 ></video>
<audio>, <video>标签的DOM操作
<audio>, <video>标签的DOM操作
与媒体相关的DOM对象包含:
- HTMLMediaElement
- HTMLAudioElement
- HTMLVideoElement
HTMLMediaElement接口是HTMLAudioElement与HTMLVideoElement的父接口。
HTMLAudioElement
Audio 对象属性可以参考网址https://www.w3school.com.cn/tags/html_ref_audio_video_dom.asp
Audio 对象方法 可以参考网址https://www.w3school.com.cn/tags/html_ref_audio_video_dom.asp
Media 事件 可以参考网址https://www.w3school.com.cn/tags/html_ref_audio_video_dom.asp
HTMLVideoElement
HTMLAudioElement 拥有的属性与方法 绝大多数 HTMLVideoElement也都可用,除此之外,HTMLVideoElement还提供一些特殊属性:
HTMLAudioElement 拥有的属性与方法 绝大多数 HTMLVideoElement也都可用,除此之外,HTMLVideoElement还提供一些特殊属性:
属性名 |
介绍 |
width |
宽度 |
videoWidth |
视频宽度(只读) |
height |
高度 |
videoHeight |
视频高度(只读) |
poster |
海报帧图片路径 |
全屏API
HTML5提供了可以让任何HTML元素及其子元素占满整个屏幕的API。
请求全屏显示
dom对象.requestFullscreen() //让dom对象全屏显示 ESC键退出全屏
退出全屏:(esc键也可以)
document.exitFullscreen()
video示例代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>03_videoplayer.html</title> <style> .container { width: 400px; border: 1px solid #ddd; text-align: center; padding-bottom: 10px; } .container p{ font-size: 1.3em; font-weight: bold; text-align: center; } .container img{ width: 340px; height: 340px; border-radius: 50%; } .container input{ width: 340px; display: block; margin: 10px auto; } .container .time{ width: 340px; height: 30px; margin: 0px auto; } .container .time .left{ float: left; } .container .time .right{ float: right; } </style> </head> <body> <div class="container"> <video src="assets/let_it_go.mp4" id="video" width="640" height="360" style="background-color: #000;"></video> <input id="range" type="range" min="0" value="0" max="100"> <div class="time"> <span class="left" id="left">00:00</span> <span class="right" id="right">00:00</span> </div> <button id="btn_play">播放/暂停</button> <button id="btn_vp">音量+</button> <button id="btn_vm">音量-</button> <button id="btn_05">0.5倍速</button> <button id="btn_1">1倍速</button> <button id="btn_2">2倍速</button> <button id="btn_fc">全屏显示</button> </div> <script src="assets/moment.js"></script> <script> let player = document.getElementById('video'); // let btnFc = document.getElementById('btn_fc'); btnFc.addEventListener('click', ()=>{ // player.requestFullscreen(); let c = document.getElementsByClassName('container')[0]; c.requestFullscreen(); }) player.addEventListener('loadedmetadata', ()=>{ let total = moment(player.duration*1000).format('mm:ss'); document.getElementById('right').innerHTML = total; }) // 实现播放、暂停业务 let btnPlay = document.getElementById('btn_play'); // 为开始按钮绑定事件 btnPlay.addEventListener('click', ()=>{ if (player.paused){ player.play() }else{ player.pause() } }); // 为控制音量按钮绑定事件 let btnp = document.getElementById('btn_vp'); let btnm = document.getElementById('btn_vm'); btnm.addEventListener('click', ()=>{ // 音量- player.volume = Math.max(player.volume-0.1, 0) console.log(player.volume) }) btnp.addEventListener('click', ()=>{ // 音量+ player.volume = Math.min(player.volume+0.1, 1) console.log(player.volume) }) // 修改音频倍速 let btn05 = document.getElementById('btn_05'); let btn1 = document.getElementById('btn_1'); let btn2 = document.getElementById('btn_2'); btn05.addEventListener('click', ()=>{ player.playbackRate = 0.5 }) btn1.addEventListener('click', ()=>{ player.playbackRate = 1 }) btn2.addEventListener('click', ()=>{ player.playbackRate = 2 }) // 为player绑定事件 timeupdate player.addEventListener('timeupdate', ()=>{ // console.log('timeupdate...') // 更新进度条 let range = document.getElementById('range'); range.max = player.duration // 总时长 (秒) range.value = player.currentTime // 当前播放位置 (秒) // 更新span 时间字符串 moment(毫秒值时间戳) let total = moment(player.duration*1000).format('mm:ss'); document.getElementById('right').innerHTML = total; let ct = moment(player.currentTime*1000).format('mm:ss'); document.getElementById('left').innerHTML = ct; }) // 为range绑定change事件,更新音乐播放进度 let range = document.getElementById('range'); range.addEventListener('change', ()=>{ let val = range.value player.currentTime = val }) </script> </body> </html>
Audio示例代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .container { width: 400px; border: 1px solid #ddd; text-align: center; padding-bottom: 10px; } .container p{ font-size: 1.3em; font-weight: bold; text-align: center; } .container img{ width: 340px; height: 340px; border-radius: 50%; } .container input{ width: 340px; display: block; margin: 10px auto; } .container .time{ width: 340px; height: 30px; margin: 0px auto; } .container .time .left{ float: left; } .container .time .right{ float: right; } </style> </head> <body> <div class="container"> <p>let it go</p> <img src="assets/logo.jpg" alt=""> <input id="range" type="range" min="0" value="0" max="100"> <div class="time"> <span class="left" id="left">00:00</span> <span class="right" id="right">00:00</span> </div> <button id="btn_play">播放/暂停</button> <button id="btn_vp">音量+</button> <button id="btn_vm">音量-</button> <button id="btn_05">0.5倍速</button> <button id="btn_1">1倍速</button> <button id="btn_2">2倍速</button> </div> <script src="assets/moment.js"></script> <script> let player = new Audio(); // 创建Audio对象 player.src = 'assets/let_it_go.mp3' // 设置数据源 // 实现播放、暂停业务 let btnPlay = document.getElementById('btn_play'); // 为开始按钮绑定事件 btnPlay.addEventListener('click', ()=>{ if (player.paused){ player.play() }else{ player.pause() } }); // 为控制音量按钮绑定事件 let btnp = document.getElementById('btn_vp'); let btnm = document.getElementById('btn_vm'); btnm.addEventListener('click', ()=>{ // 音量- player.volume = Math.max(player.volume-0.1, 0) console.log(player.volume) }) btnp.addEventListener('click', ()=>{ // 音量+ player.volume = Math.min(player.volume+0.1, 1) console.log(player.volume) }) // 修改音频倍速 let btn05 = document.getElementById('btn_05'); let btn1 = document.getElementById('btn_1'); let btn2 = document.getElementById('btn_2'); btn05.addEventListener('click', ()=>{ player.playbackRate = 0.5 }) btn1.addEventListener('click', ()=>{ player.playbackRate = 1 }) btn2.addEventListener('click', ()=>{ player.playbackRate = 2 }) // 为player绑定事件 timeupdate player.addEventListener('timeupdate', ()=>{ // console.log('timeupdate...') // 更新进度条 let range = document.getElementById('range'); range.max = player.duration // 总时长 (秒) range.value = player.currentTime // 当前播放位置 (秒) // 更新span 时间字符串 moment(毫秒值时间戳) let total = moment(player.duration*1000).format('mm:ss'); document.getElementById('right').innerHTML = total; let ct = moment(player.currentTime*1000).format('mm:ss'); document.getElementById('left').innerHTML = ct; }) // 为range绑定change事件,更新音乐播放进度 let range = document.getElementById('range'); range.addEventListener('change', ()=>{ let val = range.value player.currentTime = val }) </script> </body> </html>
二 canvas简单介绍
canvas(画布)是可以使用Javascript来绘制图形的html元素,其语法结构:
<canvas id="" width="宽度" height="高度"></canvas>
width: 定义图像的宽度
height:定义图像的高度
width与height 设置的是canvas图像真实的的宽度与高度。 css的width与height 设置的是页面中显示的宽度与高度。如果二者不相同,有可能出现图像失真现象。
canvas的使用
基本使用步骤:
let cvs = document.getElementById('canvas') let ctx = cvs.getContext('2d') // 获取用于绘制canvas的上下文对象 ctx.fillRect() // ctx提供了一些API方法用于绘制canvas ctx.xxxxx() canvas元素的方法: let ctx = canvas.getContext(contextType)
contextType: 上下文类型
- '2d' 将会返回CanvasRenderingContext2D对象,用于渲染二维图像。
- 'webgl' 将会返回WebGLRenderingContext对象,用于渲染三维图像。
canvas跟多属性及用法https://www.w3school.com.cn/tags/html_ref_canvas.asp
canvas实现 移动端触摸画画示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>03_canvas_board.html</title> <style> body {padding: 0px;} canvas { width: 640px; height: 330px; border: 1px solid black; background-color: antiquewhite; display: block; margin: 0 auto; } </style> </head> <body> <canvas id='canvas' width="640" height="330"></canvas> <script> let cvs = document.getElementById('canvas'); let ctx = cvs.getContext('2d'); // 为cvs对象绑定touch事件 cvs.addEventListener("touchstart", (event)=>{ // console.log(event) // 由于绘制坐标与事件坐标有偏移量left与top,所以需要经过计算 // 抵消这些偏移量,才可以正常绘制图像 let left = cvs.getBoundingClientRect().left; let top = cvs.getBoundingClientRect().top; let x = event.touches[0].pageX - left; let y = event.touches[0].pageY - top; console.log(x, y) // 开始触摸时,开启一个新的路径 ctx.beginPath(); ctx.moveTo(x, y); }) // 为cvs绑定touchmove事件,用于绘制路径 cvs.addEventListener("touchmove", (event)=>{ let left = cvs.getBoundingClientRect().left; let top = cvs.getBoundingClientRect().top; let x = event.touches[0].pageX - left; let y = event.touches[0].pageY - top; ctx.lineTo(x, y); ctx.strokeStyle = 'red'; ctx.stroke(); event.preventDefault(); event.stopPropagation(); }) </script> </body> </html>
结合canvas和video生成弹幕效果示例代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>03_videoplayer.html</title> <style> .container { width: 400px; border: 1px solid #ddd; text-align: center; padding-bottom: 10px; position: relative; } .container p{ font-size: 1.3em; font-weight: bold; text-align: center; } .container img{ width: 340px; height: 340px; border-radius: 50%; } .container input{ width: 340px; display: block; margin: 10px auto; } .container .time{ width: 340px; height: 30px; margin: 0px auto; } .container .time .left{ float: left; } .container .time .right{ float: right; } </style> </head> <body> <div class="container"> <video src="assets/let_it_go.mp4" id="video" width="640" height="360" style="background-color: #000;"></video> <!-- 添加canvas元素 --> <canvas id='canvas' width="640" height="360" style="position: absolute; top: 0; left: 0;"> </canvas> <input id="range" type="range" min="0" value="0" max="100"> <div class="time"> <span class="left" id="left">00:00</span> <span class="right" id="right">00:00</span> </div> <button id="btn_play">播放/暂停</button> <button id="btn_vp">音量+</button> <button id="btn_vm">音量-</button> <button id="btn_05">0.5倍速</button> <button id="btn_1">1倍速</button> <button id="btn_2">2倍速</button> <button id="btn_fc">全屏显示</button> <!-- 弹幕相关设置 --> <input type="text" id="dminput" > <button id="btn_send">发送弹幕</button> </div> <script src="assets/moment.js"></script> <script> let dmlist = []; let btnSend = document.getElementById('btn_send'); btnSend.addEventListener('click', ()=>{ let val = document.getElementById('dminput').value; // 向dmlist追加一个dm对象 {text:'xxx', x:1, y:2} dmlist.push({text:val, x:600, y:Math.random()*360}); console.log(dmlist); }) // 启动一个定时器,每秒60帧,不断绘制dmlist中的文本。 let cvs = document.getElementById('canvas'); let ctx = cvs.getContext('2d'); //实现弹幕效果 function draw(){ // 耗时代码 dmlist.forEach(item=>{ item.x--; }) // 请求动画帧,使浏览器下次更新时,顺便画一下界面。 window.requestAnimationFrame(()=>{ ctx.clearRect(0, 0, 640, 360); dmlist.forEach(item=>{ ctx.font = '16px 微软雅黑' ctx.fillStyle = 'white' ctx.fillText(item.text, item.x, item.y) }) }); setTimeout(draw, 1000/60) } draw() // window.setInterval(() => { // // 清空屏幕 // ctx.clearRect(0, 0, 640, 360); // // 把dmlist中的每个弹幕拿出来,在canvas中进行绘制 // dmlist.forEach(item =>{ //item => {text:'xxx', x:1, y:2} // item.x--; // ctx.font = '16px 微软雅黑' // ctx.fillStyle = 'white' // ctx.fillText(item.text, item.x, item.y) // }) // }, 1000/60); let player = document.getElementById('video'); // let btnFc = document.getElementById('btn_fc'); btnFc.addEventListener('click', ()=>{ // player.requestFullscreen(); let c = document.getElementsByClassName('container')[0]; c.requestFullscreen(); }) player.addEventListener('loadedmetadata', ()=>{ let total = moment(player.duration*1000).format('mm:ss'); document.getElementById('right').innerHTML = total; }) // 实现播放、暂停业务 let btnPlay = document.getElementById('btn_play'); // 为开始按钮绑定事件 btnPlay.addEventListener('click', ()=>{ if (player.paused){ player.play() }else{ player.pause() } }); // 为控制音量按钮绑定事件 let btnp = document.getElementById('btn_vp'); let btnm = document.getElementById('btn_vm'); btnm.addEventListener('click', ()=>{ // 音量- player.volume = Math.max(player.volume-0.1, 0) console.log(player.volume) }) btnp.addEventListener('click', ()=>{ // 音量+ player.volume = Math.min(player.volume+0.1, 1) console.log(player.volume) }) // 修改音频倍速 let btn05 = document.getElementById('btn_05'); let btn1 = document.getElementById('btn_1'); let btn2 = document.getElementById('btn_2'); btn05.addEventListener('click', ()=>{ player.playbackRate = 0.5 }) btn1.addEventListener('click', ()=>{ player.playbackRate = 1 }) btn2.addEventListener('click', ()=>{ player.playbackRate = 2 }) // 为player绑定事件 timeupdate player.addEventListener('timeupdate', ()=>{ // console.log('timeupdate...') // 更新进度条 let range = document.getElementById('range'); range.max = player.duration // 总时长 (秒) range.value = player.currentTime // 当前播放位置 (秒) // 更新span 时间字符串 moment(毫秒值时间戳) let total = moment(player.duration*1000).format('mm:ss'); document.getElementById('right').innerHTML = total; let ct = moment(player.currentTime*1000).format('mm:ss'); document.getElementById('left').innerHTML = ct; }) // 为range绑定change事件,更新音乐播放进度 let range = document.getElementById('range'); range.addEventListener('change', ()=>{ let val = range.value player.currentTime = val }) </script> </body> </html>
解决canvas动画卡顿办法:
动画的实现原理即每隔一段时间(非常快 1/60秒)重绘canvas。每次绘制时,内容都会有些许变化,由于视觉残留现象,实现动画。
window.setInterval(()=>{
绘制UI
}, 1000/60)
动画卡顿的核心原因: 掉帧
解决原理:利用浏览器渲染画布动画
方法:`window.requestAnimationFrame()`
`requestAnimationFrame(callback)`方法用于请求绘制动画帧(向浏览器发请求),实现定时绘制`UI`的操作。
基于`requestAnimationFrame()`实现动画的代码基本结构:
function draw(){ // 耗时代码 比如for循环 更新属性等等 xxxxxxxx xxxxxxxx window.requestAnimationFrame(()=>{ // 绘制UI 不在此做耗时操作 }) setTimeout(draw, 1000/60) } draw()
上面的代码示例中有用到此方法
三 地理定位
在页面调用地图定位:
定位的基础和原理:
1,ip定位:通过设备的IP地址来确定其地理位置
2,运营商基站定位:利用基站对手机的距离的测算距离来确定手机位置的。需要手机具有GPS定位能力,但是精度很大程度依赖于基站的分布及覆盖范围的大小,有时误差会超过一公里
3,GPS定位:利用手机上的GPS定位模块将自己的位置信号发送到定位后台来实现手机定位的,定位精度较高
地理定位的实现
地理定位`API`允许用户向`web`应用程序提供他们的位置,但是这个操作需要用户授权。
获取地理定位的相关代码如下:
//请求定位权限
geolocation = window.navigator.geolocation //获取定位信息 geolocation.getCurrentPosition((result)=>{ console.log(result) })
从`chrome50`开始,地理定位只能适用于`https`协议的网站。所以`http://127.0.0.1:5500`虽然已经获取权限,但是浏览器不信任,没有返回位置信息。
可以使用`chrome`自带的`sensors`来模拟定位。
如果定位成功,`getCurrentLocation`将会在`success`回调函数中传出定位对象:`GeolocationPosition`对象。
geolocation = window.navigator.geolocation geolocation.getCurrentPosition((result)=>{ console.log(result) })
result 即是`GeolocationPosition`实例。
timestamp属性
返回获取位置是的时间戳
coords属性
返回GeolocationCoordinates对象,封装了一些坐标位置属性:
longitude 经度
latitude 纬度
altitude 海拔高度
speed 设备的移动速度
accuracy 精准度
关于地理位置的方法可以查看https://www.w3school.com.cn/html5/html_5_geolocation.asp
### 接入第三方位置服务平台 - 百度地图/高德地图
一旦接入了这些第三方平台,可以方便的在自己的网页中嵌入地图控件。引入这些第三方位置服务的`js`库后,还可以方便的操作这些地图。显示地图内容、在地图中添加大头针,还可以通过访问这些`web`服务,实现公交路线查询、驾车路线查询、逆地址解析,位置周边检索。
百度地图`JSAPI`
百度地图`JSAPI`是一套基于`Javascript`开发的应用程序接口,支持`PC`、小程序与移动端地图开发。
**基本使用方法:**
1. 配置百度账号,赋予百度地图`JS`库的使用权限。
2. 页面中提供`div`作为显示地图的容器。
3. 引入百度地图第三方`JS`,初始化地图实例。
4. 完成基础设置,显示地图信息。
配置百度账号和获取AK
登录百度地图,点击地图开放平台链接:
登录百度账号后,点击控制台:
选择应用管理 - 我的应用:
创建应用:
填写应用基本信息:
创建成功:
创建成功的应用将会被分配一个访问应用的AK,以后在发送请求时,需要携带AK一起发送 具体使用方法可以点击官网查看
//1. 引入百度地图js文件。注明:ak
<script type="text/javascript" src="http://api.map.baidu.com/api?v=3.0&ak=您的密钥" />
//2. 准备好一个div,给一个id属性,作为地图的容器。
//3. 初始化地图实例,显示地图内容数据。
let map = new BMap.Map('container');
let point = new BMap.Point(116.396906, 39.920109);
// 把point表示的坐标点作为地图中心,默认缩放级别为15,之后显示地图
map.centerAndZoom(point, 15);
为地图添加控件
http://lbsyun.baidu.com/jsdemo.htm#webgl2_0
为地图添加覆盖物
http://lbsyun.baidu.com/jsdemo.htm#eAddMarker
通过关键字查询周边设施
http://lbsyun.baidu.com/jsdemo.htm#localSearchKey
路线规划
http://lbsyun.baidu.com/jsdemo.htm#sLngLatSearchPath
正、逆地址解析
http://lbsyun.baidu.com/jsdemo.htm#wAddressParseSingle
代码示例
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script type="text/javascript" src="http://api.map.baidu.com/api?v=3.0&ak=您的密匙"></script> <style> #container{ width: 600px; height: 400px; margin: 0 auto; } </style> </head> <body> <!-- 显示地图的容器 --> <div id="container"></div> <!-- 初始化实例显示地图 --> <script> function showMap(loc,lac){ //创建对象 let map = new BMap.Map("container"); let point = new BMap.Point(loc,lac); //point地图中心坐标 15缩放级别 map.centerAndZoom(point,15); map.addControl(new BMap.NavigationControl()); map.addControl(new BMap.ScaleControl()); map.addControl(new BMap.MapTypeControl()); map.enableScrollWheelZoom(); } let gl = window.navigator.geolocation; gl.getCurrentPosition(rel=>{ let lon = rel.coords.longitude; let loc = rel.coords.latitude; showMap(lon,loc); }) </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>01_baidu.html</title> <script type="text/javascript" src="http://api.map.baidu.com/api?v=3.0&ak=您的密匙"> </script> </head> <body> <!-- 显示地图的容器 --> <div id="container" style="width:640px; height:360px;"></div> <!-- 初始化地图实例,显示地图 --> <script> function showMap(long, lat){ //new 代表创建对象 BMap.Map let map = new BMap.Map('container'); let point = new BMap.Point(long, lat); // 把point表示的坐标点作为地图中心,默认缩放级别为15,之后显示地图 map.centerAndZoom(point, 15); // 启用滚轮缩放 map.enableScrollWheelZoom(); // 向百度地图中添加控件 Control map.addControl(new BMap.NavigationControl()) map.addControl(new BMap.ScaleControl()) map.addControl(new BMap.MapTypeControl()) // 通过经纬度 完成逆地址解析 let coder = new BMap.Geocoder(); // let point = new BMap.Point(long, lat) // 坐标点 coder.getLocation(point, result=>{ console.log(result) }) } // 获取当前经纬度,调用showMap方法,显示地图 let gl = window.navigator.geolocation; gl.getCurrentPosition((result)=>{ let long = result.coords.longitude; let lat = result.coords.latitude; showMap(long, lat); }) </script> </body> </html>
四 HTML5 拖放
详细解释可以查看https://www.w3school.com.cn/html5/html_5_draganddrop.asp
拖放
拖放是将对象从一个位置拖拽到另外一个位置的操作,任何的HTML元素都可以进行拖放操作。但为了保证兼容性,建议在拖放的对象上添加属性:draggable="true",一旦包含这个属性,当前元素即将支持拖放操作。
<p draggable="true">xxxx</p>
DragEvent 拖拽相关事件
DragEvent指拖拽相关事件,其继承自MouseEvent和Event接口。
拖拽事件涉及到的事件源有两大类:源对象、目标对象。那么拖拽过程中针对源对象与目标对象都会有一些拖拽相关事件的产生。
代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div{ width: 600px; height: 150px; margin-top: 20px; border: 2px solid black; } </style> </head> <body> <div id="d1"> <img draggable="true" id="p" src="1.jpg" width="100"> </div> <div id="d2"></div> <script> // 为源对象p,绑定拖拽事件(源对象事件) let p = document.getElementById('p') //开始拖拽 p.addEventListener("dragstart", ()=>{ console.log('dragstart...') }) //拖拽 p.addEventListener("drag", ()=>{ // console.log('drag...') }) //拖拽结束 p.addEventListener("dragend", ()=>{ console.log('dragend...') }) // 为目标对象d2 绑定事件 let d2 = document.getElementById('d2'); //拖拽进入 d2.addEventListener('dragenter', ()=>{ console.log('dragenter...') }) //拖拽悬停 d2.addEventListener('dragover', ()=>{ console.log('dragover...') }) //拖拽离开 d2.addEventListener('dragleave', ()=>{ console.log('dragleave...') }) //拖拽放开 d2.addEventListener('drop', (event)=>{ console.log('drop...') event.preventDefault() event.stopPropagation(); }) </script> </body> </html>
源对象事件
dragstart 开始拖拽源对象时触发该事件
drag 源对象拖拽过程中将会触发该事件(持续触发)
dragend 拖拽完毕后释放鼠标时将会触发该事件。
目标对象事件
dragenter 当拖拽源对象进入目标对象时触发。
dragover 当拖拽源对象在目标对象上方时触发。(持续触发)
dragleave 当拖拽源对象离开目标对象时触发。
drop 当拖拽着源对象在目标对象中释放时触发。
注意:
在dragover事件处理函数中需要加入:event.preventDefault() 阻止浏览器默认事件产生,否则不会触发目标对象的drop事件。
firefox浏览器针对图像与链接有默认的拖拽事件处理(打开新页面加载资源)。需要阻止该默认事件产生,所以需要在drop事件中:加入
event.stopPropagation()
event.preventDefault()
拖拽过程中的数据传输
HTML5规定在一次拖拽过程中如果需要进行数据的传输,需要借助DataTransfer对象来完成。
// 源对象 dragstart p.ondragstart = (event)=>{ let dt = event.dataTransfer // 存数据 dt.setData('tagName', 'p'); dt.setData('id', 'p'); dt.setData('innerHTML', p.innerHTML); }
// 目标对象 drop
d2.ondrop = (event)=>{ let dt = event.dataTransfer // 取数据 let tn = dt.getData('tagName') let id = dt.getData('id') let ih = dt.getData('innerHTML') let newE = document.createElement(tn); // 凭空创建p元素 newE.innerHTML = ih; // 为p元素的innHTML属性赋值 d2.appendChild(newE) //把元素追加到d2子元素的末尾。 }
实现简单拖拽的代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>drag_data.html</title> <style> div{ width: 600px; height: 150px; margin-top: 20px; border: 2px solid black; } </style> </head> <body> <div id="d1"> <p draggable="true" id="p">你拖我呀!!</p> </div> <div id="d2"></div> <script> // 为源对象p,绑定拖拽事件(源对象事件) let p = document.getElementById('p') p.addEventListener("dragstart", (event)=>{ console.log('dragstart...') // 想dataTransfer对象中存数据 let dt = event.dataTransfer; dt.setData('name', 'zs'); // 把 标签名、标签innerHTML、属性等零散数据分开传给对方 dt.setData('tagName', 'p'); dt.setData('id', 'p'); dt.setData('innerHTML', p.innerHTML); }) p.addEventListener("drag", ()=>{ // console.log('drag...') }) p.addEventListener("dragend", ()=>{ console.log('dragend...') }) // 为目标对象d2 绑定事件 let d2 = document.getElementById('d2'); d2.addEventListener('dragenter', ()=>{ console.log('dragenter...') }) d2.addEventListener('dragover', (event)=>{ console.log('dragover...') event.preventDefault() }) d2.addEventListener('dragleave', ()=>{ console.log('dragleave...') }) d2.addEventListener('drop', (event)=>{ console.log('drop...') // 取出当时存的数据 let dt = event.dataTransfer; let n = dt.getData('name'); // 只能存字符串 console.log(); // 接收对方传来的标签名、属性、innerHTML等零散数据,拼起来 let tn = dt.getData('tagName') let id = dt.getData('id') let ih = dt.getData('innerHTML') let newE = document.createElement(tn); // 凭空创建p元素 newE.innerHTML = ih; // 为p元素的innHTML属性赋值 d2.appendChild(newE) //把元素追加到d2子元素的末尾。 }) </script> </body> </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> #d1{ width: 700px; height: 200px; background-color: red; } #d2{ margin-top: 30px; width: 600px; height: 300px; background-color: pink; overflow: scroll; } #p{ cursor: pointer; background-color: green; width: 10%; } </style> </head> <body> <div id="d1" draggable="true"> <p draggable="true" id="p" >拽我我我~~~</p> </div> <div id="d2"> </div> <script> let p = document.getElementById('p') p.addEventListener('dragstart',(e)=>{console.log('开始别拽~'); let dt = e.dataTransfer; // dt.setData('name','战士');//只能存字符串 dt.setData('tagname','p');//传递标签名 dt.setData('id','p');//传递id dt.setData('innerHTML',p.innerHTML);//传递标签内容 }) let d2 = document.getElementById('d2'); d2.addEventListener('dragover',(e)=>{ e.preventDefault(); }) d2.addEventListener('drop',(e)=>{ let dt = e.dataTransfer;; let tagname = dt.getData('tagname');//接收标签名 let id = dt.getData('id');//接收id名 let inerh = dt.getData('innerHTML');//接收内容 let newE = document.createElement(tagname);//通过标签创建元素 d2.appendChild(newE);//添加到dom树 document.getElementById('p').remove(); newE.id='p';//通过赋值id得到样式 newE.innerHTML = inerh;//添加元素内容 e.preventDefault(); e.stopPropagation(); }) </script> </body> </html>
实现拖拽文件上传
文件上传流程与协议规范
文件上传流程
- 客户端选择需要上传的文件,点击上传,开始建立连接,准备上传。
- 服务端接收连接请求,完成连接的建立,接收上传的文件(遵守协议)。
- 服务端把接收到的文件数据,存入服务器文件系统(D:/xxx.mp4)。
- 保存完毕后即上传成功。之后浏览器可以通过链接地址访问该资源。
协议规范
- 要求客户端提交文件的请求方式必须为POST。
- 发请求时必须携带消息头:Content-Type: multipart/form-data
- 客户端将本地文件在请求body部分中以数据流的方式传输给服务端。
- 服务端接收客户端传来的数据流,边接收边保存到服务器的磁盘中。
如何获取拖拽文件的信息
当用户把文件拖拽到div中并触发drop事件后,浏览器会拉取选中文件的基本信息,通过dataTransfer对象保存并传输文件数据。在drop的事件处理函数中就可以通过以下代码获取文件信息:
files = event.dataTransfer.files
客户端发送上传文件请求
基于浏览器实现文件上传时,需要注意以下协议规则:
- 必须有表单。
- 表单提交方式得是POST。
- 设置表单属性:
<form method='post' enctype='multipart/form-data'>
- 上传文件需要使用:<input type='file' name='fileA'>
拖拽上传文件建议使用ajax的方式整理上传参数,实现文件上传。
基于axios发送ajax请求,上传文件的基本写法如下:
// 创建表单对象 用于封装参数并且发送ajax请求时传参 let formData = new FormData(); formData.append('name', 'zs') formData.append('age', '18') formData.append('uploadFile', filelist[0]) axios.post('http://xxxxxxxxxx', formData) $.post() $.ajax()
如何访问上传的资源
最基础的访问方法:
- 服务端把upload目录设置为静态托管目录。
- 客户端发送请求时,可以直接访问图片。
如何在用户上传了头像后,可以在用户登录后看到用户上传的头像?
- 上传头像后,把生成的头像文件名存入数据库中,与账号绑定在一起。
- 当下次登录成功后,获取服务端存储的用户的详细信息(包括头像名),直接访问该静态资源即可(将会访问服务端upload目录下的图片)。
客户端代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>drag_upload.html</title> <style> #div { width: 640px; height: 360px; border: 2px solid black; background-color: antiquewhite; text-align: center; font-size: 60px; font-family: '微软雅黑'; line-height: 360px; } </style> </head> <body> <div id='div'> 拖入此处上传文件 </div> <script src="axios.min.js"></script> <script> let div = document.getElementById('div'); div.addEventListener("dragover", (event)=>{ event.preventDefault(); // 阻止默认事件 这样才会触发drop。 }) div.addEventListener('drop', (event)=>{ // 阻止默认行为,否则浏览器将会认为需要打开该文件。 event.preventDefault(); // 获取文件的基本信息 let files = event.dataTransfer.files; console.log(files); // 不能拖拽文件夹 // 实现文件上传 let formData = new FormData(); // 因为服务端将会把字段名叫uploadFile的数据参数当做文件来解析 // 所以,传递的字段名称必须是uploadFile。 // files[0] 是获取拖拽上来的第一个文件 for(let i=0; i<files.length; i++){ formData.append('uploadFile', files[i]) } // 发请求 服务端接收上传的地址为:/upload axios.post('http://127.0.0.1:3000/upload', formData) }) </script> </body> </html>
服务端代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// 加载express模块 const express = require('express') // 创建web服务实例 const server = express() //配置CORS,指定放行的域名,防止出现跨域问题 const cors = require('cors') server.use(cors({ origin:['http://127.0.0.1:5500'] })) //配置multer中间件 const multer = require('multer') obj = multer.diskStorage({ destination : function(req, file, cb){ //指定目录 cb(null, 'upload') }, filename : function (req, file, cb){ // 指定文件名 // console.log(uuid.v1()) // console.log(uuid.v4()) let name = file.originalname // name: abcd.jpg xxxdfdd.zip let ext = name.substr(name.lastIndexOf('.')) cb(null, uuid.v4() + ext) } }) const uploadTools = multer({ storage : obj }) const uuid = require('uuid') // 静态资源托管upload目录 server.use(express.static('upload')) // 指定web服务器的监听端口 server.listen(3000) //接收请求 server.post('/upload', uploadTools.array('uploadFile'), (req, res)=>{ console.log(req.files) res.send('OK') })
五 WebSocket 长连接
websocket可以实现客户端与服务端的数据实时通信。(长连接)
网络通信过程中的长连接与短连接
HTTP协议是基于短连接的协议。 短连接的通信方式属于无状态通信。
什么是websocket?
websocket是一种在单个TCP连接上进行全双工通信(通讯过程允许两个方向上同时完成数据传输)的一种通信协议。是一款长连接协议。
websocket的特点
- 数据格式比较小,通信高效。
- 既可以发送文本,也可以发送二进制数据。
- 协议前缀时ws:// (不是http://)
如何使用代码建立websocket连接并实现通信?
socket.io 更多socket.io信息 https://www.w3cschool.cn/socket/socket-ulbj2eii.html
socket.io是一个为浏览器与服务器之间提供实时的、双向的、基于事件通信的网络通信库。它实现了websocket协议,提供了相关API,抹平了一些技术细节及兼容性。
长连接多用于聊天室 这里以以个聊天对话为例
// 监听connection事件
serversocket.on('connection', (client)=>{ console.log('用户已连接:'+client.id);
// 更新count变量,用于保存连接数。
count++; // 向所有客户端分发消息,把count发给所有人。 serversocket.emit('count', count); }
// 监听连接断开
client.on('disconnect', (reason)=>{ console.log(reason); // 断开连接的原因 count--; serversocket.emit('count', count); })
每当count有变化时,需要把当前count的值广播给所有客户端,让客户端更新UI。
// 接收服务端发回来的当前在线人数:count,更新UI
client.on('count', (data)=>{ let unSpan = document.getElementById('userNumber'); unSpan.innerHTML = data })
代码示例:
客户端;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>微信聊天室</title> <link rel="stylesheet" href="styles/normalize.css"> <link rel="stylesheet" href="styles/reset.css"> <link rel="stylesheet" href="styles/chart.css"> </head> <body> <div id="chart-container"> <div class="chart-user-list" id="chart-user-list"> <div class="user-item"> <img src="images/avatar/15.jpg" alt=""> 未知名 </div> </div> <div class="chart-main-area"> <div class="chart-main-title"> <h1>微信聊天室(<span id="userNumber"></span>人)-<span id="currentUser"></span></h1> </div> <div class="chart-list" id="chart-list"> <div style="display:none;" class="user-logined" id="user-logined"><span id="logined-user"></span>上线了</div> </div> <div class="chart-form"> <div><textarea class="chart-form-message" id="message"></textarea></div> <div><input type="button" id="send" class="chart-form-send" value="发表"></div> </div> </div> </div> <!-- 导入socket.io.js --> <script src="scripts/socket.io.js"></script> <!-- 建立连接 --> <script> // let client = io('ws://localhost:3000/') // http://127.0.0.1:3000/chart.html let client = io('ws://127.0.0.1:3000/') console.log(client) // 为发送按钮绑定事件,发送消息 let send = document.getElementById('send'); send.addEventListener('click', ()=>{ let textarea = document.getElementById('message'); let val = textarea.value; //获取发送文本 client.emit('message', val) // 发消息 }) // 接收服务端发回来的数据 client.on('message', (data)=>{ console.log(data); // 构造一个div,包含img头像与消息文本 let div = document.createElement('div'); div.className = 'chart-item'; div.innerHTML = `<div class="user-face"><img src="images/avatar/11.jpg" alt=""></div> <div class="user-message">${data}</div>` // 把构造完成的div,追加到列表中 let clist = document.getElementById('chart-list'); clist.appendChild(div); }) </script> </body> </html>
服务器端:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const express = require('express'); const app = express(); //将http 从express中抽出来 只有http才便于实现长连接 const http = require('http').createServer(app); let serversocket = require('socket.io')(http); // 监听connection事件 serversocket.on('connection', (client)=>{ console.log('用户已连接:'+client.id) // 接收消息 client.on('message', (data)=>{ console.log(data); // 给所有客户端再发一遍 serversocket.emit('message', data); }) }) // 设置静态托管目录 启动服务 // http://127.0.0.1:3000/index.html // http://127.0.0.1:3000/chart.html app.use(express.static('public')) http.listen(3000, ()=>{ console.log('listening on 127.0.0.1:3000...') })
六 WebWorker 多线程
Javascript采用单线程模型,也就是说所有的任务只能在一个线程上完成。一次只能做一件事。webWorker可以为Javascript提供多线程环境,通过主线程创建worker线程,可以将繁重的工作交给worker线程来实现,从而减轻主线程的工作压力。
示例:斐波那契数列。 1 1 2 3 5 8 13 21 34 55 ...
单线程代码示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>01_fb.html</title> </head> <body> <h1>n=43时的斐波那契数列值为: <span id="span"></span> <button>测试</button> </h1> <script> // 递归实现斐波那契数列 function fb(n){ return n<3 ? 1 : fb(n-1) + fb(n-2); } let r = fb(43); document.getElementById('span').innerHTML = r; </script> </body> </html>
WebWorker的使用
创建WebWorker:
// 创建了一个worker线程,该线程将会自动执行fb.js中的js脚本。 // fb.js中代码的执行不会影响主线程。 let mworker = new Worker('js/fb.js')
Worker与主线程的通信
worker线程向主线程发消息
//在 fb.js中 postMessage('data....')
主线程接收消息:
let mworker = new Worker('...') mworker.onmessage = function(event){ console.log() // 接收并更新UI }
主线程发消息:
let mworker = new Worker('...') mworker.postMessage('消息内容')
worker线程接收消息:
//在 fb.js中 onmessage = (event)=>{ // 通过event获取数据,执行耗时代码 // 计算完毕,发消息给主线程 }
采用多线程实现斐波那契数列
代码示例:
子线程js代码:
function fb(n){ return n<3 ? 1 : fb(n-1) + fb(n-2); } let r = fb(43); console.log(r); // 把结果发给主线程 postMessage(r); // 接收主线程发过来的消息 onmessage = (event)=>{ let n = event.data; let result = fb(n); postMessage(result); }
主线程HTML代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>02_fbworker.html</title> </head> <body> n: <input type="number" id="num"> <button id="btn">计算fb</button> <button id="btn2">启动新worker计算fb</button> <br><br> n=43的斐波那契数列结果: <span id="span"></span> <script> // // worker将会启动子线程执行fb.js中的脚本 // let mworker = new Worker('fb.js'); // // 接收mworker发回来的消息 // mworker.onmessage = (data)=>{ // console.log(data); // // 更新UI // let span = document.getElementById('span'); // span.innerHTML = data.data; // } // // 为按钮绑定事件: // let btn = document.getElementById('btn'); // btn.addEventListener('click', ()=>{ // // 给worker发消息,把文本框中的数据发过去进行计算 // let val = parseInt(document.getElementById('num').value); // mworker.postMessage(val); // }) let btn2 = document.getElementById('btn2'); btn2.addEventListener('click', ()=>{ let sworker = new Worker('fb2.js'); // 给worker发消息,把文本框中的数据发过去进行计算 let val = parseInt(document.getElementById('num').value); sworker.postMessage(val); sworker.onmessage = (data)=>{ console.log(data.data); // 更新UI let span = document.getElementById('span'); span.innerHTML = data.data; } }) </script> </body> </html>
七 SVG
SVG(Scalable Vector Graphics) 可伸缩的矢量图形,是基于XML语法的图像格式。可以简单理解为SVG图像的内容可以使用标签文本语言(XML)来进行绘制。浏览器会根据SVG的标准对XML扩展标示语言(Extensive Markup Language)文本进行解析,从而显示图像。详情可以点击这里
如何写一个SVG图像
新建SVG文件(后缀名.svg),编写图像内容。
<?xml version="1.0" ?> <!-- 上面这一行称为xml声明,每一篇xml文档都需要添加 该声明。xml声明必须在第一行顶格写,一个空格都不能 有,更别说写注释了 --> <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> <!-- cx、cy 定义圆心点坐标 r 定义半径 stroke 定义描边颜色 strokewidth 定义描边宽度 fill 定义填充颜色 --> <circle cx="100" cy="100" r="90" stroke="black" strokewidth="2" fill="red"/> </svg>
网页中引入该SVG即可。
<body> <img src="circle.svg" width="200" alt=""> </body>
SVG的使用方式
<!-- img标签 --> <img src="a.svg" /> <!-- CSS background的方式 --> <style> #div { background-image: url('a.svg'); } </style> <!-- 直接在html中使用svg标签 --> <svg ...> <circle> .... </circle> </svg> <!-- object标签 --> <object type="image/svg+xml" data="a.svg"> </object> <embed type="image/svg+xml" src="a.svg"> </embed> <iframe src="a.svg"> </iframe>