我是如何在markdown编辑器中完成视频的插入和播放的

news2024/11/17 15:58:10

如果你有更好用的编辑器组件,请一定推荐给我!!!(最好附带使用说明🤓️)

介绍

在开发一个社区页面的时候,需要完成发帖、浏览帖子的能力。这里考虑接入markdown编辑器进行开发,也符合大多数用户的习惯。

首先是编辑器的选择,经过深思熟虑(随缘)后,确定了为 ByteMd, 主要是平时用掘金看到它们也是这个编辑器。
安装很简单:

npm install bytemd

用起来更简单:

    import '@/assets/static/editor_index.css' // 引入布局文件,防止样式错乱

    // 可以去了解下每个插件的功能,都是现有的,不再赘述
    const plugins = [gfm(), highlight(), breaks(), frontmatter(), footnotes(), gemoji(), mediumZoom()]
    <Editor
        locale={zhHans} // 选择语言
        value={content} // 内容区域
        plugins={plugins} //支持的插件
        onChange={(v) => {
            setContent(v);
        }}
        // 自定义内容区域媒体文件的上传
        uploadImages={async (files): Promise<Pick<Image, "alt" | "title" | "url">[]> => {
            console.log("files", files);
            const imageUrl = await uploadImage(files[0]);
            return [
                {
                    title: files.map((i) => i.name).join(),
                    url: imageUrl,
                },
            ];
        }}
    />

这样就很快的实现了一个markdown的编辑器。不出问题的话就要出问题了

要支持上传视频

挠头,这个功能区没有上传视频的区域啊,这咋搞呢?去掘金上看看,掘金是有的,那肯定是可以有的。那么就看看如何在tools栏增加一个视频的icon
bytemd本身支持对tools bar做扩展,这样就简单了很多。可以拉下来源码看一下,新增一个tool的代码也很简单
    export default function videoPlugin(saveEditorContext: (editorContext: BytemdEditorContext) => void) {
        const ADD_VIDEO = "url" // 视频tool的展示icon
        const handleUploadVideo = () => {
          window.dispatchEvent(showUploadAVDialog) // 点击时的事件处理,这里也是发通知给别处去处理了
        }
        
        return {
            actions: [
                {
                    title: '视频',
                    icon: `<img src="${ADD_VIDEO}" alt={"logo"} style="width: 24px; height: 24px;"/>`,
                    handler: {
                        type: 'action',
                        click(context: BytemdEditorContext) {
                            handleUploadVideo()
                            saveEditorContext(context)
                        },
                    },
                },
            ]
        }
    }

然后 把这个** videoPlugin** 加到前面的plugins列表里面

image.png
这样就有了一个上传视频的icon,点击后需要你来实现一下打开文件选择器 -> 选择视频 -> 上传到服务器 -> 处理上传后的链接 这套逻辑(不一定是这样,得看具体的业务流程)

当然,这肯定还没完,上传之后,需要像图片一样,在编辑区把视频展示出来吧。
一开始想得很简单,直接用一个<\iframe>或者 <\video> 标签,把视频播出了不就好了。but,这肯定是行不通的,为了防止XSS,这些特殊的标签都是不允许直接在输入框内进行使用的。掘金不太一样,它只能插入它们指定播放源的视频,也就是说要保证视频源的可靠才能插入。
我们业务暂时不需要考虑,都是自己人,也不会干这种事。于是参考了其他一些网站的实现,直接将视频内容展示为一个视频播放的缩略图。对,就是下面的 – ![\AVFile](${url})\n

    const handleUploadSuccess = (url: string, file: File) => {
        if (editorContext) {
            // 创建一个视频播放器的 HTML 代码
            const videoHtml = `![AVFile](${url})\n`;
            const {line, ch} = editorContext.editor.getCursor();
            editorContext.editor.replaceRange(videoHtml, {line, ch});
            setContent(editorContext.editor.getValue());
        } else {
            message.error("上传失败,请重试")
        }
    };

在视频上传完成后,我们在插入视频的文本光标后面 主动添加视频的缩略图展示。

要注意一点,这里用到的 editorContext 是前面 videoPlugin组件中获取的,需要在用的组件内保存一下。

细心的你肯定会问:这里的url是视频的URL,用图片的语法展示会裂吧?
确实会有这个问题,于是我们还需要对整个编辑区的内容做一个处理,把展示的内容里面 视频的url替换成统一的视频缩略图(注意,只是展示位置的图片被替换了,实际上保存的还是视频的URL哈

于是我们再实现一个转换内容的插件,前提是基于你已经了解了 bytemd的 这几个接口的含义和调用时机,我不是来讲原理的,所以就不细嗦了。
image.png

    export default function videoEmbedPlugin() {
        const DEFALUT_VIDEO_URL = 'http://cdn.qboost.woa.com/files/community_article_pic/%E8%A7%86%E9%A2%91%20%281%29_1716435376866.png'
        return {
            // @ts-ignore
            remark: (processor) =>
                // @ts-ignore
                processor.use(() => (tree) => {
                    visit(tree, 'image', (node) => {
                        if (node.alt === 'AVFile') {
                            // 替换图片 URL
                            node.url = DEFALUT_VIDEO_URL;
                        }
                    });
                }),
        };
    }

OK,编辑区支持上传视频的能力也算是大功告成了。不过,查看markdown文章的展示区也还需要适配,毕竟它是不可能自动播放你添加上去的视频的。

查看视频

对于展示区的处理,会简单很多,因为我们在上传视频的时候,对视频的url做了特殊处理,也就是在前面添加了[AVFile], 那么我们就可以在布局完成后,通过遍历展示区的html结点,找到 AVFile的img标签,然后将html中的这部分标签,替换为 <video>标签,就可以播放视频了

// 替换为<video>标签
export function handlePicToVideo() {
    const markdownBodyElement = document.querySelector('.markdown-body');
    if (markdownBodyElement) {
        // 查找所有的 <p><img> 元素
        const images = markdownBodyElement.querySelectorAll('img.medium-zoom-image[alt="AVFile"]');

        images.forEach((img) => {
            const videoUrl = img.getAttribute('src');
            // 创建 video 元素
            const videoElement = document.createElement('video');
            videoElement.setAttribute('controls', 'controls');
            videoElement.setAttribute('width', 'auto');
            const sourceElement = document.createElement('source');
            sourceElement.setAttribute('src', videoUrl!);
            sourceElement.setAttribute('type', 'video/mp4');
            videoElement.appendChild(sourceElement);
            const noSupportText = document.createTextNode('Sorry, your browser doesn't support embedded videos.');
            videoElement.appendChild(noSupportText);

            // 替换 img 元素为 video 元素
            const parentParagraph = img.parentElement;
            if (parentParagraph) {
                parentParagraph.replaceChild(videoElement, img);
            }
        });
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1858956.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MM-LLM:Internvl_chat.v1.5论文解读

这个模型在我自己测的结果上也是表现优异&#xff0c;和glm4v打得有来有回。是目前开源的效果最佳的模型之一。 官方的评测榜单&#xff1a;https://huggingface.co/spaces/opencompass/open_vlm_leaderboard 摘要&#xff1a; 直接说提出了一个拉近开源和商业多模态模型的开…

强强联合 极光推送(JPush)成为华为生态市场首家推送类SDK服务商

近日&#xff0c;中国领先的客户互动和营销科技服务商&#xff0c;极光&#xff08;Aurora Mobile&#xff0c;纳斯达克股票代码&#xff1a;JG&#xff09;的核心产品极光推送&#xff08;JPush&#xff09;顺利通过华为开发者联盟的多项测试及审核&#xff0c;成为首家在Harm…

RN开发搬砖经验之—“Calculated frame index should never be lower than 0“崩溃问题分析

问题重现 崩溃堆栈&#xff1a; Back traces starts. java.lang.RuntimeException: java.lang.IllegalStateException: Calculated frame index should never be lower than 0at com.facebook.react.animated.NativeAnimatedModule$1.doFrameGuarded(NativeAnimatedModule.ja…

【FFMPEG+Mediamtx】 本地RTSP测试推流记录

利用本地FFMPEGMediamtx 搭建本地RTSP测试推流电脑摄像头 起因 本来要用qt的qml的Video做摄像头测试。 &#x1f614;但是&#xff0c;不在现场&#xff0c;本地测试&#xff0c;又要测试rtsp流&#xff0c;又因为搜了一圈找不到一个比较好的在线测试rtsp推流网址&#x1f6…

JAVA每日作业day6.24

ok了家人们今天学习了一些关键字&#xff0c;用法和注意事项&#xff0c;静态代码块这些知识&#xff0c;闲话少叙我们一起看看吧。 一&#xff0c;final关键字 1.1 final关键字的概述 final&#xff1a; 不可改变。可以用于修饰类、方法和变量。 类&#xff1a;被修饰的类&a…

Hex文件与BIn文件的关系

单片机中Hex文件与BIn文件的关系 前言 时间&#xff1a;2024/6/24 官方网站&#xff1a;.hex文件解析&#xff1a;Hexadecimal (Intel-Format) File (.hex) Definition 参考博客&#xff1a;实现STM32烧写程序-(3) Hex文件结构-CSDN博客 文件&#xff1a;《Hexfrmt.pdf》描述了…

地信大四,实习重要吗?怎么找实习岗位?

“地信怎么找实习啊&#xff1f;” “实习三个月以上&#xff1f;暑假只有两个月啊” “什么岗位实习比较有用&#xff1f;” “助理类岗位是做什么&#xff1f;” …… 同学们好啊&#xff0c;不知不觉24年已经是过完一小半了&#xff0c;24届毕业的同学们也差不多就要迎来…

JSON.parse(JSON.stringify())导致的响应式属性丢失

console.log("formdata赋值前", this.formdata);console.log("row",row);console.log("row序列化后", JSON.parse(JSON.stringify(row)));this.formdata JSON.parse(JSON.stringify(row)); console.log("formdata赋值后", this.formd…

0803功放3

1.甲乙类互补堆成功放&#xff0c; 理想12v t提供静态偏置&#xff0c;消去交越失真 2.12V Po(12)2/2RL 3.电压并联负反馈 并联减小输入电阻 电压减小输出电阻 4.-Rf/Ri 这个问题是工艺问题引起的&#xff0c;最早用PNP管用的锗管&#xff0c;后面硅工艺成熟后用的就是硅管&…

springboot 网上商城系统-计算机毕业设计源码08789

摘 要 随着互联网趋势的到来&#xff0c;各行各业都在考虑利用互联网将自己推广出去&#xff0c;最好方式就是建立自己的互联网系统&#xff0c;并对其进行维护和管理。在现实运用中&#xff0c;应用软件的工作规则和开发步骤&#xff0c;采用Java技术建设网上商城系统。 本设…

数据库攻防之MongoDB

MongoDB是一个安全性相对较高的非关系型数据库&#xff0c;它的安全问题主要出现在使用、配置过程当中。目前随着MongoDB的流行&#xff0c;它也成为了红队攻防领域不可忽视的数据库。 0x01 MongoDB简介 MongoDB 是一个由C编写、基于分布式文件存储的开源数据库系统&#xff…

java运维交接项目逆向工程

​ 背景 有承接过Java项目运维的团队估计都处理过的一件事情&#xff0c;就是同步生产代码跟本地代码&#xff0c;条件再差些甚至要直接基于生产部署包逆向本地源码工程。而哪怕是原运维团队交接了源码&#xff0c;往往也会历史久远的原因&#xff0c;给了一份不太可靠的源码…

Navicat连接服务器MySQL

Navicat连接服务器MySQL 1. Navicat连接服务器MySQL2. 如何查看MySQL用户名和密码3. 修改MySQL登录密码4. 安装MySQL(Centos7)遇到错误和问题1. error 1045 (28000): access denied for user rootlocalhost (using password:yes) 1. Navicat连接服务器MySQL 选择数据库 直接使用…

记录react实现选择框一二级联动出现的问题

需求&#xff1a;用户在选择第一个选择框的选项后&#xff0c;第二个选择框的选项会根据第一个选择框的选择动态更新。如图所示 出现的问题 一级分类选择之后二级分类没有数据&#xff0c;第二次重新选择一级分类的时候&#xff0c;二级分类就会有值。 第一次点击截图&#x…

提升办公效率的利器—OnlyOffice文档8.1深度体验

目录 1. 前言 2. 安装 3. 基本使用 3.1 文档编辑 3.2 表格处理 3.3 演示文稿 4. 个人评价 5. 总结 1. 前言 在当今信息化时代&#xff0c;办公软件已经成为我们日常工作中不可或缺的工具。无论是撰写文档、处理数据&#xff0c;还是制作演示文稿&#xff0c;办公软件的…

数电大作业-四输入表决器

&#xff08;PCB和multisim仿真画的有很大问题&#xff0c;没有VCC输入和GND&#xff0c;没学过直接裸画的&#xff0c;之后会好好看视频学习&#xff09; 应用背景&#xff1a; 四个评委&#xff0c;三个及以上评委同时按下通过按钮时&#xff0c;选手才能通过。否则不通过。…

【Flink metric(3)】chunjun是如何实现脏数据管理的

文章目录 一. 基础逻辑二. DirtyManager1. 初始化2. 收集脏数据并check3. 关闭资源 三. DirtyDataCollector1. 初始化2. 收集脏数据并check3. run&#xff1a;消费脏数据4. 释放资源 四. LogDirtyDataCollector 一. 基础逻辑 脏数据管理模块的基本逻辑是&#xff1a; 当数据消…

适用于轨道交通专用的板卡式网管型工业以太网交换机

是网管型 CompactPCI板卡式冗余环网交换机。前面板带有6个 10/100/1000Base-T(X)M12接口。后面的CPCI接口有 8个10/100/1000Base-T (X) 以太网接口。 是特别为轨道交通行业EN50155标准要求而设计的坚固型交换机。它同时具有以下特性&#xff1a; ● 支持2线以太网距离扩展端口&…

Crypto++ 入门

一、简介 Crypto&#xff08;也称为CryptoPP、libcrypto或cryptlib&#xff09;是一个免费的开源C库&#xff0c;提供了多种加密方案。它由Wei Dai开发和维护&#xff0c;广泛应用于需要强大加密安全的各种应用程序中。该库提供了广泛的加密算法和协议的实现&#xff0c;包括&…

【通用技巧】自动获取日志存放路径,无需手动修改配置文件

我们在部署环境的时候&#xff0c;常常会手动修改一些配置文件的存放地址&#xff0c;比如日志的路径、截图的路径&#xff0c;这是因为我们的环境不一样&#xff0c;部署应用的位置也不一样导致的。如果位置写死了&#xff0c;那么就会造成通用性很差&#xff0c;所以我们经常…