1
今天我们来学习一些知识点,拓宽视野,从而将来高屋建瓴

五个知识点

阅读完本文,读者可以学习到以下知识点:

    1. Vue3中xgplayer视频播放
    1. driver.js做操作导航
    1. html2canvas做截图
    1. 仿写vue的{{}}语法
    1. 仿写vue中:style的用法

Vue3中xgplayer视频播放

什么是xgplayer?

  • xgplayer是字节的西瓜团队出品的一款比较好用的音视频解决方案
  • 背靠大厂,前途无限,放心靠谱
  • 字节具体应用的地方有:西瓜视频和抖音视频(不会烂尾)
  • 而且github上xgplayer的star快8K了(毕竟还很年轻)
  • 要知道,老牌的video.js这么多年也才37.4K的star
  • 大家审查DOM可以看到对应结构层级

配置封装xgplayer

这里是在Vue3中,去举这个例子,主要是学习xgplayer的配置项语法规则

首先来一个DOM元素,作为xgplayer的占位的地方

<template>
    <div id="xgPlayerWrap"></div>
</template>

然后把xgplayer下载一下,在引入到对应.vue文件中使用,指定配置项,就能够出来对应效果了

下载npm i xgplayer --save

这里是单独封装一个MyVideo组件,便于复用

<template>
    <div id="xgPlayerWrap"></div>
</template>

<script setup>
import { ref, onMounted, reactive, computed } from 'vue';
import Player, { Events } from 'xgplayer'; // 引入西瓜视频模块
import 'xgplayer/dist/index.min.css'; // 引入西瓜视频样式

import { conf } from "./conf"; // 配置文件单独拎出来一个js文件

defineOptions({ name: 'MyVideo' })

onMounted(() => { init() })

let player = null // 实例

console.log('Events', Events);

const init = () => {
    player = new Player({
        ...conf
    });
    player.on(Events.PLAY, (ev) => {
        console.log('-播放开始-', ev);
    })
    player.on(Events.PAUSE, (ev) => {
        console.log('-播放结束-', ev);
    })
    player.on('loadedmetadata', (ev) => {
        console.log('-媒体数据加载好了-', ev);
    })
    player.on(Events.SEEKED, (ev) => {
        console.log('-跳着播放-', ev);
    })
    // 等各种监听事件
}
</script>

<style scoped>
#xgPlayerWrap { flex: auto; }
#xgPlayerWrap video { width: 100%; }
</style>

注意,这里的配置项挺多的,笔者取出来一部分常用的,大家可以复制粘贴直接用,或者再改一下。

以下是单独拎出来的配置项的js文件

./conf.js文件

export const conf = {
    /**
     * 基础功能配置
     * */
    id: 'xgPlayerWrap', // 占位dom元素
    width: 600, height: 400, // 视频宽高尺寸
    url: 'https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4', // 视频源
    poster: "http://ashuai.work/static/img/avantar.png", // 视频封面
    autoplay: false, // 是否自动播放,不自动播放,浏览器有限制规则
    autoplayMuted: false, // 是否自动播放(静音播放)
    videoInit: true, // 是否默认初始化video,默认初始化,默认true
    playsinline: true, // 是否启用内联播放模式,仅移动端生效
    defaultPlaybackRate: 1, // 默认播放速度(可选:0.5/0.75/1/1.5/2等)
    volume: 0.72, // 播放音量(可选:0 ~ 1)
    loop: false, // 是否循环播放,默认不循环播放
    startTime: 0, // 点播模式下,初始起播时间
    videoAttributes: {}, // video扩展属性,暂且不配置
    lang: 'zh-cn', // 播放器初始显示语言,设置为中文
    fluid: true, // 是否流式布局(宽高优先于流失布局,默认16:9)注掉上方宽高看效果
    fitVideoSize: 'fixed', // 保持容器宽/高,不做适配,按照容器来
    videoFillMode: 'auto', // 宽高不够自动底色填充(fill拉伸填充等...)
    seekedStatus: 'play', // 跳转后继续播放
    // 播放器进度条故事点信息数组
    progressDot: [
        {
            id: 0,         // 唯一标识,用于删除的时候索引
            time: 30,      // 展示的时间点,例子为在播放到10s钟的时候展示
            text: '进度条信息提示...',  // hover的时候展示文案,可以为空
            duration: 5,   // 展示时间跨度,单位为s
            style: {       // 指定样式
                backgroundColor: 'pink'
            }
        },
    ],
    thumbnail: null, // 进度条预览图配置,普通业务用不到
    marginControls: false, // 是否开启画面和控制栏分离模式,不开启空间多一些
    domEventType: 'default', // 响应的事件类型,不用指定,用默认的即可
    /**
     * 交互功能配置(一般使用默认即可)
     * */
    /**
     * 插件配置,根据需求自选
     * */
    icons: {}, // 使用默认的icon图标
    i18n: [], // 使用默认的中文
    // 自定义一些颜色
    commonStyle: {
        progressColor: 'green', // 整个进度条颜色
        playedColor: 'chocolate', // 已播放的进度条颜色
        volumeColor: 'pink', // 音量大小竖向滑块颜色
    },
    controls: true, // 是否使用底部控制栏,默认使用
    miniprogress: true, // 是否使用mini进度条(当底部控制栏隐藏时生效)
    screenShot: false, // 关闭截图功能
    rotate: true, // 是否使用视频旋转插件,默认不使用
    download: true, // 是否使用下载按钮,一般不用,一般自定义控制
    pip: true, // 使用使用画中画模式,默认不用
    mini: true, // 是否使用小屏幕控件
    cssFullscreen: true, // 是否使用网页样式全屏按钮开关
    playbackRate: [0.5, 1, 1.5, 2, 3], //传入倍速可选数组
    playbackRate: true, //false,禁用倍速播放(即控制栏不显示)
    keyShortcut: false, // 是否开启快捷键模式
}

