数据格式定义
工欲善其事必先利其器, 定义一套数据格式有助于理顺思路, 所以开始编写代码第一步应该先定义一套数据格式
回顾一下设想的调用方式 new dpVideo(parentDom,urlList,options)
parentDom为外层盒子
urlList 为video的src
options 为配置项
这里的urlList格式和options需要设计数据格式
首先说一下urlList的数据格式吧
urlList数据格式
允许用户传入字符串 , 数组两种形式的数据
设想中需要拥有切换下一P, 选择清晰度, 选集的功能
所以目前暂定的数据格式如下
// 格式1 : “http://asdasd”
// 格式2 : [{lebal:“张三的幸福生活1”,defaultId:0,urlList:[{id:0,url:‘http://asdasd’,label:‘360P’}]}},“http://asdasd”]
格式1说明
只传入字符串的情况下不出现切换下一P, 选择清晰度, 选集的功能
格式2说明
当数组长度等于1或者当前选中数组的下标 等于数组长度-1时, 不出现切换下一集以及选集按钮
当前选中的元素为字符串或者当前选中元素的urlList长度==1时, 不出现选择清晰度
options数据格式就是前面文章提到的
interface optionsFormat{
barrageShow?:boolean, // 是否开启弹幕功能, 默认为true
barrageList?:Array<{
msg:"",//弹幕内容
time:Number // 弹幕发送时间,相对于视频的毫秒数
}>// 弹幕数组默认为空,上方标识弹幕数据格式
barrageScope?: number, // 默认为1500毫秒, 在可视区域停留时间
title?: {
// 标题
show: boolean, // 标题是否显示
label: string // 标题显示内容
},
// 控制条显示
controller?: boolean, // 默认显示
// 控制条配置项, 如果用户手动指定则按照用户手动指定的解析
controllerOption?: {
play?: boolean, // 播放按钮, 默认为true
next?: boolean, // 下一个按钮, 默认为false, 如果传入的videoUrl的格式为2或者4的话则默认为true
clarity?: boolean, //选择清晰度, 默认为false, 如果传入的videoUrl格式为3,4为true
volume?: boolean, // 音量默认为true
playbackRate?: boolean, // 播放速度默认为true
playbackRateList?: Array<{ speed:number,label:string,default?:boolean}>, // 播放速度数组默认值
togglePip?: boolean, // 画中画 默认true
documentFull?: boolean, // 网页全屏 默认true
windowFull?: boolean, // 全屏 默认true
Mirror?: boolean // 镜像 默认false
},
_parentSelf?:any
}
文件中代码如下
src\core\baseType.d.ts
// videoList 四种数据格式
// 格式1 : "http://asdasd"
// 格式2 : [{defaultId:0,urlList:[{id:0,url:'http://asdasd',label:'360P'}]}}]
export interface urlFormat1 {
defaultId:number|string,
urlList: Array<urlFormat2>
}
export interface urlFormat2 {
id:number,
url:string,
label:string
}
// 配置项数据类型
export interface optionsFormat {
barrageShow?:boolean, // 是否开启弹幕功能, 默认为true
barrageList?:Array<{
msg:"",//弹幕内容
time:Number // 弹幕发送时间,相对于视频的毫秒数
}>// 弹幕数组默认为空,上方标识弹幕数据格式
barrageScope?: number, // 默认为1500毫秒, 在可视区域停留时间
title?: {
// 标题
show: boolean, // 标题是否显示
label: string // 标题显示内容
},
// 控制条显示
controller?: boolean, // 默认显示
// 控制条配置项, 如果用户手动指定则按照用户手动指定的解析
controllerOption?: {
play?: boolean, // 播放按钮, 默认为true
next?: boolean, // 下一个按钮, 默认为false, 如果传入的videoUrl的格式为2或者4的话则默认为true
clarity?: boolean, //选择清晰度, 默认为false, 如果传入的videoUrl格式为3,4为true
volume?: boolean, // 音量默认为true
playbackRate?: boolean, // 播放速度默认为true
playbackRateList?: Array<{ speed:number,label:string,default?:boolean}>, // 播放速度数组默认值
togglePip?: boolean, // 画中画 默认true
documentFull?: boolean, // 网页全屏 默认true
windowFull?: boolean, // 全屏 默认true
Mirror?: boolean // 镜像 默认false
},
_parentSelf?:any
}
// 导出集成的UrlListType
export type UrlListType = Array<string | urlFormat1> | string;
工具类编写
工具类是用来提炼一些常用代码和解耦的
因为不是写html,所以需要一套完整的设置HTML,创建HTML标签的方法
目前我写下来有关HTML操作的主要是
- 元素挂载Style
- 元素挂载class
- 元素绑定事件
- 创建元素
还需要判断urlList的类型
以及格式化时间的函数
元素挂载Style实现
export function styleList(dom: HTMLElement, styleList: object) {
for (let item in styleList) {
dom.style[item] = styleList[item];
}
}
元素挂载class实现
export function classNameList(dom: HTMLElement, className: Array<string>) {
dom.classList.add(...(className.filter(item=>item.length!=0)))
}
元素绑定事件实现
export function bindMethods(dom: HTMLElement, methods: object) {
let keys = Object.keys(methods);
for (let i = 0; i < keys.length; i++) {
dom.addEventListener(keys[i], methods[keys[i]])
}
}
创建元素实现
创建元素比较复杂, 使用递归实现
interface DomList {
tag: string, // 创建的tag名
innerText?: string, // tag中的innerText
className?: Array<string>, // tag类名
style?: object, // tag行内样式
children?: Array<DomList>, // tag子元素
methods?: object // tag绑定的事件
}
// 创建div的渲染函数
export function render( domList: DomList,parentDom: HTMLElement | null = null): HTMLElement {
let c_dom = document.createElement(domList.tag);
if (domList.innerText) {
c_dom.innerText = domList.innerText;
}
if (domList.className) {
classNameList(c_dom, domList.className);
}
if (domList.style) {
styleList(c_dom, domList.style);
}
if (domList.methods) {
bindMethods(c_dom, domList.methods);
}
if (domList.children && domList.children.length > 0) {
for (let i = 0; i < domList.children.length; i++) {
render(domList.children[i], c_dom);
}
}
if (parentDom !== null) {
parentDom.append(c_dom);
return parentDom;
} else {
return c_dom;
}
}
适用方式大概是下面这个样子的(使用方式怎么说呢, 感觉和jsx有些许相似, 也可是使用map方法,具体使用方式后面控制条实现篇说)
let volumeContrller = render({
tag: "div",
className: ["dp-volume-contrller"],
children: [
{tag: "span", className: ["dp-volume-msg"], innerText: this.video.volume * 100 + ""},
{
tag: "div",
className: ["dp-volume-contrller-box"],
children: [
{
tag: "div",
className: ["dp-volume-contrller-point"],
style: {
bottom: 100 * this.video.volume + "px"
},
methods: {
mousedown: () => {
let _that = this
this.video.muted = false;
function mouseMoveFn(e) {
if (_that.video.volume + (e.movementY * -1) / 100 > 1 || _that.video.volume + (e.movementY * -1) / 100 < 0) {
return;
}
_that.video.volume += (e.movementY * -1) / 100
_that.changeVolumeDom();
}
function mouseUpFn() {
window.removeEventListener("mousemove", mouseMoveFn)
window.removeEventListener("mouseup", mouseUpFn)
}
window.addEventListener("mousemove", mouseMoveFn)
window.addEventListener("mouseup", mouseUpFn)
}
}
},
{
tag: "div",
className: ["dp-volume-contrller-bg"],
style: {height: 100 * this.video.volume + "px"}
},
]
}
]
})
判断urlList的类型实现
export function whatUrlType(obj): number {
// 格式进行修改 :
// 格式1: "http://asdasd"
// 格式2: ["http://asdasd",{defaultId:0,urlList:[{id:0,url:'http://asdasd',label:'360P'}]}]
let flag = 0;
if (Object.prototype.toString.call(obj) === '[object String]') {
flag = 1;
} else if (Object.prototype.toString.call(obj) === "[object Array]") {
flag = 2;
}
return flag;
}
格式化时间实现
export function formatTime (time:number):string {
// 此处只需要 分钟:秒钟 的格式
let ceilTime = Math.ceil(time);
let m = Math.floor(ceilTime/60);
let s = ceilTime % 60;
let r_m = m < 10 ? "0" + m : m;
let r_s = s < 10 ? "0" + s : s;
return r_m + ":" + r_s;
}
src\core\utils.ts
// 检查urlListType到底是哪种类型
export function whatUrlType(obj): number {
// 格式进行修改 :
// 格式1: "http://asdasd"
// 格式2: ["http://asdasd",{defaultId:0,urlList:[{id:0,url:'http://asdasd',label:'360P'}]}]
let flag = 0;
if (Object.prototype.toString.call(obj) === '[object String]') {
flag = 1;
} else if (Object.prototype.toString.call(obj) === "[object Array]") {
flag = 2;
}
return flag;
}
// 获取元素真实属性
export function getStyle(element, style) {
return element.currentStyle ? element.currentStyle[style] : getComputedStyle(element)[style];
}
// 元素挂载style
export function styleList(dom: HTMLElement, styleList: object) {
for (let item in styleList) {
dom.style[item] = styleList[item];
}
}
// 元素挂载className
export function classNameList(dom: HTMLElement, className: Array<string>) {
dom.classList.add(...(className.filter(item=>item.length!=0)))
}
// 绑定事件
export function bindMethods(dom: HTMLElement, methods: object) {
let keys = Object.keys(methods);
for (let i = 0; i < keys.length; i++) {
dom.addEventListener(keys[i], methods[keys[i]])
}
}
interface DomList {
tag: string, // 创建的tag名
innerText?: string, // tag中的innerText
className?: Array<string>, // tag类名
style?: object, // tag行内样式
children?: Array<DomList>, // tag子元素
methods?: object // tag绑定的事件
}
// 创建div的渲染函数
export function render( domList: DomList,parentDom: HTMLElement | null = null): HTMLElement {
let c_dom = document.createElement(domList.tag);
if (domList.innerText) {
c_dom.innerText = domList.innerText;
}
if (domList.className) {
classNameList(c_dom, domList.className);
}
if (domList.style) {
styleList(c_dom, domList.style);
}
if (domList.methods) {
bindMethods(c_dom, domList.methods);
}
if (domList.children && domList.children.length > 0) {
for (let i = 0; i < domList.children.length; i++) {
render(domList.children[i], c_dom);
}
}
if (parentDom !== null) {
parentDom.append(c_dom);
return parentDom;
} else {
return c_dom;
}
}
// 格式化时间
export function formatTime (time:number):string {
let ceilTime = Math.ceil(time);
let m = Math.floor(ceilTime/60);
let s = ceilTime % 60;
let r_m = m < 10 ? "0" + m : m;
let r_s = s < 10 ? "0" + s : s;
return r_m + ":" + r_s;
}
本来打算这篇写完核心类实现的…
但是篇幅不知不觉就挺长的了…估计再写下去也没人看的下去了
下篇写核心类实现