metroluffy / blog

用于记录平时开发中所遇到问题的解决方法、笔记等
9 stars 1 forks source link

为Quill.js增加图片拖拽上传功能 #16

Open metroluffy opened 6 years ago

metroluffy commented 6 years ago

Quill.js是一款可以灵活自定义的开源的富文本编辑器,Github上已经有1.8W+的star了。拖拽上传有着良好的交互体验,原生的Quill并不支持图片拖拽上传功能,本文以插件的形式来实现。 Quill.js有着强大且灵活的插件系统,很简单便可以实现一个插件,下面是一个基本的插件模型。

class Plugin {
    /**
     * @param quill {Quill}富文本实例
     * @param options {Object}
     */
    constructor(quill, options = {}) {
        this.quill = quill
        this.options = options
    }
}

export {
  Plugin
}

上述这个模型并不包含任何功能,接下来我们来实现一个简单的统计文章字数的插件。

class wordCount {
    constructor(quill, options = {}) {
        this.quill = quill;
        this.options = options;
        this.quill.on('text-change', this.update.bind(this));
        this.toolbar = quill.getModule('toolbar');
        this.update();  // Account for initial contents
    }

    calculate(){
        return this.quill.getLength() - 1   // quill.getLength() gives 1 instead of 0 when there is no content, so there need -1
    }

    update() {
        const self = this
        let length = this.calculate();
        let label = 'word';
        if (length !== 1) {
            label += 's';
        }
        let countView = document.querySelector('#quill-word-count');
        if (!countView) {
            countView = document.createElement('span');
            countView.id = 'quill-word-count';
            self.toolbar.container.appendChild(countView);
        }
        countView.innerHTML = length + ' ' + label;
    }
}

export {
    wordCount
}

text-change当Quill的内容发生变化时发出,是Quill提供的事件系统中的一个,然后使用getLength获取字数并更新在ToolBar的状态。接下来我们继续前进,HTML5提供了Darg & Drop API,以之来实现图片拖拽上传。

import defaultsDeep from 'lodash/defaultsDeep'
const DefaultOptions = {
    url: '',
    size: 0,
    onUpload: false,
    onUploadSuccess: false,
    onUploadFaild: false,
    onSizeError: false,
    onTypeError: false
}
class ImageUploadExtend {
    /**
     * @param quill {Quill}富文本实例
     * @param options {Object} config
     */
    constructor(quill, options = {}) {
        this.quill = quill
        this.options = this. = defaultsDeep({}, options, DefaultOptions);
        this.file = ''  // 要上传的图片文件
        this.imgURL = ''  // 图片地址
        quill.root.addEventListener('drop', this.dropHandle.bind(this), false)
        quill.root.addEventListener('dropover', function (e) {
            e.preventDefault()
        }, false)
    }

    dropHandle (e) {
        const self = this
        e.preventDefault()
        self.file = e.dataTransfer.files[0]; // only one single image
        if (!self.file.type.match(/^image\/(gif|jpe?g|a?png|svg|webp|bmp|vnd\.microsoft\.icon)/i)) {
            // file is not an image
            // Note that some file formats such as psd start with image/* but are not readable
            if (self.options.onTypeError) {
                self.options.onTypeError()
            }
            return
        }
        // if set a size
        if (self.options.size !== 0 && self.file.size >= self.options.size * 1024 * 1024) {
            if (self.options.sizeError) {
                self.options.sizeError()
            }
            return
        }
        // if has`t url, insert img as Base64 source
        if (this.options.url !== '') {
            self.uploadImage()
        } else {
            self.toBase64()
        }
    }
    uploadImage () {

    }
    toBase64 () {
        const self = this
        const reader = new FileReader()
        console.log('--------把图片转换为Base64格式--------')
        reader.readAsDataURL(self.file)
        reader.onload = (e) => {
            // 返回base64
            self.imgURL = e.target.result
            self.insertImage()
        }
    }

    insertImage () {
        const self = this
        const index = (self.quill.getSelection() || {}).index || self.quill.getLength() - 1
        self.quill.insertEmbed(index, 'image', self.imgURL, 'user')
        self.quill.update()
        self.quill.setSelection(index + 1)
    }
}

export {
  ImageUploadExtend
}

上述这个类基本实现了图片拖拽上传的功能,具体的上传逻辑比较简单就不写出了。接下来我们在Quill中注册该插件,就可以使用了。

    import Quill from 'quill'
    import {ImageUploadExtend} from './ImageUploadExtend'

    Quill.register('modules/ImageUploadExtend', ImageUploadExtend)
    new Quill('#editor-container', {
        theme: 'snow',
        modules: {
            ImageUploadExtend: {
                url: ''
            },
        }
    });

Quill Toolbar默认的图片按钮是将图片转成Base64格式插入内容,图片太大的话就不再合适,Toolbar Module允许添加自定义的处理函数进行配置,我们简单改造一下,在上述ImageUploadExtend这个类中加入相应的逻辑。

    // add these code in ImageUploadExtend constructor
    constructor(quill, options = {}) {
        ...
        quill.getModule('toolbar').addHandler('image',this.toolBarImageHandler.bind(this))
    }

    //implementation
    toolBarImageHandler () {
        const self = this
        let fileInput = document.querySelector('.quill-image-input');
        if (!fileInput) {
            fileInput = document.createElement('input');
            fileInput.setAttribute('type', 'file');
            fileInput.setAttribute('accept', 'image/*');
            fileInput.classList.add('quill-image-input');
            fileInput.style.display = 'none'
            // 监听选择文件
            fileInput.addEventListener('change', function () {
                self.file = fileInput.files[0]
                fileInput.value = ''
                // if set a size
                if (self.options.size !== 0 && self.file.size >= self.options.size * 1024 * 1024) {
                    if (self.options.sizeError) {
                        self.options.sizeError()
                    }
                    return
                }
                if (self.options.url !== '') {
                    self.uploadImage()
                } else {
                    self.toBase64()
                }
            })
            document.body.appendChild(fileInput);
        }
        fileInput.click();
        document.body.removeChild(fileInput);
    }

以上,基本实现了Quill.js图片上传的功能,文件上传也是一致的实现思路。有什么问题,可以进一步在评论中交流。下一篇打算写下如何给图片上传增加进度条的功能。

参考资料

Quill.js Documentation

plh97 commented 4 years ago

文字拖拽事件如何监听...

plh97 commented 4 years ago

我拖拽后发现该拖拽内容的 id 不见了. 样式也不见了...

metroluffy commented 4 years ago

文字拖拽事件如何监听...

不太能get到你的场景哈,如果是文本节点之间拖拽更改顺序这类的话,看下实现是不是生成的新节点,能否手动加上去。节点拖拽也有很多优秀的开源库,也可以看看