如何实现一个带壳截图小程序并发布上线?

1、什么是带壳截图

我们在日常工作和生活中,往往需要截图去做汇报或分享。

普通人的做法:手机或电脑截图,然后就拿去汇报或分享。这样你的截图显得平平无奇,没有给人眼前一亮的感觉。

但是,如果我们给这张截图穿上一件“衣服”,把它打扮一下,让她鹤立鸡群,与众不同,那么就会给人留下深刻的印象!!

无设备边框 带设备边框
无边框 带边框

2、为什么要自己实现一个带壳截图

目前市面上有很多带壳截图小程序,但是他们有很多无法满足我要求的一些痛点;

痛点1:移动端带壳截图的App,高级功能需要收费;

痛点2:免费的带壳截图小程序,要么设备机型不全,要么保存的截图不高清;

痛点3:设备机型齐全的网站,设置过于复杂,而且需要电脑访问,操作不便捷;

所以,作为程序员,我想自己实现一个免费、设备机型齐全、在手机端可随时访问截图的小程序;

3、如何实现

带壳截图有两个要素,一个是设备边框,一个是上传的截图。

3.1 设备边框素材

对于苹果系列产品,其产品边框资源,可在网上找到,该资源必须为png格式的,例如:

MacBook_Air_5th_13_M1_Gold.png
得到产品边框资源后,我们需要通过PS得到其宽(width)、高(height)参数,显示器的左上角坐标信息(startX, startY)和右下角坐标信息(endX, endY),通过官网可得知每一种设备的颜色信息;

将这些信息抽象化后,得到每一个机型设备的具体信息:

机型信息:

const macs = [
  {
    "id": "MackBook_Air",
    "name": "第五代 Air",
    "products": [
      {
        "id": "MacBook_Air_5th_13_M1",
        "name":"13寸 M1",
        "colorsId": "Air_5th_13_M1",
        "w": 3310,
        "h": 1897,
        "sx": 375,
        "sy": 123,
        "ex": 2935,
        "ey": 1723
      },
      {
        "id": "MacBook_Air_5th_13_M2",
        "name":"13寸 M2",
        "colorsId": "Air_5th_13_M2",
        "w": 3220,
        "h": 2100,
        "sx": 329,
        "sy": 218,
        "ex": 2889,
        "ey": 1882
      }
    ]
  }
];

颜色信息:

const colors = {
  "Air_5th_13_M1": [
    {
      "id": "Gold",
      "value": "#f4e8ce"
    },
    {
      "id": "Silver",
      "value": "#edeee7"
    },
    {
      "id": "Space_Grey",
      "value": "#424242"
    },
  ],
  "Air_5th_13_M2": [
    {
      "id": "Midnight",
      "value": "#31353b"
    }
  ]
};

3.2 小程序实现

将页面分为两个区域,上面为截图预览展示区,下面为操作区,具体如下所示:

image.png

其中,截图预览展示区采用canvas承载,操作区采用普通wxml承载;

截图预览区之所以采用canvas承载,原因是canvas展示图片不会出现抖动的效果,而使用image标签,在加载、展示图片的过程中会出现抖动的效果,影响体验;

使用canvas绘制需要注意:为了绘制出高清的图像,必须得对canvas的尺寸进行相应的缩放:

const query = wx.createSelectorQuery();
query.select('#myCanvas')
  .fields({ node: true, size: true })
  .exec((res) => {
    const canvas = res[0].node;
    const ctx = canvas.getContext('2d');
    const dpr = wx.getSystemInfoSync().pixelRatio;
    // 重点部分
    canvas.width = res[0].width * dpr;
    canvas.height = res[0].height * dpr;
    ctx.scale(dpr, dpr);
  })
}

3.3 截图预览绘制

按照图层的顺序,先后绘制Logo、文本、产品 边框图;

3.3.1 Logo 绘制

根据每个设备屏幕的左上角(sx,sy)、右下角(ex,ey)的坐标信息,以及Logo的宽高信息,可以得到绘制Logo的(x, y, width, height)参数,然后进行绘制:

// Logo 绘制
ctx.drawImage(img, x, y, width, height);

3.3.2 文案绘制

根据每个设备屏幕的左上角(sx,sy)、右下角(ex,ey)的坐标信息,以及素材的素材的缩放比例,可以得到绘制文案的参数,然后进行绘制:

const { sx, sy, ex, ey } = currentProduct;
const xPixel = ex - sx;
const yPixel = ey - sy;

const x = sx * scale;
const y = sy * scale;

const width = xPixel * scale;
const height = yPixel * scale;

