如何找到对应位置的源码。 目前我是无法直接找到的, 我是通过搜索网上的答案或者 debugger 才能够对应源码, 我做不到短时间内了解一个库的完全流程,所以看我文章的人也不必焦虑。慢慢debugger 了解部分源码就行

quill 为什么粘贴图片时为 base64

项目使用的版本是1.3.7

quilljs/quill at 1.3.7 (github.com)

https://github.com/quilljs/quill/blob/0148738cb22d52808f35873adb620ca56b1ae061/modules/clipboard.js#L60

1
this.quill.root.addEventListener('paste', this.onPaste.bind(this));

https://github.com/quilljs/quill/blob/0148738cb22d52808f35873adb620ca56b1ae061/modules/clipboard.js#L108

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
onPaste(e) {
if (e.defaultPrevented || !this.quill.isEnabled()) return;
let range = this.quill.getSelection();
let delta = new Delta().retain(range.index);
let scrollTop = this.quill.scrollingContainer.scrollTop;
this.container.focus(); //(会将图片粘贴到剪贴板元素, 参考下面代码#1)
this.quill.selection.update(Quill.sources.SILENT);
setTimeout(() => {
delta = delta.concat(this.convert()).delete(range.length);
this.quill.updateContents(delta, Quill.sources.USER);
// range.length contributes to delta.length()
this.quill.setSelection(delta.length() - range.length, Quill.sources.SILENT);
this.quill.scrollingContainer.scrollTop = scrollTop;
this.quill.focus();
}, 1);
}

粘贴的时候核心代码在这上面。

#1

1
2
3
4
5
6
7
8
9
10
11
<div class="container">
<div class="editor" contenteditable="true" style="width: 200px;height: 200px;"></div>
<div class="clipboard" contenteditable="true"></div>
</div>
<script>
const editorEl = document.querySelector('.editor');
const clipboardEl = document.querySelector('.clipboard')
editorEl.addEventListener('paste', () => {
clipboardEl.focus()
})
</script>

image-20230312114008717

匹配到了这段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function matchBlot(node, delta) {
var match = _parchment2.default.query(node);
if (match == null) return delta;
if (match.prototype instanceof _parchment2.default.Embed) {
var embed = {};
var value = match.value(node);
if (value != null) {
embed[match.blotName] = value;
delta = new _quillDelta2.default().insert(embed, match.formats(node));
}
} else if (typeof match.formats === 'function') {
delta = applyFormat(delta, match.blotName, match.formats(node));
}
return delta;
}

获取剪贴板里面的元素转换为 delta。然后将剪贴板的元素情况。 更新quill 里面的内容。就将图片插入进去了。

回到为什么是 base64图片。 原因就在于浏览器粘贴的时候用的就是 base64.🥳

是不是有点废话。这一段分析是为了下面处理图片做铺垫的~

粘贴图片上传图片

Clipboard Module - Quill Rich Text Editor (quilljs.com)

quill 已经提供了接口。 所以我们只需要配置一下就可以了, 在 quill 的 option 里面做如下配置。 不只是粘贴图片。 quill的 toolbar 也有一个上传图片的功能。可以通过 options 里的handles 配置,这里不讨论。

1
2
3
4
5
6
clipboard: {
matchers: [
['img', this.noPastingPictures],
],
},

noPastingPictures(上传 base64图片) 部分代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 黏贴图片base64改为上传url
noPastingPictures(node, delta) {
const urlReg = /^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i;
if (delta && delta.ops) {
delta.ops.forEach(item => {
if (item.insert && item.insert.image) {
if (item.insert.image.includes('data:image')) { // base64 类型的图片,上传替换
this.handleBase64Image(item);
} else if (!urlReg.test(item.insert.image)) { // 不是网址类型的图片。直接报错
this.imageError();
}
}
});
}
return delta;
},
handleBase64Image(data) {
const { image } = data.insert;
try {
const file = this.dataURItoBlob(image);
file.name = new Date().getTime();
this.copyPicture(image, file);
} catch {
this.imageError();
}
},
async copyPicture(image, file) {
this.loadingCount++;
try {
const fileUrl = await (new FileUpload())
.uploadStart([file]);
await this.loadImage(fileUrl[0].url);

this.content = this.content.replace(image, fileUrl[0].url);
} catch {
this.$message.error('图片上传失败');
} finally {
this.loadingCount--;
}
},
imageError() {
this.$message.error('粘贴图片失败。请单独选中原图复制粘贴');
},
loadImage(url) {
const image = new Image();
return new Promise((resolve, reject) => {
image.onload = resolve;
image.onerror = reject;
image.src = url;
});
},

粘贴图片的时候你会发现 windows 从资源管理器里面粘贴的图片不显示。macos11从资源管理器里面粘贴的图片是一个文件图标,但是 macos12正常。 这就是系统上面的处理方法不同的。如果需要兼容从资源管理器里面粘贴图片。 可以自定义 quill 的 paste 事件。 读取对应的 paste 数据。转成图片。我选择的是通过拖动图片进行处理

拖动图片上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bindDropEvent() {
const quillEl = this.$refs.quill.quill.root;
quillEl.addEventListener('drop', this.dropHandle, false);
this.$once('hook:beforeDestroy', () => {
quillEl.removeEventListener('drop', this.dropHandle);
});
},
// 处理拖动上传
dropHandle(e) {
e.preventDefault();
if (!e.dataTransfer.files.length) {
return;
}

const file = e.dataTransfer.files[0];
const type = file.type.split('/')[0];
if (type !== 'image') {
return;
}
this.copyPicture(file);
},

是从 vue项目代码里面拷贝过来的,将就看一下。 大概流程是拖动图片获取对应的数据, 上传然后插入。这里有个 bug,就是编辑器里面没有内容的时候, 需要focusc才能插入。而且会插入一个空行。

第三方解决方案

[EthanYan6/quill-image-super-solution-module: quill,image,upload,paste,drop,extend…]功能最全!实现最完美!体积最小!解决同类型插件的所有bug!此模块为富文本编辑器 vue-quill-editor 的专用插件,为其提供了自定义上传图片到服务器、粘贴图片上传至服务器、拖拽图片上传至服务器的功能。支持与其他模块一起使用。 (github.com)

这是我在 github 上面找的。

我觉得这个库有一点可以优化, 就是自定义上传函数。 而不是通过库去请求上传地址。