【小程序】利用canvas实现分享功能

一、分享功能核心思路

1.在wxml页面中添加最重要的两个标签:imagecanvas, image标签用于展示分享的图片,canvas用于画图、生成路径赋值给image的src,最后保存分享的图片

二、分享功能实现路线

1.UI图如下

这张设计图比较简单,数据都是从后端返回,直接渲染上去就行。主要注意标题超长时的特殊处理。此外,二维码是后端返回,然后渲染上去。实际工作中后端返回base64编码,需要编译后利用ctx.drarImage画上去

image

2.wxml和wxss布局

(1) 在移动端呈现的效果如下,因为做的是一个简单的demo,没有后端返回数据,所以没有做二维码。布局和样式做得比较简单。

image

1.1 canvas.wxml


    
        
        
    
    保存图片

1.2 canvas.wxss

page {height: 100%}
.model-wrap {
    position: relative;
    overflow: hidden;
    height: 100%;
    background: rgba(0,0,0,.4);
}
.model-box {
    position: absolute;
    top: 50%;
    left: 0;
    transform: translate(0, -50%);
}
image {
    width: 750rpx;
    height: 788rpx;
}
canvas {
    position: absolute;
    top: 0;
    left: 750px;
    width: 750px;
    height: 788px;
}
.save-btn {
    text-align: center;
    color: #fff;
    position: absolute;
    top: 90%;
    left: 43%;
    transform: translate(0, -50%);
}

3. 开始画图

3.1 首先设置 imgUrl 并模拟一些数据

data: {
        imgUrl: '', // 用于获取canvas图片路径并渲染在image标签中
        shareDate: {
            name: 'Damon',
            title: '标题知识产权专项资金知识产权专项资金项资金项资金 ',
            status: '有效',
            area: '四川省成都市'
        }
    },

3.2 开始画图
背景图的路径需要是 https 的