因为MyVideo已经简单封装好了,那么直接使用即可。

使用封装的xgplayer

<template>
    <div class="out">
        <MyVideo />
    </div>
</template>

<script setup>
import MyVideo from "../../components/MyVideo/MyVideo.vue";
</script>

<style>
.out { width: 600px; }
</style>

效果图

完整代码

完整代码,请移步笔者的github:https://github.com/shuirongshuifu/vue3-echarts5-example

西瓜播放器官网:https://v2.h5player.bytedance.com/

xgplayer中的xg就是xigua西瓜的首字母拼音缩写...

driver.js做操作导航

什么是driver.js?

  • 任何技术的诞生,都有对应的应用场景
  • 比如一个新手刚注册某个平台
  • 那么,我们除了给到用户帮助文档让他自己看之外
  • 还可以,来点交互操作动画让用户快速更好的理解系统平台的功能

大家看一下效果图即明了:

效果图

demo代码

先引入CDN

<script src="https://cdn.jsdelivr.net/npm/driver.js@1.0.1/dist/driver.js.iife.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/driver.js@1.0.1/dist/driver.css" />

引入CDN后,模块会被挂在windows上

直接就能使用window.driver.js.driver实例了

然后再加上配置项:

// 引入的cdn的driver.js挂在windows上,可直接用
const driver = window.driver.js.driver;
// 实例化对象,传相应参数
const driverObj = driver({
    showProgress: true,
    steps: [
        { element: '.one', popover: { title: '第一步', description: '阅读001' } },
        { element: '.two', popover: { title: '第二步', description: '阅读002' } },
        { element: '.three', popover: { title: '第三步', description: '阅读003' } },
        { element: '.four', popover: { title: '第四步', description: '阅读004' } },
    ]
});

最后,在合适的时机驱动,比如点击按钮驱动driver.js开始做导航功能

document.getElementById('startTour').addEventListener('click', function () {
            driverObj.drive();
        });

完整代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Driver.js Demo</title>
    <!-- CDN -->
    <script src="https://cdn.jsdelivr.net/npm/driver.js@1.0.1/dist/driver.js.iife.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/driver.js@1.0.1/dist/driver.css" />
    <style>
        li {
            margin-bottom: 16px;
        }
    </style>
</head>

<body>
    <ul>
        <li class="one">白日依山尽</li>
        <li class="two">黄河入海流</li>
        <li class="three">欲穷千里目</li>
        <li class="four">更上一层楼</li>
    </ul>

    <button id="startTour">点击开始指引</button>

    <script>
        // 引入的cdn的driver.js挂在windows上,可直接用
        const driver = window.driver.js.driver;
        // 实例化对象,传相应参数
        const driverObj = driver({
            showProgress: true,
            steps: [
                { element: '.one', popover: { title: '第一步', description: '阅读001' } },
                { element: '.two', popover: { title: '第二步', description: '阅读002' } },
                { element: '.three', popover: { title: '第三步', description: '阅读003' } },
                { element: '.four', popover: { title: '第四步', description: '阅读004' } },
            ]
        });
        document.getElementById('startTour').addEventListener('click', function () {
            driverObj.drive();
        });
        // 官网:https://driverjs.com/
    </script>
</body>

</html>

html2canvas做截图

什么是html2canvas?

简而言之,html2canvas就做了三件事

  • 读取DOM:html2canvas会遍历指定的DOM节点(或者默认的可视区域),收集其中的元素结构和样式信息。
  • 渲染到canvas:利用收集到的信息,它会在一个隐藏的canvas元素上重新绘制这些元素和样式,尽力模拟原始HTML的外观。
  • 输出图像:一旦渲染完成,你可以选择将canvas的内容导出为Base64编码的字符串,或者创建一个图片链接,甚至下载为文件。

读取DOM、渲染到canvas、输出图像

基于此,我们可以用来做截图功能

效果图

第一步,引入html2canvas库

<!-- 引入html2canvas库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.3/html2canvas.min.js"></script>

第二步,使用html2canvas的toDataURL把指定dom的内容,转成base64图片输入即可

function takeScreenshot(elementId, fileName) {
    // 选中指定的dom元素
    var element = document.getElementById(elementId);
    // 执行html2canvas的方法
    html2canvas(element).then(function (canvas) {
        // 将canvas转换为data URL
        var dataURL = canvas.toDataURL("image/png");
        // 创建一个img元素来显示截图
        var img = document.createElement('img');
        // 指定图片地址 
        img.src = dataURL;
        // 将截图结果显示在页面上
        document.body.appendChild(img);
    }).catch(function (error) {
        console.error('Error while taking screenshot:', error);
    });
}

完整代码

CV演示

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>截图</title>
    <!-- 引入html2canvas库 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.3.3/html2canvas.min.js"></script>
    <style>
        ul {
            background-color: pink;
            width: 120px;
        }
    </style>
</head>

<body>
    <ul id="target">
        <li>白日依山尽</li>
        <li>黄河入海流</li>
        <li>欲穷千里目</li>
        <li>更上一层楼</li>
    </ul>
    <button>一键截图并下载</button>
    <hr>
    <script>
        function takeScreenshot(elementId, fileName) {
            var element = document.getElementById(elementId);
            html2canvas(element).then(function (canvas) {
                // 将canvas转换为data URL
                var dataURL = canvas.toDataURL("image/png");
                // 创建一个img元素来显示截图
                var img = document.createElement('img');
                // 指定图片地址 
                img.src = dataURL;
                // 将截图结果显示在页面上
                document.body.appendChild(img);
                // 并且下载
                downloadImage(dataURL, fileName);
            }).catch(function (error) {
                console.error('Error while taking screenshot:', error);
            });
        }

        function downloadImage(dataURL, fileName = 'screenshot.png') {
            // 创建隐藏的可下载链接
            var downloadLink = document.createElement("a");
            downloadLink.href = dataURL;
            downloadLink.download = fileName; // 设置默认文件名
            // 隐藏链接
            downloadLink.style.display = 'none';
            document.body.appendChild(downloadLink);
            // 触发点击
            downloadLink.click();
            // 稍后移除链接
            setTimeout(function () {
                document.body.removeChild(downloadLink);
            }, 0);
        }

        document.querySelector('button').onclick = () => {
            takeScreenshot('target', '下载');
        }

    </script>
</body>

</html>

仿写vue的{{}}语法

思路分析

  • vue的双花括号表达式实际上就是一个“标识”
  • 但凡是一个dom标签里面,有这个{{变量}},vue都会去查找双花括号中的对应变量对应的值
  • 然后把这个值,替换到这个dom标签里面
  • 实际上,我们自己也可以仿写的。
  • 比如vue用{{变量}},我们也可以用{变量}
  • 那么
  • 我们第一步,可以document文档中所有的标签(要排除script标签)
  • 第二步,看看这个标签里面的内容是否包含{}这个标识,若是包含,再第三步
  • 第三步,就提取这个{}中的变量,比如这个是<h2>{vvv}</h2>(即提取出来这个vvv)
  • 第四步,再去查找定义的变量中,有没有这个vvv对应的值,然后替换一下

