HTML 是网页使用的语言,定义了网页的结构和内容。浏览器访问网站,其实就是从服务器下载 HTML 代码,然后渲染出网页。HTML 的全名是“超文本标记语言”(HyperText Markup Language),它的最大特点就是支持超链接,点击链接就可以跳转到其他网页,从而构成了整个互联网。1999年,HTML 4.01 版发布,成为广泛接受的 HTML 标准。2014年,HTML 5 发布,这是目前正在使用的版本。下面我主要给大家介绍其中的图像和多媒体相关的部分。
SVG
SVG代表可缩放矢量图形,它基本上以 XML 格式定义基于矢量的图形。**SVG 图形在缩放或调整大小时不会损失任何质量。**SVG 文件中的每个元素和每个属性都可以设置动画。
SVG 的优点:与其他图像格式(如 JPEG 和 GIF)相比,使用 SVG 的优点是:
- SVG 图像可以使用任何文本编辑器创建和编辑。
- SVG 图像可以被搜索、索引、脚本化和压缩。
- SVG 图像是可缩放的。
- SVG 图像可以在任何分辨率下高质量打印。
基本使用
SVG可以直接插入网页称为DOM的一部分,然后使用js和css进行操作。也可以写在一个独立文件之中,然后用<img>
、<object>
、<embed>
、<iframe>
等标签插入网页。css中也可以使用svg文件。
将svg的代码都放在顶层标签svg之中。
<svg width="100" height="100" viewBox="50 50 50 50">
<circle id="mycircle" cx="50" cy="50" r="50" />
</svg>
其中,
<svg>
的width
属性和height
属性,指定了 SVG 图像在 HTML 元素中所占据的宽度和高度。除了相对单位,也可以采用绝对单位(单位:像素)。如果不指定这两个属性,SVG 图像的大小默认为300像素(宽)x 150像素(高)。
<viewBox>
属性的值有四个数字,分别是左上角的横坐标和纵坐标、视口的宽度和高度。
视口必须适配所在的空间。viewBox指定了视口大小是50x50,由于svg的大小是100x100,所以视口会放大取适配svg中图像的大小
基本形状
-
标签:用来绘制圆形,
<circle>
标签的cx
、cy
、r
属性分别为横坐标、纵坐标和半径,单位为像素。坐标都是相对于<svg>
画布的左上角原点 -
标签:用来绘制直线。
<line>
标签的x1
属性和y1
属性,表示线段起点的横坐标和纵坐标;x2
属性和y2
属性,表示线段终点的横坐标和纵坐标; -
标签:用于绘制一根折线,
<polyline>
的points
属性指定了每个端点的坐标,横坐标与纵坐标之间与逗号分隔,点与点之间用空格分隔。 -
标签:用于绘制矩形。
<rect>
的x
属性和y
属性,指定了矩形左上角端点的横坐标和纵坐标;width
属性和height
属性指定了矩形的宽度和高度(单位像素)。 -
标签:用于绘制椭圆。
<ellipse>
的cx
属性和cy
属性,指定了椭圆中心的横坐标和纵坐标(单位像素);rx
属性和ry
属性,指定了椭圆横向轴和纵向轴的半径(单位像素)。 -
标签:用于绘制多边形。
<polygon>
的points
属性指定了每个端点的坐标,横坐标与纵坐标之间与逗号分隔,点与点之间用空格分隔。 -
标签:用于制路径。
<path>
的d
属性表示绘制顺序,它的值是一个长字符串,每个字母表示一个绘制动作,后面跟着坐标。- M:移动到(moveto)
- L:画直线到(lineto)
- Z:闭合路径
-
标签:用于绘制文本。
<text>
的x
属性和y
属性,表示文本区块基线(baseline)起点的横坐标和纵坐标。 -
标签:用于复制一个形状。
<use>
的href
属性指定所要复制的节点,x
属性和y
属性是<use>
左上角的坐标。另外,还可以指定width
和height
坐标。 -
标签:用于将多个形状组成一个组(group),方便复用。
-
标签:用于自定义形状,它内部的代码不会显示,仅供引用。
-
标签:用于自定义一个形状,该形状可以被引用来平铺一个区域
-
标签:用于插入图片文件。
-
标签:用于产生动画效果。
-
使用代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SVG</title>
</head>
<body>
<h2>SVG相关标签</h2>
<!--圆形-->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<circle cx="100" cy="100" r="50" stroke="black" stroke-width="2" fill="grey"></circle>
</svg>
<!--直线-->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<line x1="10" y1="10" x2="180" y2="180" style="stroke:pink;stroke-width:3" />
</svg>
<!-- 折线 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<polyline points="10,10 180,20 150,150 10,10" style="stroke:pink;stroke-width:3;fill:bisque" />
</svg>
<!--矩形-->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<rect x="50" y="50" width="100" height="100" style="fill:cadetblue;stroke-width: 3;stroke: black" />
<!-- <rect x="50" y="50" width="100" height="100" rx="20" ry="20" style="fill:chocolate;stroke-width: 3;stroke: black" /> -->
</svg>
<!-- 椭圆 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<ellipse cx="100" cy="100" ry="40" rx="80" stroke="black" stroke-width="5" fill="silver"/>
</svg>
<!-- 多边形 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<polygon points="100,10 40,198 190,78 10,78 160,198" style="fill: grey; stroke: orange;stroke-width: 5; fill-rule: evenodd" />
</svg>
<!-- 路径 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<path d="
M 18,3
L 46,3
L 46,40
L 61,40
L 32,68
L 3,40
L 18,40
Z
"></path>
<text x="100" y="50">这是一个路径</text>
</svg>
<!-- use复制 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<circle id="myCircle" cx="100" cy="100" r="20" />
<use href="#myCircle" x="50" y="50" fill="yellow" stroke="blue" />
<text x="0" y="50">use的位置是相对复制的圆形计算的</text>
</svg>
<!-- group标签 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<g id="myGroup">
<text x="25" y="20">圆形</text>
<circle cx="50" cy="50" r="20"/>
</g>
<use href="#myGroup" x="50" y="50" fill="blue" />
<use href="#myGroup" x="100" y="100" fill="white" stroke="blue" />
</svg>
<!-- defs标签 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<defs>
<g id="myDefs">
<text x="25" y="20">圆形</text>
<circle cx="50" cy="50" r="20"/>
</g>
</defs>
<use href="#myDefs" x="50" y="50" fill="blue" />
<use href="#myDefs" x="100" y="100" fill="white" stroke="blue" />
</svg>
<!-- pattern标签 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<defs>
<pattern id="dots" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse">
<circle fill="#bee9e8" cx="50" cy="50" r="35" />
</pattern>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="url(#dots)" />
</svg>
<!-- 动画 -->
<svg style="width: 200;height: 200;border: 1px solid #000;">
<rect x="0" y="0" width="50" height="50" fill="#feac5e">
<animate attributeName="x" from="0" to="200" dur="2s" repeatCount="indefinite" />
<!-- <animate attributeName="y" from="0" to="200" dur="2s" repeatCount="indefinite" /> -->
</rect>
</svg>
</body>
</html>
js操作
DOM操作
var mycircle = document.getElementById('mycircle');
mycircle.addEventListener('click', function(e) {
console.log('circle clicked - enlarging');
mycircle.setAttribute('r', 60);
}, false);
由于 SVG 文件就是一段 XML 文本,因此可以通过读取 XML 代码的方式,读取 SVG 源码。使用XMLSerializer
实例的serializeToString()
方法,获取 SVG 元素的代码。
var svgString = new XMLSerializer()
.serializeToString(document.querySelector('svg'));
SVG转为Canvas:首先,需要新建一个Image
对象,将 SVG 图像指定到该Image
对象的src
属性。然后,当图像加载完成后,再将它绘制到<canvas>
元素。 效果展示
var img = new Image();
var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
var DOMURL = self.URL || self.webkitURL || self;
var url = DOMURL.createObjectURL(svg);
img.src = url;
img.onload = function () {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
};
渐变
-
线性渐变:在 SVG 文件的
defs 元素
内部,创建一个<linearGradient>
节点。其中线性渐变包括x1、x2、y1、y2这四个属性,用来控制渐变的大小和方向。
- 当y1和y2相等,而x1和x2不同时,可创建水平渐变
- 当x1和x2相等,而y1和y2不同时,可创建垂直渐变
- 当x1和x2不同,且y1和y2不同时,可创建角形渐变
offset用来设置色标位置 stop-color用来设置色标颜色 stop-opacity用来设置色标的透明度
<svg width="100" height="230" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="Gradient1" >
<stop offset="0%" stop-color="lavenderblush" stop-opacity="1" />
<stop offset="50%" stop-color="darksalmon" stop-opacity="1" />
<stop offset="100%" stop-color="rgb(221, 88, 43)" stop-opacity="1"/>
</linearGradient>
</defs>
<rect id="rect1" x="0" y="10" rx="15" ry="15" width="100" height="100" fill="url(#Gradient1)" />
</svg>
-
径向渐变:它是从一个点开始发散绘制渐变。在文档的
defs
中添加一个<radialGradient>
元素。与线性渐变的x1、y1、x2、y2属性不同,径向渐变使用cx、cy、r、fx、fy这五个属性来设置渐变。
r 设置圆的半径 cx、cy 定义渐变的中心点坐标 fx、fy 定义渐变的焦点坐标
<svg width="250" height="250" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<radialGradient id="RadialGradient1">
<stop offset="0%" stop-color="red" />
<stop offset="100%" stop-color="pink" />
</radialGradient>
</defs>
<rect x="10" y="10" rx="15" ry="15" width="100" height="100" fill="url(#RadialGradient1)" />
</svg>
剪切和遮罩
剪切:clipPath属性用于剪切,ClipPath里的元素不显示,只是用来确定剪切下来要展示的部分。
<svg width="250" height="250">
<defs>
<clipPath id="cut-off-bottom">
<rect x="0" y="0" width="200" height="100" fill="pink"/>
</clipPath>
</defs>
<circle cx="100" cy="100" r="100" clip-path="url(#cut-off-bottom)" fill="green"/>
</svg>
**遮罩:**遮罩类似于我们的mask效果,会覆盖在原本的元素上。这里我们以常见的“元素淡出”效果举例。
<svg width="250" height="250">
<defs>
<linearGradient id="Gradient">
<stop offset="0" stop-color="white" stop-opacity="0" />
<stop offset="1" stop-color="white" stop-opacity="1" />
</linearGradient>
<mask id="Mask">
<rect x="0" y="0" width="200" height="200" fill="url(#Gradient)" />
</mask>
</defs>
<rect x="0" y="0" width="200" height="200" fill="green" />
<rect x="0" y="0" width="200" height="200" fill="red" mask="url(#Mask)" />
</svg>
滤镜
在 SVG 当中,滤镜功能无疑是最强大的功能。它让我们对输出的图像可以进行像素级的控制。通过使用一些内置的滤镜功能可以快速达到我们期望的显示效果。
什么是滤镜?滤镜是用于图像的呈现特殊效果的一个工具。通过滤镜我们能够原子级参与图像的显像过程,控制最终的图片输出。
在css中内置了很多滤镜,例如高斯模糊(blur)、灰度(grayscale)等,SVG 元素通过 filter 属性设置滤镜。常见的部分滤镜如下:
-
<feGaussianBlur >
- 模糊滤镜 -
<feOffset >
- 位移滤镜 -
<feMerge>
- 多滤镜叠加滤镜 -
<feBlend>
-混合模式滤镜,包含normal( 正常)、multiply (正片叠底)、screen (滤色)、darken ( 变暗)、lighten(变亮) -
<feImage>
-提供像素数据作为输出 -
<feFlood>
- 填充效果。 -
<feDropShadow>
- 图像投影 -
<feDiffuseLighting>
-光照处理。 -
这里我们使用最常见的滤镜进行使用举例
SourceAlpha 与 SourceGraphic 具有相同的规则除了 SourceAlpha 只使用元素的**非透明部分 **
<svg width="500" height="500" >
<defs>
<filter id="filter" width="200" height="200">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" />
<feOffset in="blur" dx="10" dy="10" result="offsetBlur" />
<feMerge>
<feMergeNode in="offsetBlur" />
<!-- 将元素本身作为输入 -->
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<image x="0" y="0" width="100" height="100"
xlink:href="./src/daidai.png" filter="url(#filter)"></image>
</svg>
Canvas
<canvas>
元素用于生成图像。它本身就像一个画布,JavaScript 通过操作它的 API,在上面生成图像。它的底层是一个个像素,基本上<canvas>
是一个可以用 JavaScript 操作的位图(bitmap)。它与 SVG 图像的区别在于,<canvas>
是脚本调用各种方法生成图像,SVG 则是一个 XML 文件,通过各种子元素生成图像。
和video等标签类似,使用 Canvas API 之前,需要在网页里面新建一个<canvas>
元素。每个canvas元素都对应一个上下文对象(CanvasRenderingContext2D),Cnavas API就定义在这个对象上。getContext()
方法,返回的就是CanvasRenderingContext2D
对象。
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
注意,Canvas API 需要getContext
方法指定参数2d
,表示该<canvas>
节点生成 2D 的平面图像。如果参数是webgl
,就表示用于生成 3D 的立体图案,这部分属于 WebGL API。
绘制图形
-
路径:
- beginPath():开始绘制路径
- closePath():结束路径,返回到起点,如果已经闭合或者只有一个点,无效果
- moveTo():设置新路径的起点
- lineTo():使用直线连接到当前坐标
- fill():路径内部填充颜色
- stroke():路径线条颜色
-
矩形
- ctx.rect(x,y,width,height) - 绘制矩形路径
- ctx.strokeRect(x,y,width,height) - 绘制矩形
- ctx.fillRect(x,y,width,height) - 绘制填充矩形
- ctx.clearRect(x,y,width,height) - 清除矩形区域
-
弧线:
- ctx.arc(x,y,radius,start,end,anticlockwise) - 绘制圆形或扇形,anticlockwise顺时针还是逆时针
-
文本
- strokeText(string,x,y) - 绘制空心文字
- fillText(string,x,y) - 绘制实心文字
-
渐进色和图像填充
- createLinearGradient(x1,y1,x2,y2) - 设置线性渐变色
- ctx.createRadialGradient(x0, y0, r0, x1, y1, r1)-设置辐射渐变色。
x0
和y0
是辐射起始的圆的圆心坐标,r0
是起始圆的半径,x1
和y1
是辐射终止的圆的圆心坐标,r1
是终止圆的半径。
-
阴影
- shadowOffsetX - 设置水平位移
- shadowOffsetY - 设置垂直位移
- shadowBlur - 设置模糊度
- shadowColor - 阴影颜色
- pattern:用于定义填充或描边样式
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.src = './src/daidai.png';
img.onload = function() {
const pattern = ctx.createPattern(img, 'repeat');
ctx.fillStyle = pattern;
ctx.fillRect(0, 0,2000, 2000);
};
图像变换
- rotate-图像旋转,它接受一个弧度值作为参数,表示顺时针旋转的度数。
- scale-图像缩放,它接受两个参数,分别是
x
轴方向的缩放因子和y
轴方向的缩放因子。 - translate-图像平移,它接受两个参数,分别是 x 轴和 y 轴移动的距离(单位像素)。
- transform-通过一个变换矩阵完成图像变换,接受一个变换矩阵的六个元素作为参数,完成缩放、旋转、移动和倾斜等变形。
- setTransform-取消前面的图像变换,参数与
transform()
方法完全一致。
图像处理
-
drawImage() - 对图片进行重绘
-
getImageData()-读取 canvas 的内容,返回一个对象,包含了每个像素的信息。
-
putImageData() - 将
ImageData
对象的像素绘制在<canvas>
画布上 -
toDataURL-对图像数据做出修改后,将 canvas 数据重新转化成一般的图像文件格式,然后可以进行另存本地或转发功能。
-
save - 保存上下文环境,将画布的当前样式保存到堆栈,相当于在内存之中产生一个样式快照。
-
restore - 恢复到上一次保存的上下文环境
-
粒子demo
一个像素是有4个值(R,G,B,A)组成的。也就是说,数组信息每四个为一个像素点。因此,有以下规则,
第一个像素信息为:RGBA(data[0],data[1],data[2],data[3])
第二个像素信息为:RGBA(data[4],data[5],data[6],data[7])
.....
第N个像素信息为: RGBA(data[(n-1)*4],data[(n-1)*4+1],data[(n-1)*4+2],data[(n-1)*4+3])
-
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const image = document.getElementById('image');
const canvas1 = document.getElementById('canvas1');
const ctx1 = canvas1.getContext('2d');
canvas1.width = 500;
canvas1.height = 500;
canvas.width = 500;
canvas.height = 500;
const particleSize = 4;
const particleSpacing = 10;
window.onload = function(){
document.querySelector("#File").onchange=function(){
var Reader = new FileReader(); // 创建文件读取对象
Reader.readAsDataURL(this.files[0]); //读取Blob,获取DataURL
Reader.onload = function(){
let img = new Image();
img.src = Reader.result; //将result赋值给Image对象的src
img.onload = function(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
createParticles(imageData);
}
}
}
}
function createParticles(imageData) {
ctx1.clearRect(0, 0, canvas.width, canvas.height);
for (let y = 0; y < canvas.height; y += particleSpacing) {
for (let x = 0; x < canvas.width; x += particleSpacing) {
const i = (y * canvas.width + x) * 4;
const r = imageData.data[i];
const g = imageData.data[i + 1];
const b = imageData.data[i + 2];
const a = imageData.data[i + 3];
if (a) {
ctx1.fillStyle = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
ctx1.beginPath();
ctx1.arc(x, y, particleSize, 0, Math.PI * 2);
ctx1.fill();
}
}
}
}
HTML SVG 和 HTML Canvas 之间的区别:
- SVG 是一种用 XML 描述 2D 图形的语言,而 Canvas 使用 JavaScript 动态绘制 2D 图形。
- 如果 SVG 对象的属性发生更改,浏览器可以自动重新渲染形状,而 Canvas 则逐像素渲染。在canvas中,一旦绘制了图形,浏览器就会忘记它。
- SVG 与分辨率无关,而 Canvas 与分辨率相关。
- SVG 支持事件处理程序,而 Canvas 不支持事件处理程序。
video
基本使用
<video>
标签是一个块级元素,是H5新标签,用于放置视频。如果浏览器支持加载的视频格式,就会显示一个播放器,否则显示<video>
内部的子元素。
<video src="example.mp4" controls>
<p>你的浏览器不支持 HTML5 视频,请下载<a href="example.mp4">视频文件</a>。</p>
</video>
其中,video具备以下属性:
src
:视频文件的网址。controls
:播放器是否显示控制栏。如果不想使用浏览器默认的播放器,而想使用自定义播放器,就不要使用该属性。width
:视频播放器的宽度。height
:视频播放器的高度。autoplay
:视频是否自动播放。loop
:视频是否循环播放。muted
:是否默认静音。poster
:视频播放器的封面图片的 URL。preload
:视频播放之前,是否缓冲视频文件。这个属性仅适合没有设置autoplay
的情况。它有三个值,分别是none
(不缓冲)、metadata
(仅仅缓冲视频文件的元数据)、auto
(可以缓冲整个文件)。playsinline
:iPhone 的 Safari 浏览器播放视频时,会自动全屏,该属性可以禁止这种行为。该属性为布尔属性。crossorigin
:是否采用跨域的方式加载视频。它可以取两个值,分别是anonymous
(跨域请求时,不发送用户凭证,主要是 Cookie),use-credentials
(跨域时发送用户凭证)。currentTime
:指定当前播放位置(双精度浮点数,单位为秒)。如果尚未开始播放,则会从这个属性指定的位置开始播放。duration
:该属性只读,指示时间轴上的持续播放时间(总长度),值为双精度浮点数(单位为秒)。如果是流媒体,没有已知的结束时间,属性值为+Infinity
。
API
我们可以通过HTMLVideoElement接口使用js来控制视频播放的一些方法。
-
play()
: 开始播放视频,返回一个Promise。播放成功开始时,promise被解析,否则拒绝它。如果视频已经在播放,不执行任何操作。 -
pause()
: 在当前位置暂停视频播放器,如果已暂停不执行任何操作。 -
load()
:重新初始化视频元素并重新加载视频资源。注意:load会触发重新小苗和重新获取,因此preload属性将确定实际再次获取的数据量。 -
canPlayType(type)
:检查浏览器是否支持特定的视频格式,返回内容为三个值,“可能”、“也许”或空字符串 -
addTextTrack()
:动态的将文本轨道加入到视频中,文本轨道可以用于字母、说明等。 -
currentTime
: 该属性返回当前播放时间(以秒为单位)。如果将他设置成新值,他会自动将视频快速跳转到相应的时间戳。实践:自定义视频播放器
实现一个播放器,包含播放暂停、退出、快进快退、进度条等功能。
- demo
// js
const media = document.querySelector("video");
const controls = document.querySelector(".controls");
// 播放暂停
const play = document.querySelector(".play");
function playPauseMedia() {
if(media.paused) {
play.innerHTML = '暂停';
media.play();
}else {
play.innerHTML = '播放';
media.pause();
}
}
play.addEventListener("click", playPauseMedia);
// 停止
const stop = document.querySelector(".stop");
function stopMedia() {
media.pause();
media.currentTime = 0; // 没有stop方法,只能暂停后设置currentTime为0
play.innerHTML = '播放';
// // 快退或快进功能处于活动状态时按下播放/暂停或停止按钮
// active.set('rwd', false);
// active.set('fwd', false);
// clearInterval(intervalRwd);
// clearInterval(intervalFwd);
}
stop.addEventListener("click", stopMedia);
// 快退 & 快进
const rwd = document.querySelector(".rwd");
const fwd = document.querySelector(".fwd");
rwd.addEventListener("click", mediaBackward);
fwd.addEventListener("click", mediaForward);
let intervalFwd; // 定时器
let intervalRwd;
let active = new Map(); // 用于判断快退或快进功能是否处于活动状态
function mediaBackward() {
clearInterval(intervalFwd); // 快退和快进功能互斥,清除另一个功能的定时器
active.set('fwd', false); // 快进功能处于非活动状态
if(active.get('rwd')) { // 快退功能处于活动状态,按下按钮后,清除定时器,恢复播放
active.set('rwd', false);
clearInterval(intervalRwd);
media.play();
}else { // 快退功能处于非活动状态,按下按钮后,设置快退功能为活动状态,暂停播放,设置定时器
active.set('rwd', true);
media.pause();
intervalRwd = setInterval(windBackward, 500);
}
}
function windBackward() { // 快退功能
if (media.currentTime <= 2) { // 当前时间小于2s时,设置快退功能为非活动状态,清除定时器,停止播放
active.set('rwd', false);
clearInterval(intervalRwd);
stopMedia();
} else { // 当前时间大于2s时,快退2s
media.currentTime -= 2;
}
}
function mediaForward() {
clearInterval(intervalRwd);
active.set('rwd', false);
if (active.get('fwd')) {
active.set('fwd', false);
clearInterval(intervalFwd);
media.play();
} else {
active.set('fwd', true);
media.pause();
intervalFwd = setInterval(windForward, 500);
}
}
function windForward() {
if (media.currentTime >= media.duration - 2) {
active.set('fwd', false);
clearInterval(intervalFwd);
stopMedia();
} else {
media.currentTime += 2;
}
}
// 进度时间
const timerWrapper = document.querySelector(".timer");
const timer = document.querySelector(".timer span");
const timerBar = document.querySelector(".timer div");
function setTime() { // 设置当前时间
const minutes = Math.floor(media.currentTime / 60); // 分钟,向下取整,不足两位补0
const seconds = Math.floor(media.currentTime - minutes * 60); // 秒数,向下取整,不足两位补0
const minuteValue = minutes.toString().padStart(2, "0");
const secondValue = seconds.toString().padStart(2, "0");
const mediaTime = `${minuteValue}:${secondValue}`;
timer.textContent = mediaTime;
const barLength =
timerWrapper.clientWidth * (media.currentTime / media.duration); // 进度条长度,当前时间/总时间
timerBar.style.width = `${barLength}px`; // 设置进度条长度
}
media.addEventListener("timeupdate", setTime); // 监听当前时间变化
// 画中画
media.addEventListener('click', () => {
if (media !== document.pictureInPictureElement) {
media.requestPictureInPicture(); // 进入画中画
} else {
document.exitPictureInPicture(); // 退出画中画
}
});
media.removeAttribute("controls");
controls.style.visibility = "visible";
// HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自定义视频播放器</title>
<link rel="stylesheet" href="./myVideo.css">
</head>
<body>
<div class="player">
<video controls >
<source src="./src/test.mp4" type="video/mp4" />
<track src="captions.vtt" kind="captions" srclang="en" label="English" default>
</video>
<div class="controls">
<button class="play" >播放</button>
<button class="stop" >停止</button>
<div class="timer">
<div></div>
<span aria-label="timer">00:00</span>
</div>
<button class="rwd" >快退</button>
<button class="fwd" >快进</button>
</div>
</div>
<script src="./myVideo.js"></script>
</body>
</html>
// css
.controls {
visibility: hidden;
opacity: 0.5;
width: 400px;
border-radius: 10px;
position: absolute;
bottom: 20px;
left: 50%;
margin-left: -200px;
background-color: black;
box-shadow: 3px 3px 5px black;
transition: 1s all;
display: flex;
}
.player:hover .controls,
.player:focus-within .controls {
opacity: 1;
}
button:before {
font-size: 20px;
position: relative;
content: attr(data-icon);
color: #aaa;
text-shadow: 1px 1px 0px black;
}
.timer {
line-height: 38px;
font-size: 10px;
font-family: monospace;
text-shadow: 1px 1px 0px black;
color: white;
flex: 5;
position: relative;
}
.timer div {
position: absolute;
background-color: rgba(255, 255, 255, 0.2);
left: 0;
top: 0;
width: 0;
height: 38px;
z-index: 2;
}
.timer span {
position: absolute;
z-index: 3;
left: 19px;
}
video::cue {
background-color: rgba(0, 0, 0, 0.8);
color: #fff;
font-size: 16px;
padding: 4px;
}