前言,之前因为开发原生小程序的时候,就遇到了需要生成海报的场景。当时虽然说通过canvas自己纯手搓出来了海报,满足了需求。但是那一行行的代码,写的也是心力交瘁,而且代码看着也确实费劲,借鉴能力不强,给别的同学看了,还是得自己摸索着写,心智负担毛都没省。哎!感兴趣的可以看看原文章小程序生成分享海报,带小程序码,并保存图片。闲言少叙,今天带来的是一款挺好用的海报生成插件Painter,支持原生小程序和uniapp,Painter传送门

其实海报的痛点就是保真还原,如果能做到这点,那就属于一款好用的插件了。呐,Painter,就是。

说的花里胡哨的,怎么用呢?别急,看图说话:算了官网说的太多了,你自己看吧。我来说我怎么用的吧。

  1. 先克隆git clone https://github.com/Kujiale-Mobile/Painter.git;
  2. 克隆下来的项目,找到components/painter,如图,然后把它整个复制到你小程序的components文件夹中
  3. 在你要用的页面引入,类似这样:
  4. 在页面中写入,组件和承载容器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;"/>
  5. 最后就是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的使用流程也就是这样了。为了避免传送门,可能有时候打不开,我就把官网的截图发出来吧。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