美文网首页微信小程序开发HTML5 Canvas
小程序篇-canvas使用小技巧

小程序篇-canvas使用小技巧

作者: 前端精 | 来源:发表于2018-08-27 17:00 被阅读1488次

    前面说的话

    某产品:这个版本我想要做个我们的内容可以生成海报的功能,

    用户可以保存海报,然后在朋友圈分享,

    巴拉巴拉

    。。。

    。。

    ,现在UI还在设计一款比较漂亮的海报。。。

    某前端(心里想):希望布局不要太复杂,不然用canvas生成的话,又得加班加点了。
    (眉头一皱):嗯,可以,到时候UI出来设计稿的时候,我就开始动工。


    然后UI出图了。【我们UI一般把图上传至 蓝湖 这样比较好看一些尺寸信息,下面的图片也是从 蓝湖 上截图的】

    设计稿图

    感觉还不错,不会很复杂的样子,(以这张海报先举个例子)

    看到这幅图的时候,你要做的几件事:

    1.首先问问UI,这是不是最终版本

    2.确定好是最终版本后,我们开始在这个海报页上区分,哪些是变量,哪些是固定元素/切图,哪些是UI需要提供的切图

    3.确定好之后,我们就可以开始动工了


    接下来我们就来看看几个需要实现的技术点

    1.海报中间的图片需要怎么样去控制,才能显示得“好(保持纵横比缩放图片,只保证图片的短边能完全显示出来也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。)”?

    2.底部的文字如何实现换行

    3.分享自@xxx 的文字如果太长怎么办?


    我们一一来解决这些问题吧

    基础准备,设计稿一般都为750*1334,一般来说我们会让设计稿标注各个元素的间距,这个间距是px单位的,

    但是小程序是rpx单位的,怎么建立起对应关系,而又在开发中提升效率呢

    举个例子
    首先我们创建一个叫做poster的小程序页面
    我们在wxml,这里有三个变量,分别是cardCreateImgUrl【canvas生成的图片路径】、WIDTH【设计稿的宽度】、HEIGHT【设计稿的高度】

    <!-- ... -->
    <view id="posterWrp">
      <view class="cardCreateWrp">
         <image src="{{cardCreateImgUrl}}" mode="aspectFill" class="imgCardWrp" bindload="bindload" 
    style="width:{{WIDTH}}rpx;height:{{HEIGHT}}rpx;"></image> 
      </view>
      <canvas class="myCanvas" canvas-id="myCanvas" style="width:{{WIDTH}}px;height:{{HEIGHT}}px;" ></canvas>
    </view>
    <!-- ... -->
    
    

    wxss中,我们把canvas的top设置成-9999px ,
    其实canvas是存在页面的,只是我们把它用这种方式隐藏了,
    这样做的目的,就相当于canvas我们现在只用来作为一个画图工具,
    我们画好canvas并把它生成的图片路径赋值到image标签的src来进行显示,
    这就是我们提到的提高开发效率的第一步,
    我们现在看到的是一张已经生成好的图片,而不是canvas

    // ...
    .cardCreateWrp image {
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
    }
    .myCanvas {
      position: absolute;
      top: -9999px;
      left: 50%;
      transform: translate(-50%, -50%);
    }
    // ...
    

    js中,
    我们根据设计稿的宽高来设置

    海报图片
    data: {
      // ...
      WIDTH: 750,
      HEIGHT: 1148,
      cardCreateImgUrl: ''
      // ...
    },
    

    我们在一开始画canvas的时候就要把canvas生成图片了

    // ...
    // 前面是一些canvans的操作,略过
    // ...
    ctx.draw(false, () => {
      // 生成图片
      wxp.canvasToTempFilePath({
        canvasId: 'myCanvas'
      }).then(({
        tempFilePath
      }) => {
        this.setData({
          cardCreateImgUrl: tempFilePath
        });
      });
    });
    

    这样子的话,我们一开始就是一张图片,边画画布的时候,就能看到图片生成的样子。

    这些都是准备工作,准备好了之后我们就可以开始解决我们在画图过程中的技术点了。


    1.海报中间的图片需要怎么样去控制,才能显示得“好(保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。)”?

    这里就要涉及到画布的clip()方法和我们用js去计算了,
    不清楚clip的话可以先去微信小程序官网api了解一下,
    了解以后我们来看以下的代码

    设计图的图片宽高是这样的 621px、668px
    距离顶部和左部是这样的 251px、66px

    代码就不多解释了,反正就是这些计算就是让图片能更好的显示

    // ...
    wxp.getImageInfo({
      // 七牛云做压缩
      src: `${data.imageUrl}?imageView2/1/w/621/h/668/q/50`
    }).then((res) => {
      // 计算海报宽高 
      const scale1 = res.width / res.height;
      const scale2 = 621 / 668;
      let drawW = 0,
          drawH = 0,
          mt = 0,
          ml = 0;
      if (scale1 > scale2) {
        drawH = 668;
        drawW = 668 * scale1;
        ml = (621 - drawW) / 2;
      } else {
        drawW = 621;
        drawH = drawW / scale1;
        mt = (668 - drawH) / 2;
      }
    
      ctx.save();
      ctx.beginPath();
      ctx.strokeStyle = "rgba(0,0,0,0)";
      ctx.rect(66, 251, 621, 668);
      ctx.closePath();
      ctx.stroke();
    
      ctx.clip();
      // 画图片
      ctx.drawImage(res.path, ml + 66, mt + 251, drawW, drawH);
      ctx.restore();
      
     // ...
    });
    // ...
    

    这样子,我们第一个技术点就解决了


    2.底部的文字如何实现换行
    我们都知道canvas不像我们在写html页面时,如果文字太长了,会自动换行。
    所以我们应该怎么做呢
    这里我们用到了 measureText() 这个方法

    我们把文字过长换行的方法封装了一下
    我们创建了一个名为canvas-text-break.js 的js文件

    
    /**
     * @desc canvas文字过长换行脚本
     * @param {Object} ctx canvas对象
     * @param {String} text 文字
     * @param {Number} x 距离左边的宽度
     * @param {Number} y 距离右边的宽度
     * @param {Number} w 文本区域的宽度
     * @param {Object} fontStyle 文本的字体风格/位置,有默认值
     */
    const CTB = ({
      ctx,
      text,
      x,
      y,
      w,
      fontStyle: {
        lineHeight = 60,
        textAlign = 'left',
        textBaseline = 'top',
        font = 'normal 40px arial',
        fillStyle = '#000000'
      }
    }) => {
      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])) {
            // console.log(`我是${chr[a]},我在末尾,我不换行`);
            temp += ` ${chr[a]}`;
            // 跳过这个字符
            a++;
          }
          if (/[《]/im.test(chr[a - 1])) {
            // console.log(`我是${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();
    };
    export default CTB;
    
    
    文字的相关信息

    根据上面这个图,我们就可以获取信息

      x: 64,
      y: 988,
      w: 350,
      fontStyle: {
        lineHeight: 39,
        textAlign: 'left',
        textBaseline: 'top',
        font: 'normal 23px arial',
        fontSize: 23,
        fillStyle: '#000000'
      }
    

    然后在js中我们这样子做

    import CTB from '../../utils/canvas-text-break';
    let ctx = null;
    // ...
    
    // ...
    ctx = wx.createCanvasContext('myCanvas');
    const text = '你来人间一趟,你要看看太阳。和你的心上人一起走在街上。';
    // ...
    CTB({
      ctx,
      text,
      x: 64,
      y: 988,
      w: 350,ß
      fontStyle: {
        lineHeight: 39,
        textAlign: 'left',
        textBaseline: 'top',
        font: 'normal 23px arial',
        fontSize: 23,
        fillStyle: '#000000'
      }
    });
    

    这样子,我们的第二个技术点也解决了


    3.分享自@xxx 的文字如果太长怎么办?
    这个问题比较简单


    文字距离顶部和右部是这样的 1065px、66px

    我们在js中这样写,记得
    ctx.textBaseline = 'top';
    ctx.textAlign = 'right';
    这样子的话文字多的话,就会往左边延伸了,这个原理和我们在操作html文字的原理是类似的

    ctx.save();
    ctx.font = 'bold 25px arial';
    ctx.fillStyle = '#000000';
    ctx.textBaseline = 'top';
    ctx.textAlign = 'right';
    const {
      nickName
    } = wx.getStorageSync('user');
    ctx.fillText(`分享自 @${nickName}`, WIDTH - 63, 1065);
    ctx.restore();
    

    这样子,我们所有的技术点都解决了


    注:wxp为我自己封装的对象,并不是wx的对象,在这里 小程序篇-wx自带api接入Promise,提升编程体验

    更新:可能有些人比较习惯看demo,这里分享一个 代码片段

    以上只是分享一些小技巧而已,我们在实际操作中肯定遇到的场景会更复杂,这些都是一些基础要知道的。
    ——尼古拉斯·峰

    相关文章

      网友评论

        本文标题:小程序篇-canvas使用小技巧

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