代码层面

CV演示

<!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>
    <h1>{aaa}</h1>
    <h2>{bbb}</h2>
    <h3>{ccc}</h3>
    <h4>唐僧</h4>
    <script>
        // 判断是否有花括号
        function isHaveKuoHao(str) {
            if (str[0] == '{' && str.at(-1) == '}') {
                return true
            } else {
                return false
            }
        }
        // 提取花括号中的内容,也就是key
        function getKuoHaoKey(str) {
            let key = str.split('{')[1].split('}')[0]
            return key
        }
        // data数据函数
        window.data = function () {
            return {
                aaa: '孙悟空',
                bbb: '猪八戒',
                ccc: '沙和尚'
            }
        }
        // 获取所有body的元素标签
        const bodyChildren = document.body.children;
        // 遍历操作
        for (const dom of bodyChildren) {
            // 排除掉script标签
            if (dom.tagName != 'SCRIPT') {
                // 包含双花括号的
                if (isHaveKuoHao(dom.innerHTML)) {
                    // 提取花括号中的内容,就是字段key
                    let key = getKuoHaoKey(dom.innerHTML)
                    // data函数定义的字典
                    let Dict = data()
                    // 重新赋值,即改变了数据视图
                    dom.innerHTML = Dict[key]
                }
            }
        }
    </script>
</body>

</html>
这里笔者只演示了一层对象,还有多层对象,就是要用递归的操作了。不过思路最重要,有思路了,问题就好办了

仿写vue中:style的用法

我们知道,vue中:style有一种写成函数返回对象的写法,如下:

<div :style="{ color: dynamicColor, fontSize: fontSize + 'px' }"></div>

即:

<div :style="styleFn"></div>

const styleFn = () = {
    return { color: 'pink', fontSize:  '24px' }
}

实际上,我们也可以自己实现对应功能,仿写,思路如下:

仿写思路

  • 比如我们有这样的<h3 :style="fn">新的一天,打工仔加油</h3>
  • 我们只是需要,去拿到这个dom身上的:style属性的值
  • 这个值是一个函数,然后去methods对象中找到这个函数
  • 再把这个函数的返回值,给应用到这个dom上即可
  • 代码如下:

CV大家再看一下,结合注释,就明白啦

代码层面

<!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>
    <h3 :style="fn">新的一天,打工仔加油</h3>
    <script>
        /**
         * window全局,注册一个中转函数,用来把传进来的函数执行一下
         * */
        window.mounted = function (cb) { cb() }

        /**
         * 定义对象,里面有很多函数,每一个函数的返回值,都是可以使用的样式
         * */
        const methods = {
            fn() {
                let conf = {
                    background: 'pink',
                    color: 'green'
                }
                return conf
            },
            fn1() {
                let conf = {
                    background: 'chocolate',
                    color: 'deepskyblue'
                }
                return conf
            },
            fn2() {
                let conf = {
                    background: 'gray',
                    color: 'yellowgreen'
                }
                return conf
            },
        }

        /**
         * 钩子函数
         * */
        mounted(() => {
            // 选中dom
            let h3Dom = document.querySelector('h3')
            // 获取:style绑定的值,对应就是methods对象中的key
            let val = h3Dom.getAttribute(':style') // fn
            // 遍历对比查找
            for (const fnName in methods) {
                if (val == fnName) { // 找到了
                    let targetFn = methods[fnName]
                    let styleObj = targetFn()
                    // 动态给标签元素,添加样式
                    for (const key in styleObj) {
                        // 原生js语法
                        h3Dom.style[key] = styleObj[key]
                    }
                }
            }
        })
    </script>
</body>

</html>

这里,笔者并没有做一些边界值的控制

因为更多的是提供代码操作思路

而Vue框架则是把特别多的情况考虑到,

然后做边界控制,兜错处理

这就是框架的复杂之处(当然还有别的方面...)

好记性不如烂笔头,记录一下吧...

A good memory is better than a bad pen. Write it down...

以上快速学习的案例代码,全部都在笔者的github仓库中。

仓库地址: https://github.com/shuirongshuifu/vue3-echarts5-example


水冗水孚
1.1k 声望585 粉丝

每一个不曾起舞的日子,都是对生命的辜负