一、简介
TypeScript是JavaScript类型的超集,它可以编译成纯JavaScript。
- TypeScript可以再任何浏览器、任何计算机和任何操作系统上运行,并且是开源免费的。
为什么要使用TypeScript?
- TypeScript可以提供静态类型检查,规范团队的编码及使用方式,适合大型项目的开发。
- IDE的友好提示,也是适合大型项目的开发。
二、Webpack搭建项目环境
1、提前安装好node.js和npm
2、新建一个空文件夹,自定义命名(此处命名为miProject)
(1)在cmd进入该文件夹,创建该文件夹的配置文件(命令:npm init -y
)
(2)局部安装webpack和webpack-cli(命令:npm i -D webpack webpack-cli
)
(3)用VsCode打开该文件夹,创建一个webpack的配置文件:webpack.config.js
const path = require('path');
module.exports = {
entry : "./src/main.js",
output : {
path : path.resolve(__dirname , 'dist'),
filename : "main.js"
},
mode : "development"
};
(4)创建src文件夹,在src下创建mian.js和a.js,用于测试环境
a.js:
let a = 3;
export default a;
mian.js:
import a from './a.js';
console.log(a);
在package.json添加一个执行脚本:
命令行执行打包(命令:npm run build
):
可以再原工程项目中找到output文件dist,创建index.html:
选择在浏览器打开index.html,F12查看打印结果为3,表示环境搭建ok:
(5)将css文件作为模块打包
需要在工程中下载一个style-loader(命令:npm i -D style-loader css-loader
),并在webpack.config.js中添加引入规则:![在这里插入图片描述](
const path = require('path');
module.exports = {
entry : "./src/main.js",
output : {
path : path.resolve(__dirname , 'dist'),
filename : "main.js"
},
module : {
rules : [
{
test : /\.css$/,
use : ['style-loader','css-loader']
}
]
},
mode : "development"
};
在src下创建一个a.css,在main.js中引入:
再次在浏览器中打开index.html,可以看到css引入成功:
3、webpack的插件使用
A、自动引入插件
(1)安装命令:npm i -D html-webpack-plugin
(2)安装完毕后需要在webpack.config.js中引入调用
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry : "./src/main.js",
output : {
path : path.resolve(__dirname , 'dist'),
filename : "main.js"
},
module : {
rules : [
{
test : /\.css$/,
use : ['style-loader','css-loader']
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : "./src/index.html"
})
],
mode : "development"
};
(3)删除掉原来的dist文件,在src下新建一个index.html,重新打包项目
运行npm run buile
打包命令之后打开新生成的dist文件夹下的index.html,发现main.js已经被自动引入了:
B、自动清理插件
(1)作用
当s工程目录下文件名修改之后再次打包时,它会先清理dist文件在重新生成
(2)安装命令:npm i -D clean-webpack-plugin
(3)安装完毕后需要在webpack.config.js中引入调用
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry : "./src/main.js",
output : {
path : path.resolve(__dirname , 'dist'),
filename : "main.js"
},
module : {
rules : [
{
test : /\.css$/,
use : ['style-loader','css-loader']
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : "./src/index.html"
}),
new CleanWebpackPlugin()
],
mode : "development"
};
4、webpack-dev-server的使用
(1)功能作用
- 为静态文件提供web服务
- 自动刷新和热替换(HMR)
–自动刷新指当修改代码时webpack会进行自动编译,更新网页内容
–热替换指运行时更新各种模块,即局部刷新
(2)安装命令:npm i -D webpack-dev-server
(3)安装完毕后需要在webpack.config.js中引入调用
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry : "./src/main.js",
output : {
path : path.resolve(__dirname , 'dist'),
filename : "main.js"
},
devServer : {
contentBase : "/dist",
open : true
},
module : {
rules : [
{
test : /\.css$/,
use : ['style-loader','css-loader']
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : "./src/index.html"
}),
new CleanWebpackPlugin()
],
mode : "development"
};
在package.json中添加新的启动方式:
在cmd运行命令npm start
即可启动并自动启动服务器访问index.html。
5、支持字体图标的使用
推荐网址:点击访问(1)安装file-loader(命令:npm i -D file-loader
)
(2)在wackpack.config.js中配置规则
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry : "./src/main.js",
output : {
path : path.resolve(__dirname , 'dist'),
filename : "main.js"
},
devServer : {
contentBase : "/dist",
open : true
},
module : {
rules : [
{
test : /\.css$/,
use : ['style-loader','css-loader']
},
{
test : /\.(eot|woff2|woff|ttf|svg)$/,
use : ['file-loader']
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : "./src/index.html"
}),
new CleanWebpackPlugin()
],
mode : "development"
};
(3)将iconfgnt网站上我的项目中图标下载到本地
修改a.css文件路径问题:
(4)图标的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Document</title>
</head>
<body>
hello
<i class="iconfont iconyinliang"></i>
<i class="iconfont iconguanbi"></i>
</body>
</html>
启动项目npm start
,可以看到图标加载成功:
6、支持TypeScript的使用
(1)安装命令:npm i -D ts-loader typescript
- 能够将ts文件转为js
(2)在wackpack.config.js中配置规则
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry : "./src/main.ts",
output : {
path : path.resolve(__dirname , 'dist'),
filename : "main.js"
},
devServer : {
contentBase : "/dist",
open : true
},
module : {
rules : [
{
test : /\.css$/,
use : ['style-loader','css-loader']
},
{
test : /\.(eot|woff2|woff|ttf|svg)$/,
use : ['file-loader']
},
{
test : /\.ts$/,
use : ['ts-loader'],
exclude : /node_modules/
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : "./src/index.html"
}),
new CleanWebpackPlugin()
],
mode : "development"
};
(3)在根目录下创建ts4config.json配置文件
{
"compilerOptions": {
"module": "ES6",
"target": "ES5",
}
}
如何在main.ts文件中导入a.ts呢?
默认情况下webpack在省略扩展名的情况下优先匹配a.js,没有找到的话在匹配a.json,再找不到就会报错。所以需要在webpack.config.js中配置匹配规则:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
entry : "./src/main.ts",
output : {
path : path.resolve(__dirname , 'dist'),
filename : "main.js"
},
devServer : {
contentBase : "/dist",
open : true
},
resolve : {
"extensions" : ['.ts','.js','.json']
},
module : {
rules : [
{
test : /\.css$/,
use : ['style-loader','css-loader']
},
{
test : /\.(eot|woff2|woff|ttf|svg)$/,
use : ['file-loader']
},
{
test : /\.ts$/,
use : ['ts-loader'],
exclude : /node_modules/
}
]
},
plugins : [
new HtmlWebpackPlugin({
template : "./src/index.html"
}),
new CleanWebpackPlugin()
],
mode : "development"
};
引入a.ts成功:
另外,css文件在ts环境下是正常使用的。
三、需求分析&视频列表实现
1、弹层播放器需求分析
(1)列表相关数据如何获取?
- 通过服务端的渲染技术通过模板直接渲染到页面
- 在页面当中发起ajax请求获取json格式的数据,在通过解析json渲染
(2)弹层功能与播放器功能最好独立开发,这样可以适配其他功能需求
(3)弹层组件包括:popup.ts、popup.css
(4)播放器组件包括:video.js、video.css
(5)弹层组件配置参数
- width、hright:默认为父容器大小
- title:弹层上面的标题
- content:弹层内容(表单、播放器、图片等)
- pos:默认情况下位于浏览器居中的位置
- mask:背景遮罩
(6)播放器组件配置参数
- weight、height:默认为父容器大小
- url:要播放视频的地址
- elem:最终创建出来的播放器组件要渲染到哪个容器当中
- autoplay:默认情况下自动播放
2、index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="list">
<ul class="list-wrap clearfix">
<li data-url="https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/11c70c96529b6e6938567ec1aa0910e0.mp4"
data-title="Redmi 10X系列发布会">
<div>
<img src="https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/101b19aca4bb489bcef0f503e44ec866.jpg?thumb=1&w=370&h=225&f=webp&q=90"
alt="">
<i class="iconfont iconbofang"></i>
</div>
<h3>Redmi 10X系列发布会</h3>
</li>
<li data-url="https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/7cdabcaa763392c86b944eaf4e68d6a3.mp4"
data-title="小米10 青春版 发布会">
<div>
<img src="https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/96563e75833ba4563bd469dd28203b09.jpg?thumb=1&w=370&h=225&f=webp&q=90"
alt="">
<i class="iconfont iconbofang"></i>
</div>
<h3>小米10 青春版 发布会</h3>
</li>
<li data-url="https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/e25d81c4922fca5ebe51877717ef9b76.mp4"
data-title="小米10 8K手机拍大片">
<div>
<img src="https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/2fd26bb99b723337a2f8eaba84f7d5bb.jpg?thumb=1&w=370&h=225&f=webp&q=90"
alt="">
<i class="iconfont iconbofang"></i>
</div>
<h3>小米10 8K手机拍大片</h3>
</li>
<li data-url="https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/eadb8ddc86f1791154442a928b042e2f.mp4"
data-title="小米10发布会">
<div>
<img src="https://cdn.cnbj1.fds.api.mi-img.com/mi-mall/a8dd25cab48c60fc6387b9001eddc3f9.jpg?thumb=1&w=370&h=225&f=webp&q=90"
alt="">
<i class="iconfont iconbofang"></i>
</div>
<h3>小米10发布会</h3>
</li>
</ul>
</div>
</body>
</html>
3、样式main.css
@font-face {font-family: "iconfont";
src: url('./iconfont/iconfont.eot?t=1612242154731'); /* IE9 */
src: url('./iconfont/iconfont.eot?t=1612242154731#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAARQAAsAAAAACPAAAAQBAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDKAqFAIQnATYCJAMYCw4ABCAFhG0HWRu+B8guIScbXqQZU7Jrl6DRYMH7b5YXl/Yg/EM8fO33eu7uvg2CSxRhSbMERN+p+5VAKnXVlRWuOrK+ClhHv7/8y7d/Tw7kniwl7QUuKVepkg4sysz9uZ8WyZbfXSWSwhGn2eyHlxSAjZyQm1UsXBsgQUZ+lkaagAMO1SfCPZCgGRemFJjGkA1893/cO25MsGy+8y6nOTZ1H7OsizvvAhtj0RaJCwRbYOdQBxN0nECvaebQIfdbojAopHGBeJzMBYXBp1NSikO3UGOWFvEO6LrTc+oWeGv+Pv7DgkXSZNJJx++eyYriL/SLnjsyIiwTIPVZcFtFxgpQiMuxtrPUdmwF1vuJaSyBfvULftFbwC+k83orQfYkp/x5UUmyfsQdjjf8QirPlVMoZcoxSqGchlIop6NgNVS1xiED2AK2QVKt1P+xoQhp0sQZd/BUnSrKmFaeNMlHcZoePVTfk7nvxtjGzAKif9hPampXxB0dXG66QVo5ATiwuQ38N0flicKRiYqt0EbppNKQ7lFlorBErFuhi9qKNDCQvtRen0jtkZKVuXlGOs3hvWhFN1CdKZ8HoTNt4WrFID+ilE43Td51XH4OC+dcR0iSzmY4u6lTiII/h5eSg/Cz6Qiez8I93VqHTSkQauLEVeQmaCK5tN5H6rSHslYqb9MTUwGUONDmbKf39kyNUSxb2DusANYOu404pICb/Z00r0xhXXO+KM1MCpl7iOmmBVj90nrhx5lui6bdEiP6p9CkHUMbFS22+4FH+BiYVKdQWwyNBbvlEoyYnJGxdavOBHfJ+/vfrU6Xzi2+XZ2+vmkBhfxd+Rv57b+p2VIuV+PfczTKfbzFtyhKmgFflSexl/CFRk3kRGpbQhkF2s+fGEHuTS9k0mmkQO4AkL5RPwF5IH26Os7zvqX6SKHtyeYCvtr/n1lbLt4tkZt/LXQq0ALKXSjpa3kLVGOLe4tAdZsWJPTqRSt2jXqtyzEByBK673yEpMsUZN1mUAW3Ao0+u6Hqthd6LfNY3WdMDrUoPVhSCxCGLUAy6DNkw+5QBfcCjUl/UA0Hhl5nQ27LPnORzHHJQWGMMojF0IjtdZliHJus4mfkk7Yw9ApNKsXhqBGEuKV1MHmBXRzWMWNUwBPjPAYsp9eBczgN2+0e9HN6TZTGkXreuF+IWiXEvSkiZXYUJgcNIcEYkoEYCyCZpKeL6a3HTKHvP0PcRJtgmIL4YafQ0Eije2DDknUO1IWsmwuxL71zTXEJecYYj0ow1NMB3NGC2nKsB2TFD2pCkrGIpBKRvgIrqpeQVxZpX9HZym3QS7qiRoocJZqo0aX7aXK33RC6XJeYr3vEhiFb1PQbfktMJouy6Euil8zb5QoFAAAA') format('woff2'),
url('./iconfont/iconfont.woff?t=1612242154731') format('woff'),
url('./iconfont/iconfont.ttf?t=1612242154731') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('./iconfont/iconfont.svg?t=1612242154731#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.iconyinliang:before {
content: "\e605";
}
.iconguanbi:before {
content: "\e602";
}
.iconquanping:before {
content: "\e603";
}
.iconbofang:before {
content: "\e604";
}
.iconzanting:before {
content: "\e601";
}
*{ margin: 0;padding: 0; }
ul{ list-style: none; }
img{ display:block; }
body{ background:#f5f5f5; height:2000px; }
.clearfix::after{ content: ""; display: block; clear: both; }
#list{ width:1226px; margin:20px auto; }
#list .list-wrap{ width:1240px; }
#list .list-wrap li{ float:left; width:296px; height:auto; margin-right:14px; background:white; cursor:pointer; }
#list .list-wrap li:hover i{ background:#ff6700; }
#list .list-wrap div{ position: relative; }
#list .list-wrap img{ width:100%; height:100%; }
#list .list-wrap i{ position:absolute; bottom:10px; left:19px; border:2px white solid; color:white; border-radius:10px; width:32px; height:20px; text-align:center; line-height:20px; font-size:12px; }
#list .list-wrap h3{ font-size:12px; text-align: center; padding: 30px 0;}
4、成果展示
四、Popup弹层组件开发
1、popup.css
.popup { position: fixed; z-index: 20; border: 1px #ccc solid;}
.popup-title{ height: 60px; background: #f5f5f5; display: flex; justify-content: space-between; align-items: center;}
.popup-title h3{ font-size: 18px; margin-left: 20px; }
.popup-title i{ font-size: 18px; margin-right: 20px; cursor: pointer; }
.popup-content{ height: calc(100% - 60px); background: white; }
.mask{ position: absolute; left: 0; top: 0; z-index: 10; background: rgba(0, 0, 0, 0.5); }
2、popup.ts
//import './popup.css' //全局css操作
let styles = require('./popup.css').default;
interface IPopup {
width? : string;
height? : string;
title? : string;
pos? : string;
mask? : boolean;
content? : (content : HTMLElement) => void;
}
interface IComponent {
tempCpntainer : HTMLElement;
init : () => void;
template : () => void;
handle : () => void;
}
function popup( options : IPopup ) {
return new Popup(options);
}
class Popup implements IComponent {
tempCpntainer;
mask;
constructor( private settings : IPopup ) {
this.settings = Object.assign({
width : '100%',
height : '100%',
title : '',
pos : 'center',
mask : true,
content : function(){}
},this.settings);
this.init();
}
//初始化
init() {
this.template();
this.settings.mask && this.createMask();
this.handle();
this.contentCallback();
}
//创建模板
template() {
this.tempCpntainer = document.createElement('div');
this.tempCpntainer.style.width = this.settings.width;
this.tempCpntainer.style.height = this.settings.height;
this.tempCpntainer.className = styles.popup;
this.tempCpntainer.innerHTML = `
<div class="${styles['popup-title']}">
<h3>${this.settings.title}</h3>
<i class="iconfont iconguanbi"></i>
</div>
<div class="${styles['popup-content']}"></div>
`;
document.body.appendChild(this.tempCpntainer);
if(this.settings.pos === 'left'){
this.tempCpntainer.style.left = 0;
this.tempCpntainer.style.top = window.innerHeight - this.tempCpntainer.offsetHeight + 'px';
}else if(this.settings.pos === 'right'){
this.tempCpntainer.style.right = 0;
this.tempCpntainer.style.top = window.innerHeight - this.tempCpntainer.offsetHeight + 'px';
}else{
this.tempCpntainer.style.left = (window.innerWidth - this.tempCpntainer.offsetWidth)/2 + 'px';
this.tempCpntainer.style.top = (window.innerHeight - this.tempCpntainer.offsetHeight)/2 + 'px';
}
}
//事件操作
handle() {
let popupClose = this.tempCpntainer.querySelector(`.${styles['popup-title']} i`);
popupClose.addEventListener('click',()=> {
document.body.removeChild(this.tempCpntainer);
this.settings.mask && document.body.removeChild(this.mask);
});
}
//创建遮罩
createMask() {
this.mask = document.createElement('div');
this.mask.className = styles.mask;
this.mask.style.width = '100%';
this.mask.style.height = document.body.offsetHeight + 'px';
document.body.appendChild(this.mask);
}
//回调方法
contentCallback() {
let popupContent = this.tempCpntainer.querySelector(`.${styles['popup-content']}`);
this.settings.content(popupContent);
}
}
export default popup;
3、main.ts
import './main.css';
import popup from './components/popup/popup';
let listItem = document.querySelectorAll('#list li');
for(let i=0;i<listItem.length;i++){
listItem[i].addEventListener('click',function(){
let url = this.dataset.url;
let title = this.dataset.title;
//console.log(url,title);
popup({
width : '880px',
height : '556px',
title,
pos : 'center',
mask : true, //true打开遮罩
content(elem){
console.log(elem);
}
});
});
}
五、弹出层中Video播放器组件开发
1、video.css
.video{ position: relative; overflow: hidden; }
.video-content{ width: 100%; height: 100%; object-fit: cover; }
.video-controls{ width: 100%; position: absolute; bottom: -55px; left: 0; height: 50px; background: rgba(0, 0, 0, 0.8); transition: 0.5s; }
.video-progress{ width: 100%; height: 5px; background: #222223; position: relative; }
.video-progress-now{ width: 0; height: 100%; background: #ff6a03; position: absolute; left: 0; z-index: 1; }
.video-progress-suc{ width: 0; height: 100%; background: #666; position: absolute; left: 0; }
.video-progress-bar{ width: 14px; height: 14px; background: white; border-radius: 50%; position: absolute; left: 0; top: 0; margin-left: -7px; margin-top: -4px; z-index: 2; }
.video-play{ float: left; height: 45px; line-height: 45px; margin-left: 35px; }
.video-play i{ font-size: 20px; color: white; cursor: pointer; }
.video-time{ color: white; float: left; height: 45px; line-height: 45px; margin-left: 30px; }
.video-full{ float: right; height: 45px; line-height: 45px; margin-right: 20px; }
.video-full i{ color: white; font-size: 20px; cursor: pointer; }
.video-volume{ height: 45px; float: right; display: flex; align-items: center; margin-right: 30px; }
.video-volume i{ font-size: 20px; color: white; margin-right: 20px; cursor: pointer; }
.video-volprogress{ width: 100px; height: 5px; background: #222223; position: relative; }
.video-volprogress-now{ width: 50%; height: 100%; background: #ff6a03; }
.video-volprogress-bar{ width: 14px; height: 14px; background: white; border-radius: 50%; position: absolute; left: 50%; top: 0; margin: -7px; margin-left: -7px; margin-top: -4px; z-index: 2; }
2、video.ts
let styles = require('./video.css').default;
interface IVideo {
url : string;
elem : string | HTMLElement;
width? : string;
height? : string;
autoPlay? : boolean;
}
interface IComponent {
tempContainer : HTMLElement;
init : () => void;
template : () => void;
handle : () => void;
}
function video(options : IVideo){
return new Video(options);
}
class Video implements IComponent{
tempContainer;
constructor(private settings : IVideo) {
this.settings = Object.assign({
width : '100%',
height : '100%',
autoPlay : false
},this.settings);
this.init();
}
init() {
this.template();
this.handle();
}
template() {
this.tempContainer = document.createElement("div");
this.tempContainer.className = styles.video;
this.tempContainer.style.width = this.settings.width;
this.tempContainer.style.height = this.settings.height;
this.tempContainer.innerHTML = `
<video class="${styles['video-content']}" src="${this.settings.url}"></video>
<div class="${styles['video-controls']}">
<div class="${styles['video-progress']}">
<div class="${styles['video-progress-now']}"></div>
<div class="${styles['video-progress-suc']}"></div>
<div class="${styles['video-progress-bar']}"></div>
</div>
<div class="${styles['video-play']}">
<i class="iconfont iconbofang"></i>
</div>
<div class="${styles['video-time']}">
<span>00:00</span> / <span>00:00</span>
</div>
<div class="${styles['video-full']}">
<i class="iconfont iconquanping"></i>
</div>
<div class="${styles['video-volume']}">
<i class="iconfont iconyinliang"></i>
<div class="${styles['video-volprogress']}">
<div class="${styles['video-volprogress-now']}"></div>
<div class="${styles['video-volprogress-bar']}"></div>
</div>
</div>
</div>
`;
if(typeof this.settings.elem === 'object') {
this.settings.elem.appendChild(this.tempContainer);
}else {
document.querySelector(`this.settings.elem`).appendChild(this.tempContainer);
}
}
handle() {
let videoContent : HTMLVideoElement = this.tempContainer.querySelector(`.${styles['video-content']}`); //播放器video
let videoControls = this.tempContainer.querySelector(`.${styles['video-controls']}`); //视频下方控制组件
let videoPlay = this.tempContainer.querySelector(`.${styles['video-controls']} i`); //播放暂停按钮
let videoTimes = this.tempContainer.querySelectorAll(`.${styles['video-time']} span`);
let timer; //定时器
let videoFull = this.tempContainer.querySelector(`.${styles['video-full']} i`); //全屏按钮
let videoProgress = this.tempContainer.querySelectorAll(`.${styles['video-progress']} div`); //播放进度条组件
let videoVolProgress = this.tempContainer.querySelectorAll(`.${styles['video-volprogress']} div`); //音量进度条组件
videoContent.volume = 0.5; //初始化音量为50%
if(this.settings.autoPlay){ //判断是否自动播放
timer = setInterval(playing,1000);
videoContent.play();
}
//视频是否加载完毕
videoContent.addEventListener('canplay',() => {
//console.log('canplay');
videoTimes[1].innerHTML = formatTime(videoContent.duration);
});
//视频播放事件
videoContent.addEventListener('play',() => {
videoPlay.className = 'iconfont iconzanting';
timer = setInterval(playing, 100); //每隔0.1s执行一次
});
//视频暂停事件
videoContent.addEventListener('pause',() => {
videoPlay.className = 'iconfont iconbofang';
clearInterval(timer);
});
//播放暂停按钮点击事件
videoPlay.addEventListener('click',() => {
if(videoContent.paused){
videoContent.play();
}else{
videoContent.pause();
}
});
//全屏点击事件
videoFull.addEventListener('click',() => {
videoContent.requestFullscreen();
});
//播放小球鼠标拖拽事件
videoProgress[2].addEventListener('mousedown',function(ev:MouseEvent) {
let downX = ev.pageX;
let downL = this.offsetLeft;
document.onmousemove = (ev:MouseEvent)=> {
let scale = (ev.pageX - downX + downL + 8) / this.parentNode.offsetWidth;
if(scale < 0) {
scale = 0;
}else if(scale > 1) {
scale = 1;
}else {
videoProgress[0].style.width = scale * 100 + '%';
videoProgress[1].style.width = scale * 100 + '%';
this.style.left = scale * 100 + '%';
videoContent.currentTime = scale * videoContent.duration;
}
};
document.onmouseup = ()=> {
document.onmousemove = document.onmouseup = null;
};
ev.preventDefault();
});
//音量小球鼠标拖拽事件
videoVolProgress[1].addEventListener('mousedown',function(ev:MouseEvent) {
let downX = ev.pageX;
let downL = this.offsetLeft;
document.onmousemove = (ev:MouseEvent)=> {
let scale = (ev.pageX - downX + downL + 8) / this.parentNode.offsetWidth;
if(scale < 0) {
scale = 0;
}else if(scale > 1) {
scale = 1;
}else {
videoVolProgress[0].style.width = scale * 100 + '%';
this.style.left = scale * 100 + '%';
videoContent.volume = scale;
}
};
document.onmouseup = ()=> {
document.onmousemove = document.onmouseup = null;
};
ev.preventDefault();
});
//鼠标移入视频下方控制组件出现事件
this.tempContainer.addEventListener('mouseenter',function() {
videoControls.style.bottom = 0;
});
//鼠标移出视频下方控制组件消失事件
this.tempContainer.addEventListener('mouseleave',function() {
videoControls.style.bottom = '-55px';
});
//时间转换:秒->分:秒
function formatTime(number:number):string {
number = Math.round(number); //四舍五入
let min = Math.floor(number/60);
let hou = Math.floor(min/60);
min = min%60;
let sec = number%60;
if(hou != 0){
return fillZero(hou) + ':' + fillZero(min) + ':' + fillZero(sec);
}else{
if(min != 0){
return fillZero(min) + ':' + fillZero(sec);
}else{
return '00:' + fillZero(sec);
}
}
}
//时间补零
function fillZero(number:number):string {
if(number < 10) {
return '0' + number;
}else{
return number.toString();
}
}
//播放进度
function playing(){
let scale = videoContent.currentTime / videoContent.duration; //当前进度比
let scaleSuc = videoContent.buffered.end(0) / videoContent.duration; //缓存进度比
videoTimes[0].innerHTML = formatTime(videoContent.currentTime);
videoProgress[0].style.width = scale * 100 + '%';
videoProgress[1].style.width = scaleSuc * 100 + '%';
videoProgress[2].style.left = scale * 100 + '%'; //进度小球
}
}
}
export default video;
六、成品展示
(1)main.ts
import './main.css';
import popup from './components/popup/popup';
import video from './components/video/video';
let listItem = document.querySelectorAll('#list li');
for(let i=0;i<listItem.length;i++){
listItem[i].addEventListener('click',function(){
let url = this.dataset.url;
let title = this.dataset.title;
//console.log(url,title);
popup({
width : '880px',
height : '556px',
title,
pos : 'center',
mask : true, //true打开遮罩
content(elem){
//console.log(elem);
video({
url,
elem,
autoPlay : true
});
}
});
});
}
(2)项目总结构
(3)启动