const bgUrl = "https://cdn.chacha.top/mini_pro/share_bg.png";
draw() {
    let _that = this; // 先保存this,如果你以组件的方式写分享功能,在生成图片时要传入this
    wx.getImageInfo({
            src: bgUrl,
            success(res) {
                let {shareDate} = _that.data;
                let titleHeight;  // 标题高度
                let ctx = wx.createCanvasContext('myCanvas');
  
                   ctx.drawImage(res.path, 0, 0, 750, 788);
                         // 这里放 画在画布的文字
                   ctx.draw(true, (res)=> {
                        wx.canvasToTempFilePath({
                            canvasId: 'myCanvas',
                            success(res) {
                                _that.setData({
                                        imgUrl: res.tempFilePath
                                })
                            }
                    }, _that)
             })
         }
})

完成以上两步,就能把背景图画出来了,如下图所示


image.png

3.3 补充用户名、标题、其他内容

//  用户名
util.wrapText({
    ctx,
    text:shareDate.name,
    x: 120,
    y: 75,
    w: 170,
    fontStyle: {
       lineHeight: 83,
       textAlign: 'left',
       textBaseline: 'top',
       font: 'normal 24px arial',
       fillStyle: '#333'
    }
})

// 标题
// 如果标题过长时截取
let title = shareDate.title.length < 80 ? shareDate.title : shareDate.title.substring(0,80)+'...'
titleHeight = util.wrapText({
    ctx,
    text: title,
    x: 42,
    y: 195,
    w: 645,
    fontStyle: {
        lineHeight: 58,
        textAlign: 'left',
        textBaseline: 'top',
        font: 'normal 40px arial',
        fillStyle: '#535353'
    }
});

                
let top1 = titleHeight + 120; // 地区 在y轴上的值
let top2 = top1 + 45; // 状态 在y轴上的值

// 地区
ctx.font = 'normal normal 28px arial';
ctx.fillStyle = '#b4a296';
ctx.fillText('适用于 ', 42, top1);

let w1 = ctx.measureText('适用于 ').width;
ctx.font = 'normal normal 28px arial';
ctx.fillStyle = '#ff7010';
ctx.fillText(shareDate.area, 42+w1, top1);

let w2 = ctx.measureText(shareDate.area).width;
ctx.font = 'normal normal 28px arial';
ctx.fillStyle = '#b4a296';
ctx.fillText(' 的机构', 42+w1+w2, top1);

 // 政策状态
ctx.font = 'normal normal 28px arial';
ctx.fillStyle = '#b4a296';
ctx.fillText('政策状态:', 42, top2);

let w3 = ctx.measureText('政策状态:').width;
ctx.font = 'normal normal 28px arial';
ctx.fillStyle = '#ff7010';
ctx.fillText(shareDate.status, 42+w3, top2);


// 长按小程序码查看详情
ctx.font = 'normal normal 24px arial';
ctx.fillStyle = '#535353';
ctx.fillText('长按小程序码查看详情', 35, 670);

呈现的效果如下,由于 标题的高度变化 会影响到适用地区和政策状态的渲染高度,所以动态设置。

image.png

3.4 用户头像
用户头像放在最后是因为 头像链接 是外链,通过异步加载获取,所以放在最后画方便控制,ctx.draw()也需要放在成功的回调中,不然会渲染不上

// 头像
wx.downloadFile({
      url: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1543030619359&di=d26dfbfe8898517f3fc0d14dd7fb1437&imgtype=0&src=http%3A%2F%2Fb.hiphotos.baidu.com%2Fzhidao%2Fwh%253D450%252C600%2Fsign%3Df6277da51a4c510fae91ea1e5569091b%2F4b90f603738da97748712546b051f8198618e305.jpg',
      success(res) {

           let width = 63; //头像宽
           let height = 63; //头像高
           let x = 40; // 头像距x轴距离
           let y = 55; // 头像距y轴距离

           ctx.beginPath();
           ctx.arc(width/2+x, height/2+y, width/2, 0, Math.PI*2);
           ctx.clip();
           ctx.drawImage(res.tempFilePath, x, y, width, height);


           ctx.draw(true, (res)=> {
                wx.canvasToTempFilePath({
                    canvasId: 'myCanvas',
                    success(res) {

                       _that.setData({
                           imgUrl: res.tempFilePath
                       })
                    }
               }, _that)
          })
       }
 })

3.5 保存图片
保存图片功能比较简单,主要下载canvas图片生成的路径,在下载时调用wx.saveImageToPhotosAlbum({})这个api

save() {
        let _that = this;
        wx.showLoading({
            mask: true
        })
        wx.canvasToTempFilePath({
            x: 0,
            y: 0,
            width: 750,
            height: 788,
            destWidth: 750,
            destHeight: 788,
            canvasId: 'myCanvas',
            success(res) {
                
                wx.saveImageToPhotosAlbum({
                    filePath: res.tempFilePath,
                    success(res) {
                        wx.hideLoading();
                        wx.showModal({
                            title: '提示',
                            showCancel: false,
                            confirmText: '知道了',
                            confirmColor: '#0facf3',
                            content: '已成功为您保存图片到手机相册,请自行前往朋友圈分享。',
                            success: (res) => {
                                if (res.confirm) {
                                    console.log('保存成功,隐藏模态框')
                                }
                            }
                        })
                    },
                    fail(res) {
                        wx.hideLoading();
                        wx.showModal({
                            title: '保存出错',
                            showCancel: false,
                            confirmText: '知道了',
                            confirmColor: '#0facf3',
                            content: '您拒绝了授权 ,如果您要保存图片,请删除小程序,再重新打开。',
                            success: (res) => {
                                console.log(res)
                            }
                        })
                    }
                })
            }
        }, _that)
    }

4.用到的文本折行方法

const wrapText = ({
    ctx,
    text,
    x,
    y,
    w,
    fontStyle: {
        lineHeight = 60,
        textAlign = 'left',
        textBaseline = 'top',
        font = 'normal 40px arial',
       fillStyle = '#000'
    }
}) => {
      ctx.save();
      ctx.font = font;
      ctx.fillStyle = fillStyle;
      ctx.textAlign = textAlign;
      ctx.textBaseline = textBaseline;
      const chr = text.split('');
      const row = [];
      let temp = '';

      for (let a = 0 ; a < chr.length ; a++) {
          if (ctx.measureText(temp).width

三、总结

第一次实现分享功能时一头雾水,不知道从哪入手,看了很多教程,发现有两种方法实现。

第一种是 在页面布局时放两个 canvas,一个用于展示,另一个用于保存。这种方法要根据UI图去计算两个canvas的缩放比例,在画图时也需要画两次,第一次花在展示的canvas上,第二次根据第一次画距离*比例。我利用这种方法去做,发现比例不太方便控制,如果内容再多一些,要写更多的代码。

第二种就是今天分享的,在计算距离时,只需要根据UI图给出来的数字填上去就行,偶尔微调一下。保存出来的效果图也很好看。

项目地址:
https://github.com/keepi/canvasshare

你可能感兴趣的:(【小程序】利用canvas实现分享功能)