美文网首页
【小程序】利用canvas实现分享功能

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

作者: 花卷呀花卷 | 来源:发表于2018-11-24 11:09 被阅读0次

    一、分享功能核心思路

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

    二、分享功能实现路线

    1.UI图如下

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

    image

    2.wxml和wxss布局

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

    image

    1.1 canvas.wxml

    <view class="model-wrap">
        <view class="model-box">
            <image src="{{imgUrl}}"></image>
            <canvas canvas-id="myCanvas"></canvas>
        </view>
        <view class="save-btn" bindtap="save">保存图片</view>
    </view>
    

    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<w) { } else {
                  if (/[,。!》]/im.test(chr[a])) {
                      temp += ` ${chr[a]} `;
                      a++;
                  }
    
            if (/[《]/im.test(chr[a-1])) {
                    temp = temp.substr(0, temp.length -1);
                    a--;
            }
    
                row.push(temp);
                temp = '';
          }
                temp += chr[a] ? chr[a]: '';
        }
            row.push (temp);
            for(let b = 0 ; b < row.length ; b++) {
                ctx.fillText(row[b], x, y+b*lineHeight)
    
            }
        
            ctx.restore();
            return y+(row.length-1)*lineHeight //返回文本高度
    }
    
    

    三、总结

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

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

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

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

    相关文章

      网友评论

          本文标题:【小程序】利用canvas实现分享功能

          本文链接:https://www.haomeiwen.com/subject/ztpeqqtx.html