小程序制作海报保存到相册

426 阅读4分钟

说在前面的话

自己第一次制作了海报并且保存相册,故来记录一下。。。分享给需要的朋友,这里以字节小程序为例,明白思路,学以致用!!!

正文开始了(一本正经)

思路:
	1.用canvas绘图,将自己的海报绘制出来
	2.将绘制好的canvas转化成图片
	3.调用api将生成的图片保存到相册
	tip:为什么要用canvas呢?因为只有图片才可以保存到相册,把你海报的所有东西变成一张图片,仿佛只能canvas了吧。思路了解,码上就来!

第一步:绘制海报

我们可以想一下海报的构成:背景,头像,昵称,标题,正文,二维码。。。主要可能也就是这些了吧。然后再来分析一下,背景、二维码、头像是图片;昵称标题,正文是文字。所以就是这么简单,图片加文字,自己测量好长宽、位置放置就好啦。第一次绘制canvas的同学不要慌,就是如此简单的吖!!!

我呢还是很贴心的啦,虽然没有成品的海报(毕竟每个人的海报中内容不一样),but 我把文字图片的绘制方法都写出来了,还绘制了一些常用图形,以及海报中可能遇到的一些需求,所以大家根据自己的需要修改他们的大小和位置就好啦

index.js 首先创建一个canvas(我是在onshow中)

data: {
    canvasWidth: 375,
    canvasHeight: 500,
    bgwidth: "",
    bgHeight: "",
    text: "骑着我心爱的小摩托温婉居家小仙女啦啦啦",
    img: ''
  },
onShow() {
    this.ctx = tt.createCanvasContext("myCanvas");//myCanvas是canvas的id,这个之后我们在html中给大家展示写法
    this.createCanves();//调用绘制海报的方法
  },
  createCanves() {
    let ctx = this.ctx;
    let imgArr = {
      url1:
        "../../image/bg.jpg",
      url2: "../../image/user.jpg",
    };
    this.drawBg(imgArr.url1);
    let row = this.drawText(this.data.text, 60, 240, 20, 50);
    this.drawOther(imgArr.url2, row, 20);//这个是文字换行的方法
    ctx.draw(true, () => {
      setTimeout(() => {
        this.canvasToTempFilePath();
      }, 500);
    });
  },
  // 绘制背景
  async drawBg(url) {
    let ctx = this.ctx;
    ctx.setFillStyle("#ffffff");//最好是白色的背景,默认是黑色的
    ctx.fillRect(0, 0, this.data.canvasWidth, this.data.canvasHeight);
    let bgImg = this.data.canvasBg ? this.data.canvasBg : url;
    // drawImage 绘制一个图片,可以有三种写法
    // 3参数法:图片,开始横坐标,开始纵坐标。原图铺满整个canvas,导致图片显示不完整
    // 5参数法:图片,开始横坐标,开始纵坐标,在canvas中的宽,在canvas中的高。图片回压缩完整显示在指定大小内,图片全部显示
    // 7参数法:图片,图片开始截取横坐标,图片开始截取纵坐标,截取图片的宽,截取图片的高,canvas中开始横坐标,canvas中开始纵坐标,在canvas中的宽,在canvas中的高。可以灵活截取图片的大小。
    ctx.drawImage(bgImg, 0, 0, this.data.canvasWidth, this.data.canvasHeight);
  },
  // 绘制其他一些东西,这里可以根据需要自己绘制,我这里只绘制了几个常用的图形
  drawOther(url, row, textLineHeight) {
    let ctx = this.ctx;
    // 画长方形
    ctx.save();
    ctx.setStrokeStyle("#ffff00");
    ctx.strokeRect(50, 150, 50, 50);
    ctx.setFillStyle("#ff00ff");
    ctx.fillRect(120, 150, 50, 50);
    // 画圆,给圆里面添加图片,此时图片会将圆覆盖
    let r = 25;
    ctx.setStrokeStyle("#ffff00");
    ctx.setLineWidth("4");
    ctx.setLineCap("round"); //圆环结束断点的样式  butt为平直边缘 round为圆形线帽  square为正方形线帽
    ctx.arc(215, 150 + r, r, 0, 2 * Math.PI);
    ctx.stroke();
    ctx.clip();
    ctx.drawImage(url, 215 - r, 150, 50, 50);
    ctx.restore();
    // 画一个圆角矩形,绘制顺序:左上角-top线-右上角-right线-右下角-bottom线-左下角-left线
    let h = 220;
    let w = 50;
    let r1 = 10;
    let textH = row * textLineHeight;
    ctx.save();
    ctx.beginPath();
    ctx.arc(w + r1, h + r1, r1, 1 * Math.PI, 1.5 * Math.PI);
    ctx.moveTo(w + r1, h);
    ctx.lineTo(w + r1 + 40, h);
    ctx.arc(w + r1 + 40 + r1, h + r1, r1, 1.5 * Math.PI, 2 * Math.PI);
    ctx.moveTo(w + 3 * r1 + 40, h + r1);
    ctx.lineTo(w + 3 * r1 + 40, h + r1 + textH);
    ctx.arc(w + r1 + 40 + r1, h + r1 + textH, r1, 0 * Math.PI, 0.5 * Math.PI);
    ctx.moveTo(w + 2 * r1 + 40, h + 2 * r1 + textH);
    ctx.lineTo(w + r1, h + 2 * r1 + textH);
    ctx.arc(w + r1, h + r1 + textH, r1, 0.5 * Math.PI, 1 * Math.PI);
    ctx.moveTo(w, h + r1 + textH);
    ctx.lineTo(w, h + r1);
    ctx.stroke();
    ctx.restore();
  },
  // 在canvas中文字是不会自己换行的,所以这里用到了一个自动换行的方法
   drawText(text, startX, startY, lineHeight, MAX_WIDTH) {
    let ctx = this.ctx;
    let allArr = text.split("");
    let rowArr = []; //拆分出来的每一行
    let rowStrArr = []; //每一行的文字数组
    for (let i = 0; i < allArr.length; i++) {
      let currentStr = allArr[i];
      rowStrArr.push(currentStr);
      let rowStr = rowStrArr.join("");
      console.log(rowStr);
      if (ctx.measureText(rowStr).width > MAX_WIDTH) {
        rowStrArr.pop(); //删除最后一个
        rowArr.push(rowStrArr.join(""));
        rowStrArr = [currentStr];
        continue;
      }
      // 最后一个字母,直接添加到最后一行
      if (i == allArr.length - 1) {
        rowArr.push(rowStr);
      }
    }
    ctx.setFillStyle("#ff0000");
    // ctx.setTextAlign('center');
    for (let i = 0; i < rowArr.length; i++) {
      ctx.fillText(rowArr[i], startX, startY + i * lineHeight);
    }
    return rowArr.length; //返回行数,几行
  },

