能在安卓app上应用动静增加文字、并且文字可能拖拽、编辑、删除、缩放、设置色彩、字体大小、文字排列形式,还有能够插入图片、并且图片也能缩放、删除。

假如场景:用户在后盾页面上配置自定义海报,须要将数据传到app并展现。

我应用的是fabricjs。官网:http://fabricjs.com/

实现的原理就是应用webview间接实现,我在网上找了很久也没有找到比拟快的办法实现,其实有一种,那就是应用canvas去实现,然而canvas的绘制很简单、麻烦。大佬能够试试emmm

间接上代码了emmm,做个笔记。

index.vue的代码如下:

<template>    <view class="promotionPosterEdit">        <web-view :src="'../../../hybrid/html/promotionPosterEdit/index.html?data=' + obj" @message="getMessage" ref="wv"></web-view>    </view></template><script>    import { pathToBase64, base64ToPath } from '../../../js_sdk/mmmm-image-tools/index.js';    export default {        data() {            return {                obj: null,                currentWebview: null,            }        },        onLoad() {            let statusBarHeight = uni.getSystemInfoSync()['statusBarHeight']            let top = uni.getStorageSync('navbarHeight') + 30            let height = uni.getSystemInfoSync()['screenHeight'] - 64 - uni.getStorageSync('navbarHeight') - 74            let width = uni.getSystemInfoSync()['screenWidth'] - 100            let screenHeight = uni.getSystemInfoSync()['screenHeight']            let obj = {                statusBarHeight: statusBarHeight,                top: top,                height: height,                width: width,                screenHeight: screenHeight            }            this.obj = JSON.stringify(obj)    //这里是传给webview的参数用来调整款式的        },        onReady() {            const self = this            self.currentWebview = self.$scope.$getAppWebview().children()[0]        },        methods: {            getMessage(e) {                console.log('接管html发送的数据', e.detail)                if(e.detail.data[0].msg === 'openCamera') {    //关上手机相册                    uni.chooseImage({                        count: 1,                        success: (res) => {                            console.log('长期门路', res.tempFilePaths[0])                            pathToBase64(res.tempFilePaths[0]).then(base64 => {                                let info = base64.replace(/[\r\n]/g, '')                                const self = this;                                self.currentWebview.evalJS(`uniEvent(${JSON.stringify(info)})`);    //app向html发送数据                            }).catch(error => {                                console.log('转换失败:', error);                            });                        }                    })                } else if(e.detail.data[0].msg === 'save') {    //保留到手机相册                    console.log('app', e.detail.data[0].canvas)                    uni.showLoading({                        title: '保留中...'                    })                    let imageStr = e.detail.data[0].canvas                    base64ToPath(imageStr).then(path => {                        console.log('转换下载图片', path)                        this.saveImage(path);                    }).catch(error => {                        console.error('长期门路转换出错了:', error);                    });                } else if(e.detail.data[0].msg === 'wx') {        //转发到微信                    uni.showToast({                        icon: 'none',                        title: '暂未凋谢'                    })                } else if(e.detail.data[0].msg === 'friend') {        //转发到微信朋友圈                    uni.showToast({                        icon: 'none',                        title: '暂未凋谢'                    })                } else if(e.detail.data[0].msg === 'addEmptyText') {    //不能增加空文字                    uni.showToast({                        icon: 'none',                        title: '请输出文字'                    })                }            },            // 保留canvas图片到手机相册            saveImage(filePath) {              uni.saveImageToPhotosAlbum({                filePath, // 须要临时文件门路,base64无奈保留                success: () => {                    uni.hideLoading()                    uni.showToast({                        icon: 'none',                        title: '保留胜利'                    })                },                fail: (error) => {                  console.error('保留失败,请重试', error);                }              });            },        }    }</script><style lang="scss" scoped>    .promotionPosterEdit {        min-height: 100vh;        background-color: #303030;    }</style>

/hybrid/html/promotionPosterEdit/index.html的代码如下:
应用的插件html2canvas 1.4.1 https://html2canvas.hertzen.com
fabricjs5.3.0

<!DOCTYPE html><html>    <head>        <meta charset="utf-8" />        <meta name="viewport"            content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />        <title>网络网页</title>        <link rel="stylesheet" href="./css/index.css" />    </head>    <body>        <div class="post-message-section">            <div class="headers">                <div class="header">                    <div class="left item" data-action="navigateBack">                        <div class="back">                            <img src="../img/back.png" alt="back" />                        </div>                    </div>                    <div class="title item" style="color: #fff">                        <div class="txt">宣传海报编辑</div>                    </div>                    <div class="right item">                        <div class="share">                            <img src="../img/share.png" alt="back" />                        </div>                    </div>                </div>            </div>            <div class="myCanvas">                <canvas id="canvas"></canvas>            </div>            <div class="shares">                <div class="sharesmask"></div>                <div class="content">                    <div class="itemsurl">                        <div class="item wx">                            <div class="imgs">                                <img src="../img/wechat.png" alt="wechat" srcset="" />                            </div>                            <div class="txt">微信</div>                        </div>                        <div class="item friends">                            <div class="imgs">                                <img src="../img/friends.png" alt="friends" srcset="" />                            </div>                            <div class="txt">朋友圈</div>                        </div>                        <!-- <div class="item">                            <div class="imgs">                                <img src="../../../static/download.png" alt="download" srcset="" />                            </div>                            <div class="txt">生成海报</div>                        </div> -->                    </div>                    <div style="height: 6px;width: 100%;background-color: #ececec;"></div>                    <div class="cancel">勾销</div>                </div>            </div>                        <div class="addText">                <div class="addTextmask"></div>                <div class="addTextContent">                    <div class="title">文字款式设置</div>                    <div class="tabtool">                        <div class="items">                            <input class="color" type="color" value="#000000" />                        </div>                        <div class="item bold">                            <img src="../img/bold.png" alt="bold" srcset="" />                        </div>                        <div class="item italic">                            <img src="../img/italic.png" alt="italic" srcset="" />                        </div>                        <div class="item amplify">                            <img src="../img/amplify.png" alt="amplify" srcset="" />                        </div>                        <div class="item reduce">                            <img src="../img/reduce.png" alt="reduce" srcset="" />                        </div>                    </div>                    <div class="text">                        <textarea id="MainText" cols="30" row="10" style="font-size: 18px;" placeholder="请输出文字"></textarea>                    </div>                    <div class="confirm">                        <button class="cancelAddText btn btn-red" type="button">勾销</button>                        <button class="confirmAddText btn" type="button">确认</button>                    </div>                </div>            </div>            <div class="footer">                <div class="itemBox">                    <div class="item reload">                        <div class="icons">                            <img src="../img/reload.png" alt="reload" />                        </div>                        <div class="txt">复原默认</div>                    </div>                    <div class="item pic">                        <div class="icons">                            <img src="../img/pic.png" alt="pic" />                        </div>                        <div class="txt">图片/二维码</div>                    </div>                    <div class="item edit-pen">                        <div class="icons">                            <img src="../img/edit-pen.png" alt="edit-pen" />                        </div>                        <div class="txt">增加文字</div>                    </div>                    <div class="item downs">                        <div class="icons">                            <img src="../img/downs.png" alt="downs" />                        </div>                        <div class="txt">保留本地</div>                    </div>                </div>            </div>                        <div class="deleteFooter">                <div class="delete">                    <div class="box">                        <img src="../img/delete.png" alt="delete" srcset="" />                        <div class="tip">拖到此处删除</div>                    </div>                </div>                <div class="deleteactive">                    <div class="box">                        <img src="../img/deleteactive.png" alt="deleteactive" srcset="" />                        <div class="tip">松手即可删除</div>                    </div>                </div>            </div>            <input class="imageinput" style="display: none;" type="image" accept="image/jpeg,image/jpg,image/png" capture />        </div>        <script src="../js/fabric.js"></script>        <script src="../js/html2canvas.min.js"></script>        <script type="text/javascript">            var userAgent = navigator.userAgent;            if (/quickapp/i.test(userAgent)) {                // quickapp                document.write('<script type="text/javascript" src="https://quickapp/jssdk.webview.min.js"><\/script>');            }            if (!/toutiaomicroapp/i.test(userAgent)) {                document.querySelector('.post-message-section').style.visibility = 'visible';            }        </script>        <!-- uni 的 SDK -->        <script src="../js/uni-webview.js"></script>        <!-- <script type="text/javascript" src="https://unpkg.com/@dcloudio/uni-webview-js@0.0.1/index.js"></script> -->        <script type="text/javascript">            // console.log('接管参数', decodeURIComponent(location.href.split('data=')[1]))            let params = JSON.parse(decodeURIComponent(location.href.split('data=')[1]))            document.getElementsByClassName('headers')[0].style.paddingTop = params.statusBarHeight + 'px'            document.getElementsByClassName('myCanvas')[0].style.paddingTop = params.top + 30 + 'px'            document.getElementById('canvas').setAttribute('width', params.width + 'px')            document.getElementById('canvas').setAttribute('height', params.height + 'px')            document.getElementsByClassName('post-message-section')[0].style.height = params.screenHeight + 'px'            // 生成canvas            var canvas = new fabric.Canvas('canvas');            // ...这里能够写canvas对象的一些配置,前面将会介绍                        // console.log('fabric.Textbox', fabric.Textbox)            // 如果<canvas>标签没设置宽高,能够通过js动静设置            // canvas.setWidth(350)            // canvas.setHeight(200)            function initCanvas() {                // 读取图片地址,设置画布背景                fabric.Image.fromURL('../img/10801920.png', (img) => {                    img.set({                        // 通过scale来设置图片大小,这里设置和画布一样大                        scaleX: canvas.width / img.width,                        scaleY: canvas.height / img.height,                    });                    // 设置背景                    canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas));                    canvas.renderAll();                }, {                    crossOrigin: 'anonymous'    //加上这个是因为我须要将海报下载到手机相册,容许跨域,如果没设置是下载不了图片的                });                // 通过图片门路增加                fabric.Image.fromURL('../img/10801920.png', (img) => {                    img.set({                        scaleX: canvas.width / img.width / 2,                        scaleY: canvas.height / img.height / 2,                        hasControls: true, // 是否开启图层的控件                    });                    // 增加对象后, 如下图                    canvas.add(img);                    // deleteDiv(img)                }, {                    crossOrigin: 'anonymous'                });                                                                                // IText不可让文字竖排。textbox能够让文字竖排,主动依据宽度填写文字                const IText = new fabric.Textbox('这是一段初始默认文字', {                    left: 50,                    top: 50,                    // width: 50,    //不设置宽度,拉伸主动换行                    fontSize: 18, // 字体大小                    fontWeight: 600, // 字体粗细                    fill: '#aaaaff', // 字体色彩                    fontStyle: 'italic',  // 斜体                    splitByGrapheme: true, //true文本会实时依据宽度进行换行                    hasControls: true, //元素操作控件显示\即描边正方体                                        // fontFamily: 'Delicious', // 设置字体                    // fontFamily: 'monospace', // 设置字体                    // fontFamily: 'fantasy', // 设置字体                    // stroke: 'green', // 描边色彩                    // strokeWidth: 3, // 描边宽度                    // borderColor: 'orange',                    // editingBorderColor: 'orange' // 点击文字进入编辑状态时的边框色彩                                    }, {                    crossOrigin: 'anonymous'                });                // 增加文字                canvas.add(IText)                // deleteDiv(IText)            }            initCanvas()                        // 删除元素操作            function deleteDiv(target) {                target['on']('moving', function(info) {                    document.getElementsByClassName('deleteFooter')[0].style.display = 'block'                    if(info.pointer.y > params.height) {                        document.getElementsByClassName('delete')[0].style.display = 'none'                        document.getElementsByClassName('deleteactive')[0].style.display = 'block'                    } else {                        document.getElementsByClassName('delete')[0].style.display = 'block'                        document.getElementsByClassName('deleteactive')[0].style.display = 'none'                    }                })                target['on']('mouseup', function(info) {                    if(info.pointer.y > params.height + 5) {                        canvas.remove(target)                    }                    document.getElementsByClassName('delete')[0].style.display = 'block'                    document.getElementsByClassName('deleteactive')[0].style.display = 'none'                    document.getElementsByClassName('deleteFooter')[0].style.display = 'none'                })            }            // webview页面加载实现            document.addEventListener('UniAppJSBridgeReady', function() {                // webview加载实现,显示页面                document.getElementsByClassName('post-message-section')[0].style.display = 'block'                // 返回上一页                document.querySelector('.left').addEventListener('click', function(evt) {                    uni.navigateBack()                });                // 分享                document.querySelector('.right').addEventListener('click', function(evt) {                    console.log(getComputedStyle(document.documentElement).getPropertyValue("--sat"))                    console.log(getComputedStyle(document.documentElement).getPropertyValue("--sab"))                    document.querySelector('.shares').style.display = 'block'                    slideTimer(0, 'add', 1, 30)                });                document.querySelector('.sharesmask').addEventListener('click', function(evt) {                    console.log('点击分享遮罩层', evt.target)                    slideTimer(200, 'dec', 1, 30)                })                // 勾销分享                document.querySelector('.cancel').addEventListener('click', function(evt) {                    slideTimer(200, 'dec', 1, 30)                })                // 高度动画                function slideTimer(height, type, setup, times) {                    let timer = null                    let heights = height                    let setups = setup                    timer = setInterval(() => {                        if(type === 'add') {                            heights += 10 * setups                        } else {                            heights -= 10 * setups                        }                        console.log(Number(document.querySelector('.content').style.height.replace('px', '')))                        document.querySelector('.content').style.height = heights + 'px'                        setups++                        if (Number(heights) <= 0 || Number(document.querySelector('.content').style.height.replace('px', '')) >= 200) {                            clearInterval(timer)                            if(type !== 'add') {                                document.querySelector('.shares').style.display = 'none'                            }                            heights = 0                            setups = 1                        }                    }, times)                }                                // 分享到微信                document.querySelector('.wx').addEventListener('click', function(evt) {                    console.log('分享到微信')                    uni.postMessage({                        data: {                            msg: 'wx'                        }                    });                })                                // 分享到朋友圈                document.querySelector('.friends').addEventListener('click', function(evt) {                    console.log('分享到朋友圈')                    uni.postMessage({                        data: {                            msg: 'friend'                        }                    });                })                // 点击增加文字                document.querySelector('.edit-pen').addEventListener('click', function(evt) {                    console.log('增加文字')                    document.querySelector('.addText').style.display = 'block'                    initAddText()                    document.getElementsByClassName('color')[0].onchange = function(evt) {                        console.log('色彩扭转', evt.target.value)                        document.getElementById('MainText').style.color = evt.target.value                    }                })                                // 点击加粗文字                document.querySelector('.bold').addEventListener('click', function(evt) {                    console.log('点击加粗文字')                    if(document.getElementById('MainText').style.fontWeight === 'bold') {                        document.getElementById('MainText').style.fontWeight = 'normal'                    } else {                        document.getElementById('MainText').style.fontWeight = 'bold'                    }                })                                // 点击斜体文字                document.querySelector('.italic').addEventListener('click', function(evt) {                    console.log('点击斜体文字')                    if(document.getElementById('MainText').style.fontStyle === 'italic') {                        document.getElementById('MainText').style.fontStyle = 'normal'                    } else {                        document.getElementById('MainText').style.fontStyle = 'italic'                    }                })                                // 点击文字变大                document.querySelector('.amplify').addEventListener('click', function(evt) {                    console.log('点击文字变大')                    console.log(Number(document.getElementById('MainText').style.fontSize.replace('px', '')))                    console.log(document.getElementById('MainText').style.fontSize.replace('px', ''))                    document.getElementById('MainText').style.fontSize = Number(document.getElementById('MainText').style.fontSize.replace('px', ''))+1 + 'px'                })                                // 点击文字变小                document.querySelector('.reduce').addEventListener('click', function(evt) {                    console.log('点击文字变小')                    if(Number(document.getElementById('MainText').style.fontSize.replace('px', '')) === 12) {                        console.log('无奈再减了')                    } else {                        document.getElementById('MainText').style.fontSize = Number(document.getElementById('MainText').style.fontSize.replace('px', ''))-1 + 'px'                    }                })                                // 确认增加文字到canva中                document.getElementsByClassName('confirmAddText')[0].addEventListener('click', function(evt) {                    console.log('确认增加到canva中')                                        if(document.getElementById('MainText').value !== '') {                        // IText不可让文字竖排。textbox能够让文字竖排,主动依据宽度填写文字                        const IText = new fabric.Textbox(document.getElementById('MainText').value, {                            left: 50,                            top: 50,                            // width: 50,    //不设置宽度,拉伸主动换行                            fontSize: Number(document.getElementById('MainText').style.fontSize.replace('px', '')), // 字体大小                            fontWeight: document.getElementById('MainText').style.fontWeight, // 字体粗细                            fill: document.getElementById('MainText').style.color, // 字体色彩                            fontStyle: document.getElementById('MainText').style.fontStyle,  // 斜体                            splitByGrapheme: true, //true文本会实时依据宽度进行换行                            hasControls: true, //元素操作控件显示\即描边正方体                        }, {                            crossOrigin: 'anonymous'                        });                        // 增加文字                        canvas.add(IText);                        deleteDiv(IText)                                                initAddText()                        document.querySelector('.addText').style.display = 'none'                    } else {                        uni.postMessage({                            data: {                                msg: 'addEmptyText'                            }                        });                    }                })                                  // 敞开增加文字                document.querySelector('.addTextmask').addEventListener('click', function(evt) {                    initAddText()                    document.querySelector('.addText').style.display = 'none'                })                                // 勾销增加文字                document.getElementsByClassName('cancelAddText')[0].addEventListener('click', function(evt) {                    console.log('勾销增加文字')                    initAddText()                    document.querySelector('.addText').style.display = 'none'                })                                // 初始化增加文字                function initAddText() {                    document.getElementById('MainText').style.fontWeight = 'normal'                    document.getElementById('MainText').style.fontStyle = 'normal'                    document.getElementById('MainText').style.fontSize = '18px'                    document.getElementById('MainText').style.color = '#000000'                    document.getElementById('MainText').value = ''                    document.getElementsByClassName('color')[0].value = '#000000'                }                                // 复原默认canvas                document.querySelector('.reload').addEventListener('click', function(evt) {                    console.log('复原默认')                    canvas.clear();                    initCanvas()                })                // app被动与html通信,用于增加图片到canvas中                function addUniEvenPassthrough() {                    window.uniEvent = function(info) {                        console.log('获取完相册了', info)                        fabric.Image.fromURL(info, (img) => {                            img.set({                                scaleX: canvas.width / img.width / 2,                                scaleY: canvas.height / img.height / 2,                                hasControls: true, // 是否开启图层的控件                            });                            // 增加对象后, 如下图                            canvas.add(img);                            deleteDiv(img)                        }, {                            crossOrigin: 'anonymous'                        });                    }                }                addUniEvenPassthrough()                                // 点击了图片/二维码                document.querySelector('.pic').addEventListener('click', function(evt) {                    console.log('点击了图片/二维码')                    uni.postMessage({                        data: {                            msg: 'openCamera'                        }                    });                })                // 海报保留本地                document.querySelector('.downs').addEventListener('click', function(evt) {                    console.log('保留本地')                    if (document.getElementsByClassName('canvasimg')[0]) {                        document.getElementsByClassName('canvasimg')[0].remove()                    }                    let dom = document.getElementById('canvas')                    var img = new Image();                    img.crossOrigin = "anonymous"; //要害、容许跨域                    var image = dom.toDataURL('image/png')                    img.src = image;                    img.setAttribute('style','position: absolute; top: 0px; left: -666px;width: 320px; height: 750px;')                    img.setAttribute('class', 'canvasimg')                    document.body.appendChild(img);                    let canvasdom = document.getElementsByTagName('canvas')[0]                    html2canvas(canvasdom, {                        width: canvasdom.clientWidth, //dom 原始宽度                        height: canvasdom.clientHeight,                        scrollY: 0, // html2canvas默认绘制视图内的页面,须要把scrollY,scrollX设置为0                        scrollX: 0,                        useCORS: true, //反对跨域                        scale: 2, // 设置生成图片的像素比例,默认是1,如果生成的图片含糊的话能够开启该配置项。(设置为1无奈触发长按扫一扫)                    }).then((res) => {                        console.log(res)                        uni.postMessage({                            data: {                                msg: 'save',                                canvas: res.toDataURL('image/png').replace(/[\r\n]/g, '')                            }                        });                    }).catch(err => {                        console.log('生成失败')                    })                })            });        </script>    </body></html>

效果图: