electron+vue實現div contenteditable功能|截圖

最近在學習基於electron + electron-vue開發聊天客戶端項目時,須要用到編輯器插入表情功能。通常經過input或textarea也能實現,經過插入[笑臉]、(:12 這些標籤,展現的時候解析標籤就行。
以下圖效果:
360截圖20200107111730111.png
在網上找到的jq插件實如今textarea光標處插入表情符標籤css

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
        <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
        <div class="container">
            <div class="row">
                <div class="col col-sm-12">
                    <button class="btn btn-success" data-emoj="[笑臉]">[笑臉]</button>
                    <button class="btn btn-success" data-emoj="[奮鬥]">[奮鬥]</button>
                    <button class="btn btn-success" data-emoj="[:17]">[:17]</button>
                </div>
                <div class="col col-sm-12">
                    <textarea class="form-control" id="content" rows="10"></textarea>
                </div>
            </div>
        </div>
     
        <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
        <script>
            (function ($) {
                $.fn.extend({
                    insertEmojAtCaret: function (myValue) {
                        var $t = $(this)[0];
                        if (document.selection) {
                            this.focus();
                            sel = document.selection.createRange();
                            sel.text = myValue;
                            this.focus();
                        } else if ($t.selectionStart || $t.selectionStart == '0') {
                            var startPos = $t.selectionStart;
                            var endPos = $t.selectionEnd;
                            var scrollTop = $t.scrollTop;
                            $t.value = $t.value.substring(0, startPos) + myValue + $t.value.substring(endPos, $t.value.length);
                            this.focus();
                            $t.selectionStart = startPos + myValue.length;
                            $t.selectionEnd = startPos + myValue.length;
                            $t.scrollTop = scrollTop;
                        } else {
                            this.value += myValue;
                            this.focus();
                        }
                    }
                });
            })(jQuery);
                 
            $("button").on("click", function() {
                $("#content").insertEmojAtCaret($(this).attr("data-emoj"));
            });
        </script>
    </body>
</html>

但是這種方法並非我想要的相似微信編輯框插入表情效果。
如是就想到了div模擬 設置contenteditable="true" 實現富文本編輯器效果,這種方法是能夠實現,不過在vue中不能綁定v-model,最後參考一些技術貼實現了這個功能,一頓操做下來採坑很多,因而就作一些分享記錄吧。
360截圖20200107160057637.pnghtml

vue中經過給div添加contenteditable=true屬性實現富文本功能

360截圖20200107100855597.png

實現方式:
單獨聲明一個vue組件,chatInput.vue,經過監聽數據變化並返回父組件。vue

一、父組件添加v-modelnode

<template>
    ...
    <chatInput ref="chatInput" v-model="editorText" @focusFn="handleEditorFocus" @blurFn="handleEditorBlur" />
</template>

import chatInput from './chatInput'

export default {
    data () {
        return {
            editorText: '',
            
            ...
        }
    },
    components: {
        chatInput,
    },
    ...
}

二、v-model中傳入的值在子組件prop中獲取jquery

export default {
    props: {
        value: { type: String, default: '' }
    },
    data () {
        return {
            editorText: this.value,
            ...
        }
    },
    watch: {
        value() {
            ...
        }
    },
}

三、經過監聽獲取到的prop值,並將該值賦值給子組件中的v-html參數,雙向綁定就ok了。bootstrap

chatInput.vue組件

<!-- vue實現contenteditable功能 -->

<template>
    <div 
        ref="editor"
        class="editor"
        contenteditable="true"
        v-html="editorText"
        @input="handleInput"
        @focus="handleFocus"
        @blur="handleBlur">
    </div>
</template>

<script>
    export default {
        props: {
            value: { type: String, default: '' }
        },
        data () {
            return {
                editorText: this.value,
                isChange: true,
            }
        },
        watch: {
            value() {
                if(this.isChange) {
                    this.editorText = this.value
                }
            }
        },
        methods: {
            handleInput() {
                this.$emit('input', this.$el.innerHTML)
            },
            // 清空編輯器
            handleClear() {
                this.$refs.editor.innerHTML = ''
                this.$refs.editor.focus()
            },
            
            // 獲取焦點
            handleFocus() {
                this.isChange = false
                this.$emit('focusFn')
            },
            // 失去焦點
            handleBlur() {
                this.isChange = true
                this.$emit('blurFn')
            },
            

            /**
             * 光標處插入內容
             * @param html 須要插入的內容
             */
            insertHtmlAtCaret(html) {
                let sel, range;
                if(!this.$refs.editor.childNodes.length) {
                    this.$refs.editor.focus()
                }
                if (window.getSelection) {
                    // IE9 and non-IE
                    sel = window.getSelection();

                    if (sel.getRangeAt && sel.rangeCount) {
                        range = sel.getRangeAt(0);
                        range.deleteContents();
                        let el = document.createElement("div");
                        el.appendChild(html)
                        var frag = document.createDocumentFragment(), node, lastNode;
                        while ((node = el.firstChild)) {
                            lastNode = frag.appendChild(node);
                        }
                        range.insertNode(frag);
                        if (lastNode) {
                            range = range.cloneRange();
                            range.setStartAfter(lastNode);
                            range.collapse(true);
                            sel.removeAllRanges();
                            sel.addRange(range);
                        }
                    }
                } else if (document.selection && document.selection.type != "Control") {
                    // IE < 9
                    document.selection.createRange().pasteHTML(html);
                }
                
                this.handleInput()
            }
        }
    }
</script>

<style>

</style>

組件功能已經親測,直接一次性拿走使用。微信

如下是一些參考:

一、vue官方描敘,自定義組件的v-model:
一個組件上的 v-model 默認會利用名爲 value 的 prop 和名爲 input 的事件,v-model的值將會傳入子組件中的prop
https://cn.vuejs.org/v2/guide/components-custom-events.html#自定義組件的-v-modelapp

二、vue中div可編輯光標處插入內容
https://blog.csdn.net/weixin_...electron

https://blog.csdn.net/qq_3106...編輯器

360截圖20200107101051427.png

360截圖20200107101115114.png

electron+vue中實現截圖功能

主要使用的是微信截圖dll,經過node執行便可

screenShot() {
    return new Promise((resolve) => {
        const { execFile } = require('child_process')
        var screenWin = execFile('./static/PrintScr.exe')
        screenWin.on('exit', function(code) {
            let pngs = require('electron').clipboard.readImage().toPNG()
            let imgData = new Buffer.from(pngs, 'base64')
            let imgs = 'data:image/png;base64,' + btoa(new Uint8Array(imgData).reduce((data, byte) => data + String.fromCharCode(byte), ''))
            resolve(imgs)
        })
    })
},
相關文章
相關標籤/搜索