能在安卓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>
效果图: