Vue quill-editor插入视频并适配
背景:
公司现在需要富文本编辑器插入视频的功能,之前富文本编辑使用的是 Vue quill-editor 插件。
目标:
使用人员自由插入本地视频或者网络视频链接。如下图:
实现步骤:
提醒:本文章适合已经会基本使用 quill-editor 功能的读者。
1. 先实现基本插入视频的功能
在 toolbar 内配置 video 功能:
toolbar: {
container: [
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
[{ header: 1 }, { header: 2 }], // 1、2 级标题
[{ indent: "-1" }, { indent: "+1" }], // 缩进
[{ direction: "rtl" }], // 文本方向
[{ size: ["small", false, "large", "huge"] }], // 字体大小
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
[{ font: [] }], // 字体种类
[{ align: [] }], // 对齐方式
["clean"], // 清除文本格式
["image", "video"], // 图片, 视频
["emoji"], //表情包
[{ lineHeight: lineHeightStyle.whitelist }], //行高
], // 工具菜单栏配置
handlers: {
lineHeight: (value) => {
if (value) {
let quill = this.$refs.myQuillEditor.quill;
quill.format("lineHeight", value);
}
},
video: (value) => {
this.videoDialog.show = true;
},
},
},
点击 toolbar 内的 video 按钮时,弹出上传视频的弹框:
<!--视频上传-->
<div>
<el-dialog :close-on-click-modal="false" width="50%" style="margin-top: 1px" title="视频上传" :visible.sync="videoDialog.show" append-to-body>
<el-tabs v-model="videoDialog.activeName">
<el-tab-pane label="添加视频链接" name="first">
<el-input v-model="videoDialog.videoLink" placeholder="请输入视频链接" clearable></el-input>
<el-button type="primary" size="small" style="margin: 20px 0px 0px 0px" @click="addVideoLink(videoDialog.videoLink)">添加 </el-button>
</el-tab-pane>
<el-tab-pane label="本地视频上传" name="second">
<el-upload
v-loading="uploading"
style="text-align: center"
drag
:action="uploadUrl"
accept="video/*"
:data="videoFileData"
:before-upload="onBeforeUploadVideo"
:on-success="onSuccessVideo"
:on-error="onErrorVideo"
:multiple="false"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
</el-upload>
</el-tab-pane>
</el-tabs>
</el-dialog>
</div>
视频上传的回调函数:
// --------- 视频相关 ---------
addVideoLink(videoLink) {
if (!videoLink) return this.$message.error("请输入视频地址");
this.videoDialog.show = false;
let range = this.editor.selection.savedRange;
let index = 0;
if (range != null) {
index = range.index;
}
// 插入
this.editor.insertEmbed(index, "video", videoLink);
// 调整光标到最后
this.editor.setSelection(index + 1);
},
// el-文件上传组件
onBeforeUploadVideo(file) {
this.loading = true;
let acceptArr = ["video/mp4"];
const isVideo = acceptArr.includes(file.type);
if (isVideo) {
console.log(file.name);
//笔者公司的视频托管在七牛云,读者按照自己公司的业务配置
return api.getQiniuToken().then((res) => {
// 配置上传的七牛token
this.videoFileData = {
token: res.retInfo,
name: file.name,
key: Math.floor(Math.random() * 100 + 1) + file.name,
};
});
} else {
return false;
}
},
onSuccessVideo(res) {
this.uploading = false;
//笔者公司的视频托管在七牛云,读者按照自己公司的业务配置
const videoUrl = "xxx" + res.retInfo;
this.addVideoLink(videoUrl);
},
onErrorVideo(file, fileList) {
this.uploading = false;
this.$message.error("上传失败");
},
// --------- 视频相关 ---------
重点:addVideoLink
的功能是将视频链接插入到quill-editor
编辑框内,let range = this.editor.selection.savedRange;
是计算编辑框光标的位置(其中this.editor = this.$refs.myQuillEditor.quill
)。刚开始用的是this.editor.getSelection()
,第一次插入视频时,获取的range
总是为null
,换成this.editor.selection.savedRange
后,获取range
就正常了。
2.适配插入视频的布局
插入视频默认用的是iframe
标签,插入竖屏视频后,样式如下:
生成的富文本代码如下:
<iframe class="ql-video" frameborder="0" allowfullscreen="true" src="https://xxx.com/65IMG_3870.mp4"></iframe><p><br></p>
将iframe
标签换成 html5 内的video
标签:
先创建 video.js 文件:
import { Quill } from "vue-quill-editor";
// 源码中是import直接导入,这里要用Quill.import引入
const BlockEmbed = Quill.import("blots/block/embed");
const Link = Quill.import("formats/link");
const ATTRIBUTES = ["height", "width"];
class Video extends BlockEmbed {
static create(value) {
const node = super.create(value);
// 添加video标签所需的属性
node.setAttribute("controls", "controls");
node.setAttribute("type", "video/mp4");
node.setAttribute("src", this.sanitize(value));
//为了兼容 iOS 设备上,显示海报图(视频封面)
node.setAttribute("preload", "metadata");
return node;
}
static formats(domNode) {
return ATTRIBUTES.reduce((formats, attribute) => {
if (domNode.hasAttribute(attribute)) {
formats[attribute] = domNode.getAttribute(attribute);
}
return formats;
}, {});
}
static sanitize(url) {
return Link.sanitize(url); // eslint-disable-line import/no-named-as-default-member
}
static value(domNode) {
return domNode.getAttribute("src");
}
format(name, value) {
if (ATTRIBUTES.indexOf(name) > -1) {
if (value) {
this.domNode.setAttribute(name, value);
} else {
this.domNode.removeAttribute(name);
}
} else {
super.format(name, value);
}
}
html() {
const { video } = this.value();
return `<a href="${video}">${video}</a>`;
}
}
Video.blotName = "video";
Video.className = "ql-video";
Video.tagName = "video"; // 用video标签替换iframe
export default Video;
将 video.js 引入到 quill-editor 所在文件:
import Video from "../utils/video";
Quill.register(Video, true);
将入视频后,样式如下:
生成的富文本代码如下:
<video class="ql-video" controls="controls" type="video/mp4" src="https://xxx.com/65IMG_3870.mp4"></video><p><br></p>
生成的iframe
标签页面和video
标签页面效果对比:
iframe
标签页面
video
标签页面
3.video标签替换iframe标签后,出现新的问题
在iOS设备上,html内嵌入的视频默认只显示一个大大的播放按钮,不显示海报图(封面图)。在其他系统(windows、mac、android等)没问题。 排查问题: 首先:在渲染加载富文本内容时,我们可以为视频的video标签设置poster属性,指定视频第一帧为海报图:
//富文本视频第一帧作为封面
videoCover(item, url) {
const video = item; // 获取视频对象
let canvas = document.createElement("canvas"); //创建canvas
video.src = url; // video的url地址
const ctx = canvas.getContext("2d"); // 绘制2d
video.crossOrigin = "anonymous"; // 解决跨域问题
video.currentTime = 1; // 视频第一帧
video.addEventListener('loadeddata', function () {
canvas.width = video.clientWidth; // 视频宽度
canvas.height = video.clientHeight; //视频高度
// 利用canvas对象方法绘图
ctx.drawImage(video, 0, 0, video.clientWidth, video.clientHeight);
// 转换成base64形式并设置封面
video.poster = canvas.toDataURL("image/jpeg", 1); // 截取后的视频封面
});
},
//在vue的updated函数中调用videoCover函数:
updated() {
//在vue中使用document.querySelectorAll时,必须在mounted、updated等函数中调用,才能获取到document内容
const videos = document.querySelectorAll("video");
videos.forEach((item) => {
this.videoCover(item, item.src);
});
},
实现上述的想法,打开调试时,发现video标签内并没有设置poster属性。打断点调试,发现在iOS设备上,根本没有调用loadeddata
方法。
在stackoverflow上,发现有开发者回答笔者的疑惑。大概意思是:在iOS设备上,video标签不会默认加载视频数据。
然后此回答下面有其他开发者回答,为video标签添加
preload="metadata"
属性,就可以在iOS设备上显示海报图了。
笔者在video.js文件,默认为video标签添加
preload="metadata"
属性,解决此问题。
问题完美解决,效果如下:
总结:
1:quill-editor配置video功能。 2:插入视频默认使用的是iframe标签,竖屏视频显示效果比较差。使用video标签替换iframe标签。 3:解决video标签在iOS设备上不显示海报图(封面图)的问题。
转载自:https://juejin.cn/post/7239897512906850341