推荐一款亲测好用的小程序海报生成插件Painter
本文介绍了小程序海报生成插件Painter的使用方法。作者曾手动编写canvas代码生成海报,过程繁琐且复用性差。Painter插件支持原生小程序和uniapp,能精准还原设计稿。使用方法包括:1)克隆GitHub仓库获取组件;2)复制painter组件到项目;3)在页面引入组件并配置画布参数;4)通过JSON模板定义海报样式和内容。文章详细展示了医疗证明海报的实现代码,包括文字、二维码、边框等元
·
前言,之前因为开发原生小程序的时候,就遇到了需要生成海报的场景。当时虽然说通过canvas自己纯手搓出来了海报,满足了需求。但是那一行行的代码,写的也是心力交瘁,而且代码看着也确实费劲,借鉴能力不强,给别的同学看了,还是得自己摸索着写,心智负担毛都没省。哎!感兴趣的可以看看原文章小程序生成分享海报,带小程序码,并保存图片。闲言少叙,今天带来的是一款挺好用的海报生成插件Painter,支持原生小程序和uniapp,Painter传送门
其实海报的痛点就是保真还原,如果能做到这点,那就属于一款好用的插件了。呐,Painter,就是。
说的花里胡哨的,怎么用呢?别急,看图说话:算了官网说的太多了,你自己看吧。我来说我怎么用的吧。
- 先克隆git clone https://github.com/Kujiale-Mobile/Painter.git;
- 克隆下来的项目,找到components/painter,如图,然后把它整个复制到你小程序的components文件夹中
- 在你要用的页面引入,类似这样:
- 在页面中写入,组件和承载容器image
<painter customActionStyle="{{customActionStyle}}" palette="{{paintPallette}}" bind:imgOK="onImgOK" bind:touchEnd="touchEnd" action="{{action}}" widthPixels="1000" /> <image src="{{image}}" style="width: {{cvsWidth}}px; height: {{cvsHeight}}px; position: absolute;left: -100%;top: 0;"/>
- 最后就是js了,为了能够逻辑连贯,直接把我自己的页面的js都放上去吧
import { request } from '../../../../../../utils/request'; import { toastMsg } from '../../../../../../utils/toast'; Page({ /** * 页面的初始数据 */ data: { cvsWidth:0, cvsHeight:0, zmid:'', detail:'', paintPallette:'', customActionStyle: { border: { borderColor: '#1A7AF8', }, scale: { textIcon: '/palette/switch.png', imageIcon: '/palette/scale.png', }, delete: { icon: '/palette/close.png', }, }, qrCodeUrl:'', }, /** * 生命周期函数--监听页面加载 */ onLoad(options) { if(options&&options.zmid){ this.setData({ zmid:options.zmid }) } if(options&&options.scene){ let zmid = decodeURIComponent(options.scene); this.setData({ zmid:zmid }) } this.getDetail() }, async getDetail(){ wx.showLoading({ title: '加载中···', }) const {data} = await request.get(`/feeCenter/hisMedicalCertQueryOne/${this.data.zmid}`) wx.hideLoading() this.setData({ detail:data }) }, onImgOK(e) { this.imagePath = e.detail.path; this.setData({ image: this.imagePath, }); wx.hideLoading() this.saveImage(this.imagePath); }, saveImage() { if (this.imagePath && typeof this.imagePath === 'string') { this.isSave = false; wx.saveImageToPhotosAlbum({ filePath: this.imagePath, success(res){ toastMsg("保存成功,可到相册查看") } }); } }, touchEnd({ detail }) { let needRefresh = detail.index >= 0 && detail.index <= this.data.template.views.length; if (needRefresh) { this.history.push({ ...detail, }); if (this.data.template.views[detail.index].id === detail.view.id) { this.data.template.views.splice(detail.index, 1); } else { this.data.template.views.splice(detail.index, 0, detail.view); } } else { if (!this.data.template || !this.data.template.views) { return; } for (let view of this.data.template.views) { if (view.id === detail.view.id) { this.history.push({ view: { ...detail.view, ...view, }, }); view.css = detail.view.css; break; } } } this.future.length = 0; const props = { paintPallette: this.data.template, }; if (needRefresh) { props.template = this.data.template; } this.setData(props); }, async saveDownImg(){ wx.showLoading({ title: '加载中···', }) const {data} = await request.get(`/feeCenter/createQrCode/${this.data.detail.zmid}`) if(data){ this.setData({ qrCodeUrl:data }) let that = this; const query = wx.createSelectorQuery().in(this); query.select('#medical_content').fields({ size: true, scrollOffset: true }, data => { let width = data.width; let height = data.height; that.setData({ cvsWidth: width, cvsHeight:height+43 }) }).exec(); setTimeout(() => { console.log("宽高",that.data.cvsWidth,that.data.cvsHeight) that.startDraw() }, 500); } }, startDraw() { var msgBoxHeight = 0; var firstHeight = 0; var endHeight = 0; const query = wx.createSelectorQuery().in(this); query.select('#medical_msg_box').fields({ size: true, scrollOffset: true }, data => { msgBoxHeight = data.height+16 }).exec(); query.select('#first_zhenduan').fields({ size: true, scrollOffset: true }, data => { firstHeight = data.height }).exec(); query.select('#last_yijian').fields({ size: true, scrollOffset: true }, data => { endHeight = data.height }).exec(); setTimeout(() => { console.log(msgBoxHeight,firstHeight,endHeight) let templateObj = { width: this.data.cvsWidth+'px', height: this.data.cvsHeight+'px', background: '#ffffff', views: [ { type: 'rect', css: { width:this.data.cvsWidth+'px', height:this.data.cvsHeight+'px', color: '#FFFFFF', boxShadow: 'inset 0rpx -2rpx 2rpx 0rpx rgba(255,255,255,0.5)', borderRadius: '16rpx', padding: '40rpx 36rpx', }, }, { type: 'text', text: "某某妇产科医院", css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '32rpx', color: '#333333', lineHeight: '44rpx', textAlign: 'center', top:'40rpx', left:'140rpx', }], }, { type: 'text', text: "医疗证明书", css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '600', fontSize: '36rpx', color: '#333333', lineHeight: '50rpx', textAlign: 'center', top:'94rpx', left:'254rpx', }], }, { type: 'rect', css: { width:'182rpx', height:'144rpx', color: '#EFF6FF', borderRadius: '8rpx', top:'124rpx', left:'36rpx' }, }, { type: 'text', text: '扫码认证', css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '24rpx', width:'30rpx', color: '#384D6C', lineHeight: '28rpx', top:'138rpx', left:'184rpx', }], }, { type: 'image', url: this.data.qrCodeUrl, css: { top: '140rpx', left: '52rpx', width: '111rpx', height: '111rpx', borderRadius:'50%' }, }, { type: 'text', text: this.data.detail.kjsj, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '26rpx', color: '#8A8A8A', lineHeight: '36rpx', textAlign: 'center', top:'230rpx', right:'36rpx', }], }, { type: 'rect', css: { width: '614rpx', height: msgBoxHeight+'px', color: '#ffffff', left: '36rpx', top: `284rpx`, borderRadius: '12rpx', borderWidth: '2rpx', borderColor: '#D4D4D4', borderStyle: 'solid', borderTop:'none' }, }, { type: 'text', text: "姓名:"+this.data.detail.brxm, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '28rpx', color: '#333333', lineHeight: '40rpx', top:'316rpx', left:'72rpx', }], }, { type: 'text', text: "性别:"+this.data.detail.brxb, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '28rpx', color: '#333333', lineHeight: '40rpx', top:'316rpx', left:'380rpx', }], }, { type: 'text', text: "年龄:"+this.data.detail.brnl, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '28rpx', color: '#333333', lineHeight: '40rpx', top:'372rpx', left:'72rpx', }], }, { type: 'text', text: "病案号:"+this.data.detail.bahm, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '28rpx', color: '#333333', lineHeight: '40rpx', top:'372rpx', left:'380rpx', }], }, { type: 'rect', css: { width: '614rpx', height: '2rpx', color: '#D4D4D4', left: '36rpx', top: `444rpx`, }, }, { type: 'text', text: "初步诊断:", css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '26rpx', color: '#333333', lineHeight: '36rpx', top:'478rpx', left:'72rpx', }], }, { type: 'text', text: this.data.detail.zdmc, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '28rpx', color: '#333333', lineHeight: '36rpx', top:'526rpx', left:'72rpx', width:'542rpx', }], }, { type: 'rect', css: { width: '614rpx', height: '2rpx', color: '#D4D4D4', left: '36rpx', top: (276+firstHeight)+'px', }, }, { type: 'text', text: "处理意见:", css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '26rpx', color: '#333333', lineHeight: '36rpx', top:(293+firstHeight)+'px', left:'72rpx', }], }, { type: 'text', text: this.data.detail.clyj, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '28rpx', color: '#333333', lineHeight: '36rpx', top:(312+firstHeight)+'px', left:'72rpx', width:'542rpx', }], }, { type: 'text', text: "医生签名:"+this.data.detail.kjys, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '24rpx', color: '#333333', lineHeight: '40rpx', top:(328+firstHeight+endHeight)+'px', left:'72rpx', }], }, { type: 'text', text: "医院盖章:", css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '24rpx', color: '#333333', lineHeight: '40rpx', top:(328+firstHeight+endHeight)+'px', left:'388rpx', }], }, { type: 'text', text: "开具时间:"+this.data.detail.kjsj, css: [{ fontFamily: 'PingFangSC, PingFang SC', fontWeight: '400', fontSize: '24rpx', color: '#333333', lineHeight: '40rpx', top:(348+firstHeight+endHeight)+'px', left:'72rpx', }], }, { type: 'image', url: 'https://womanhospital-public-oss.gjwlyy.com/resource/img/yiliaozhengmingzhang.png', css: { bottom: '50rpx', right: '70rpx', width: '200rpx', height: '200rpx', }, }, ], } this.setData({ paintPallette:templateObj }) }, 500); // let that = this; // // 创建wxml2canvas对象 // let drawMyImage = new Wxml2Canvas( // { // element: "canvasId", // canvas的id, // obj: that, // 传入当前组件的this // width: that.data.cvsWidth*2, // canvas画布宽度 // height: that.data.cvsHeight*2, // canvas画布高度 // background: "#FFFFFF", // 生成图片的背景色 // progress(percent) { // // 进度 // // console.log(percent); // }, // finish(url) { // // 生成的图片 // wx.hideLoading(); // that.savePoster(url); // }, // error(res) { // wx.hideLoading() // // 失败原因 // console.log(res); // }, // }, // this // ); // let data = { // // 获取wxml数据 // list: [ // { // type: "wxml", // class: ".medical_content .draw_medical_content", // draw_pnode要绘制的wxml元素根类名, draw_node单个元素的类名(所有要绘制的单个元素都要添加该类名) // limit: ".medical_content", // 要绘制的wxml元素根类名,也就是指定要绘制哪些父级中的元素 // x: 15, // y: 0, // }, // ], // }; // // 绘制canvas // drawMyImage.draw(data, this); }, savePoster(url) { const that = this; wx.saveImageToPhotosAlbum({ filePath: url, success: function () { wx.showToast({ title: "保存成功", icon: "none", duration: 1500, }); }, fail(err) { if ( err.errMsg === "saveImageToPhotosAlbum:fail:auth denied" || err.errMsg === "saveImageToPhotosAlbum:fail auth deny" || err.errMsg === "saveImageToPhotosAlbum:fail authorize no response" ) { wx.showModal({ title: "提示", content: "需要您授权保存相册", showCancel: false, success: (modalSuccess) => { wx.openSetting({ success(settingdata) { if (settingdata.authSetting["scope.writePhotosAlbum"]) { wx.saveImageToPhotosAlbum({ filePath: that.data.imgUrl, success: function () { wx.showToast({ title: "保存成功", icon: "success", duration: 2000, }); }, }); } else { wx.showToast({ title: "授权失败,请稍后重新获取", icon: "none", duration: 1500, }); } }, }); }, }); } }, }); }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady() { }, /** * 生命周期函数--监听页面显示 */ onShow() { }, /** * 生命周期函数--监听页面隐藏 */ onHide() { }, /** * 生命周期函数--监听页面卸载 */ onUnload() { }, /** * 页面相关事件处理函数--监听用户下拉动作 */ onPullDownRefresh() { }, /** * 页面上拉触底事件的处理函数 */ onReachBottom() { }, })
6.对应效果图
到这里就算结束了,Painter的使用流程也就是这样了。为了避免传送门,可能有时候打不开,我就把官网的截图发出来吧。
更多推荐
所有评论(0)