一、简介

TypeScript是JavaScript类型的超集,它可以编译成纯JavaScript。

  • TypeScript可以再任何浏览器、任何计算机和任何操作系统上运行,并且是开源免费的。

为什么要使用TypeScript?

  • TypeScript可以提供静态类型检查,规范团队的编码及使用方式,适合大型项目的开发。
  • IDE的友好提示,也是适合大型项目的开发。

项目已上传CSDN:点击访问

二、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中添加引入规则:![在这里插入图片描述](https://img-blog.csdnimg.cn/20210202094040975.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjQ1MzU4Mg==,size_16,color_FFFFFF,t_70

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)启动
在这里插入图片描述
在这里插入图片描述

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