微信小程序之实现两张图片合成一张并保存到相册

不喜欢看废话的小伙伴,可以直接看图片下面的文字和注意事项 实现是没问题的。

应用场景:

项目中,每个用户都会生成自己专属的小程序码,当用户扫描这个小程序码的时候,默认这个用户就会成为你的好友,,嗯,举个身边的例子,比如现在最火的 [邀好友领红包],通过扫描二维码确定绑定关系。(不要纠结为啥放这张图片,因为刚好昨天我妹妹发我,让我给她扫一扫,🌰栗子放在这里 感觉会更加清楚一点,有点广告嫌疑 哈哈哈哈 可以忽略 )。

在小程序中,我们可以生成带参的小程序码,但是呢,这个小程序码是一张小程序码图片,如果想要和产品放在一张图上,前端不做,后端做,反正总要有人做,然后,,决定让前端来做。ok,动起来,开始干。


image.png

结合文档和度娘,发现可以用canvas来实现,发现用两个函数就可以搞定, CanvasContext.drawImage 画出你想画的图片
wx.canvasToTempFilePath 把画布的内容输出为图片

实际效果:
两张独立的图片,根据需要绘制在指定位置,合成一张图片,并保存到相册。

image.png

注意事项:
CanvasContext.drawImage

drawImage(imageResource, dx, dy)
drawImage(imageResource, dx, dy, dWidth, dHeight)
drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight) 从1.9.0 起支持

imageResource,绘制的图片的路径,必须是本地图片,如果是网络图片,必须用wx.downloadFile获取本地图片的缓存后再绘制。
dx、dy图片的位置,画布左上角为原点(0,0),注意是画布的坐标原点而不是以手机屏幕为准。
dWidth, dHeight 绘制图片的宽高,即你想把这张图片画多大,如果想要绘制原图,则通过 wx.getImageInfo获取图片的尺寸信息,直接进行绘制就可以了。

特别注意,CanvasContext.drawImage中的dx, dy, dWidth, dHeight都是以像素px为单位的,所以不要用 rpx !!(我踩过的坑)

wx.canvasToTempFilePath
对这个函数,我的理解就是 手机截屏,截的是画布中的内容,画布多大它就多大,嗯,说到这,你应该就知道怎么控制底图大小,然后还很完美没有多余的白边。

代码:

可以直接用的代码,不过还是建议自己写下,也许写的过程还会发现其他有趣的问题呢。

  // 保存图片到相册
  savePhoto: function(index) {
    let that = this
    let imgs = that.data.imgs

    if (imgs[index].isDownLoad) {
      // 如果已经下载过 提示用户
      wx.showToast({
        title: '你已经下载过该图片',
        icon: 'none'
      })
      return
    } else {
      // 提示用户正在合成,否则用户可能有不当操作或者以为手机卡死
      wx.showLoading({
        title: '合成中......',
        mask: true
      })

      // 创建画布对象
      const ctx = wx.createCanvasContext("myCanvas", that)
      // 获取图片信息,要按照原图来绘制,否则图片会变形 
      wx.getImageInfo({
        src: that.data.imgs[index].img,
        success: function(res) {
          // 根据 图片的大小 绘制底图 的大小
          console.log(" 绘制底图 的图片信息》》》", res)
          let imgW = res.width
          let imgH = res.height
          let imgPath = res.path

          that.setData({
            canvasHeight: imgH,
            canvasWidth: imgW
          })
          // 绘制底图 用原图的宽高比绘制
          ctx.drawImage(imgPath, 0, 0, imgW, imgH)

          wx.getImageInfo({
            src: that.data.codeImg, // 二维码图片的路径
            success: function(res) {
              console.log(" 绘制二维码》》》", res)
              // 绘制二维码
              ctx.drawImage(res.path, 50, imgH - 380, 300, 300)
              ctx.draw()

              wx.showLoading({
                title: '正在保存',
                mask: true

              })

              setTimeout(() => {
                wx.canvasToTempFilePath({
                  canvasId: 'myCanvas',
                  success: function(res) {
                    console.log("合成的带有小程序码的图片success》》》", res)
                    let tempFilePath = res.tempFilePath
                    // 保存到相册
                    wx.saveImageToPhotosAlbum({
                      filePath: tempFilePath,
                      success(res) {

                        // 修改下载状态
                        imgs[index].isDownLoad = true

                        wx.hideLoading()
                        wx.showModal({
                          title: '温馨提示',
                          content: '图片保存成功,可在相册中查看',
                          showCancel: false,
                          success(res) {
                            wx.clear
                            if (res.confirm) {
                              that.setData({
                                isShow: true
                              })
                            }
                          }
                        })

                        that.setData({
                          imgs: imgs,
                        })
                      },

                      fail(res) {
                        wx.hideLoading()
                        wx.showModal({
                          title: '温馨提示',
                          content: '图片保存失败,请重试',
                          showCancel: false
                        })
                      }
                    })

                    console.log("合成的带有小程序码的图片的信息》》》", res)
                  },
                  fail: function(res) {
                    console.log("生成的图拍呢 失败 fail fail fail ", res)
                    wx.hideLoading()
                    wx.showModal({
                      title: '温馨提示',
                      content: '小程序码图片合成失败,请重试',
                      showCancel: false
                    })
                  }
                }, that)
              },1500)
            },
            fail(res) {
              wx.hideLoading()
              wx.showModal({
                title: '温馨提示',
                content: '二维码获取失败,请重试',
                showCancel: false
              })
            }
          })

        },
        fail(res) {
          wx.hideLoading()
          wx.showModal({
            title: '温馨提示',
            content: '图片信息获取失败,请重试',
            showCancel: false
          })
        }
      })
    }

  },

wxml:

 <view class="placeHoder"></view>
 <canvas style="width:{{canvasWidth}}px; height:{{canvasHeight}}px;" canvas-id="myCanvas" />

json
禁止页面滑动

 "disableScroll":true

真良心,xml也给贴出来啦。isShow 是因为我想直接保存,不给用户看到绘制的过程,如果你那里可以预览的话,简直完美,不需要隐藏绘制过程。

一点曲折

需求:

用户看到的是一张不包含二维码的缩略图,当用户点击之后 会执行下载操作,把包含二维码的原图保存到手机相册中,用户不能看到绘制过程,

解决思路:

原先的处理是 用hidden来控制canvas的显示隐藏,由于原图比较大 所以会出现页面滑动出现大量空白页面的问题,并且 有可能绘制不出图片,有时候会出现小程序意外退出的问题。

后来 我这里直接 用一个view和缩略图占据全屏,然后 在看不到的地方绘制原图和二维码,绘制完成后进行保存。

问题:
IOS: canvasToTempFilePath:fail. errMsg:"canvasToTempFilePath:fail no image"
Android:canvasToTempFilePath:fail. errMsg:"canvasToTempFilePath:fail create bitmap failed"

这个问题 是由于对canvas 用了hidden 属性,导致绘图失败,本来是打算用用hidden来隐藏绘制过程的,前期没有问题,后来出现绘制失败 无法保存合成的图片,甚至小程序意外退出的情况,因此,canvas绘制在屏幕外,禁止屏幕滑动来隐藏绘制过程。

另外一个需要注意的问题是 当点击图片的时候会进行保存,如果用户连续点击多次则会出现卡死退出的情况,因为绘制的是原图,比较大,所以当正在绘制的时候要控制不能多次下载绘制。可以用一个变量来控制。

IOS 保存多张图片的时候会出现内存不足,小程序退出的情况,因为在绘制过程中会先缓存图片然后进行绘制,当图片过多的时候会出现这种情况,需要在微信中 设置==> 存储 ==> 缓存 清除缓存 再次进行存储。

嗯,惯例,写给自己一句话,好好爱自己呀。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容