第二步:canvas转换图片

// 转化图片
  canvasToTempFilePath() {
    let that = this;
    // 当canvas太小的时候,转化为图片会变模糊,可以将图片适当调大一些,canvas足够大时则不用,注意canvas大小有限制。
    tt.canvasToTempFilePath({
      canvasId: 'myCanvas',
      x: 0,
      y: 0,
      width: this.data.canvasWidth,
      height: this.data.canvasHeight,
      destWidth: this.data.canvasWidth * 2,//destWidth比width大点会更清晰,不要太大
      destHeight: this.data.canvasHeight * 2,//destHeight比height大点会更清晰,不要太大
      async success(res) {
        console.log('image::::', res);
        that.setData({
          img: res.tempFilePath  //将转化好的图片放到data中用来展示
        })
      },
      fail(err) {
        _showToast('图片转换失败');
      }
    });
  },

第三步:长按保存到相册

// 前两个函数用来实现长按,在页面中定义touchStart和touchEnd
  touchstart(e) {
    touchStart = e.timeStamp;
  },
  touchend(e) {
    touchEnd = e.timeStamp;
  },
  saveImg() {
    if (touchEnd - touchStart > 600) {
      // 想要保存到相册,需要开启授权
      tt.authorize({
        scope: 'scope.album',
        success: () => {
          tt.saveImageToPhotosAlbum({
            filePath: this.data.img, // 图片文件路径
            success: (res) => {
              _showToast('保存成功');
            },
            fail: res => {
              _showToast('保存失败');
            }
          });
        },
        fail: res => {
          _showToast('授权失败');
        }
      })
    }
  }

以上三步走就好了吖,再来看一眼html中是怎么写的吧。

提示一下思路(不要嫌啰嗦呀,小声bb):canvas是有大小限制的,并且canvas大一些会比较清晰,在展示的时候可能会导致展示不全。然后就想着展示一个图片岂不是很好,图片可以自己调整大小还方便操作,所以呢就把那个canvas藏起来,canvas不能隐藏所以只能利用定位把他移出屏幕了。

index.ttml
// 这里的canvas-id就是上面提到的id
<canvas canvas-id="myCanvas" class="canvas" style="width: {{canvasWidth}}px; height: {{canvasHeight}}px;position:absolute;top:-2000px"></canvas>
<image src="{{img}}" mode="" style="width: {{canvasWidth}}px; height: {{canvasHeight}}px;" bindtouchstart="touchstart" bindtouchend="touchend" bindtap="saveImg"/>

希望对大家有帮助呀,以上内容也有查自网络的东西。本人也是小白,哪里不对欢迎指正,告辞!