ctx.fillText(`设备分辨率: ${xPixel} x ${yPixel} px`, x + width / 2, y + height - 10);

3.3.3 上传截图绘制

根据每个设备屏幕的左上角(sx,sy)、右下角(ex,ey)的坐标信息,以及素材的素材的缩放比例,可以得到绘制截图的参数,然后进行绘制:

const { sx, sy, ex, ey } = currentProduct;

const x = sx * scale;
const y = sy * scale;

const width = (ex - sx) * scale;
const height = (ey - sy) * scale;

ctx.drawImage(img, x, y, width, height);

3.3.4 产品边框绘制

对于,任意的产品边框素材,有的素材高度 > 宽度,有的素材宽度 > 高度;

为了让素材边框很好适配展示区域的尺寸,需要根据上面两种情况进行区分,然后分别设置;

当素材高度 > 宽度时,设置素材的高度和canvas的高度相等,宽度进行同比例缩放,裁剪方式与 object-fit: contian 一致;

const { w, h } = currentProduct;
const imgScale = canvasHeight / h;
const deviceHeight = canvasHeight;
const deviceWidth = imgScale * w;
const startX = (canvasWidth - deviceWidth) / 2;
const startY = (canvasHeight - deviceHeight);
context.drawImage(image, startX, startY, deviceWidth, deviceHeight);

当素材宽度 > 高度时,设置素材的宽度和canvas的宽度相等,高度进行 比例缩放,裁剪方式与 object-fit: contian 一致;

const { w, h } = currentProduct;
const imgScale = canvasWidth / w;
const deviceWidth = canvasWidth;
const deviceHeight = imgScale * h;
const startX = (canvasWidth - deviceWidth) / 2;
const startY = (canvasHeight - deviceHeight) / 2;
context.drawImage(image, startX, startY, deviceWidth, deviceHeight);

3.4 组件化封装

将展示区与操作区封装成一个组件,组件所需的数据从外部传入,iPhone、iPad、MacBook、iWatch数据格式保持一致;

组件化与产品边框素材的抽象数据结合,即可实现一次开发,多处复用;之后不管是需要新增设备类型非常方便,还是同一类型下新增新的产品,只要变更数据即可现实简单高效;

4个Tab页引入该组件,然后给组件传入对应的设备数据和颜色数据即可渲染出展示区与操作区:

<device-display-page
  devices="{{devices}}"
  colors="{{colors}}"
></device-display-page>
iPhone iPad MacBook iWatch
iPhone系列 iPad系列 MacBook系列 iWatch系列

3.5 离线 canvas 保存截图

在开始时,虽然对canvas进行了宽高设置以及缩放设置,但是小程序中对素材缩小尺寸后,再在使用canvas绘制绘,会导致图像失真,看起来有模糊的情况。

为了得到高清截图,我们采用离线canvas绘制素材,canvas的宽高尺寸与边框产品的宽高尺寸保持一致,不进行缩放处理,然后将canvas转换为图片,再保存到相册中;

const { w, h } = currentProduct;
// 创建离屏 2D canvas 实例
const canvas = wx.createOffscreenCanvas({type: '2d', width: w, height: h });
// 获取 context。注意这里必须要与创建时的 type 一致
const context = canvas.getContext('2d');

// 绘制部分代码省略,可参考3.3.3和3.3.4

// 获取画完后的数据
const base64 = context.canvas.toDataURL('image/png');
const formatedBase64 = base64.replace('data:image/png;base64,', '')
      .replace(/[\r\n]/g, '')
const shotScreenPath = `${wx.env.USER_DATA_PATH}/shotScreen.png`;
fs.writeFile({
    filePath: shotScreenPath,
    data: formatedBase64,
    encoding: 'base64',
    success: (res) => {
      wx.saveImageToPhotosAlbum({
        filePath: shotScreenPath,
        success(res) { 
          wx.showToast({
            title: '边框保存成功',
          })
        },
        complete() {
          wx.hideLoading()
        }
      });
    },
    fail: (err) => {
      console.log('err:', err);
    }
})

4、小程序部署

相对于H5,小程序部署非常简单,只要将代码上传,然后提审就可以,提审的前提是你已经注册了小程序号。

5、总结

  • 在网上找到产品边框素材资源;
  • 获得素材资源的尺寸与宽高信息,并抽象化;
  • 页面区域划分并抽象组件化,便于不同Tab页面复用;
  • 展示区域采用常规canvas,将素材进行尺寸缩放,便于展示;
  • 保存截图采用离线canvas,不进行缩放,canvas尺寸等于产品边框素材尺寸,提高截图分辨率;
© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容