小程序|炎炎夏日、清爽一夏、头像大换装

1,407 阅读10分钟

我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛

写作背景:

     炎炎的夏日一起来为头像增加点夏天的元素,让清爽的头像陪伴你一整个夏天。通过一个完整的小程序串通整个开发生命周期。

需求设计:

     开发一款可以为头像增加夏日元素贴纸的小程序。支持直接使用用户当前头像信息,支持将制作的头像保存到用户相册。

首页模块设计:

     首页模块分为未授权和已授权使用用户信息两种状态,当用户刚进入页面未操作的情况下提供授权的操作按钮,当用户完成授权后展示头像制作的视图。

未授权使用头像状态功能概述:

  • “Get 新头像 清爽一夏~”按钮:用户点击后进行用户信息获取授权的操作。

成品图:

WechatIMG17.png

已授权使用头像状态功能概述:

  • 头像制作区域,用来显示授权的头像,并在这个区域完成贴纸的调整;
  • 贴纸区,用来展示小程序内置的夏日贴纸素材,并提供选取;
  • 按钮【保存下来】:将头像和贴纸保存至用户手机相册;
  • 按钮【重来一次】:将重置贴纸移动、选中、缩放的数据到初始值;

成品图:

WechatIMG16.png

关于页面设计:

  • 提供小程序及作者的相关信息展示

成品图:

WechatIMG18.png

注册微信小程序:

     微信小程序注册流程包括账号信息填写、邮箱激活、信息登记三个步骤,按网站提示注册即可。

Tips:

  1. 微信小程序注册的邮箱要求使用未在微信公众平台、开放平台、个人微信等绑定的全新邮箱,可以再去163注册一个使用。
  2. 信息登记主体的使用需要注意同一个身份证仅支持绑定5个小程序,同一手机号仅支持绑定5个小程序,主体类型选择个人即可。

快速开始小程序项目:

创建项目

     使用微信开发者工具创建一个不使用任意模板的项目并按照下面的目录约定进行少许调整,见注释的特别约定:

CoolAvatar
├─app.js	// 默认约定:app主函数
├─app.json	// 默认约定:窗口、页面、组件配置
├─app.wxss	// 默认约定:app全局样式定义文件
├─package.json	// 默认约定:使用npm安装依赖后自动生成更新
├─project.config.json	// 默认约定:配置npm依赖文件、文件夹、appid等
├─project.private.config.json
├─sitemap.json			
├─utils	        // 默认约定:开发过程中用到的工具函数
|   └─util.js
├─templates	// 特别约定:用来放置页面用到的模板
|     ├─authorization
|     |       ├─authorization.wxml
|     |       └─authorization.wxss
├─pages		// 默认约定:每个页面的位置
|   ├─index
|   |   ├─index.js
|   |   ├─index.json
|   |   ├─index.wxml
|   |   └─index.wxss
├─images	// 特别约定:项目基础图片素材
|   ├─icon-about-default.png
|   ├─icon-about-selected.png
|   ├─logo.png
|   ├─material	// 特别约定:贴纸素材
|   |    ├─1.png
|   |    └─2.png

选装vant-ui:

     根据自己的喜好和习惯可以自行选择,一个顺手的UI库对于快速开发还是很有必要的,安装、配置及组件使用支持参照vant-ui组件文档即可。

Tips:

  1. 需要更改 app.json 配置文件,避免默认组件造成的样式覆盖错乱问题;
  2. 需要更改 project.config.json 配置文件,以支持 npm 依赖的正确使用;

配置tabbar:

     配置tabbar,满足首页和关于页面的切换。通过微信开发者工具【右键】=>【新建 Page】创建about页面后将下面的配置添加到 app.json 完成底部标签栏的配置:

属性描述
tabbar.color未选中字体颜色
tabbar.selectedColor选中的字体颜色
tabbar.list配置每个标签的内容
tabbar.list.text标签文案
tabbar.list.pagePath页面的实际路径
tabbar.list.iconPath未选中的标签ICON
tabbar.list.selectedIconPath选中的标签ICON
{
  "tabBar": {
    "color": "#dbdbdb",
    "selectedColor": "#04cd95",
    "list": [
      {
        "selectedIconPath": "images/icon-home-selected.png",
        "iconPath": "images/icon-home-default.png",
        "pagePath": "pages/index/index",
        "text": "首页"
      },
      {
        "selectedIconPath": "images/icon-about-selected.png",
        "iconPath": "images/icon-about-default.png",
        "pagePath": "pages/about/about",
        "text": "关于"
      }
    ]
  }
}

配置完成后结果:

image.png

配置合法域名:

     因为我们存在读取用户头像并下载的操作,所以需要将微信头像的域名地址配置到downloadFile里面,开发过程中虽然可以在开发者工具设置不校验域名,但还是建议一开始就配置好:

页面开发:

     我们主要来实现首页中夏日新头像的制作,关于页面仅展示静态文案就不过多讲述了,完整代码在文末提供。

模板使用:

     微信小程序在开发过程中提供了模块的概念,使得我们可以将一个视图文件中的”一块“提取到单独的文件中,既能减少原视图文件中的代码量又能在可能存在复用的情况下进行复用,节省开发工作。

定义模板:

  1. 使用 template 元素包整个视图内容;
  2. 使用 name 命名模板的名称;
  3. 模板插值和事件定义同视图开发;
<template name="authorization">
  <view class="get-user-info">
    <van-image width="120" height="120" fit="cover" round src="url" />
    <van-button color="#04cd95" plain wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile" block>Get 新头像 清爽一夏~</van-button>
  </view>
</template>

引用模板:

  1. 在目标视图文件顶部导入模板;
  2. 再次使用 template 元素并使用 is 属性来明确渲染的模板名称;
  3. 在模板中使用到的插值数据通过 data 属性传递;
  4. 在模板中还定义了待触发的事件,我们就直接在目标视图的 js 文件中定义吧;
<import src="../../templates/authorization/authorization.wxml" />

<template is="authorization" data="{{canIUseGetUserProfile}}" />

视图内容:

我们通过使用模板将各版块拆分到 templates 目录:

  1. 授权模板;
  2. 头像制作区模板;
  3. 操作栏区模板;
  4. 贴纸区模板;
<!-- 导入模板 -->
<import src="../../templates/switch-rect/switch-rect.wxml" />
<import src="../../templates/operating-area/operating-area.wxml" />
<import src="../../templates/material-area/material-area.wxml" />
<import src="../../templates/authorization/authorization.wxml" />

<!-- 未授权提示 -->
<van-dialog message="亲,必要的权限您需要开启哦~" show="{{ isOpenSetting }}" confirm-button-text="开启" bind:confirm="handlerOpenSetting">
</van-dialog>

<!-- 合成头像画布 -->
<canvas class="userinfo-canvas" canvas-id="festivalCanvas"></canvas>

<!-- 内容主体 -->
<view class="userinfo">
  <block wx:if="{{!hasUserInfo}}">
    <!-- 授权区 -->
    <template is="authorization" data="{{canIUseGetUserProfile}}" />
  </block>
  <view wx:else style="text-align: center;">
    <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}"></image>
    <!-- 拖动贴纸 -->
    <template is="switchRect" data="{{rotate,festivalSize,festivalLeft,festivalTop,festivalSrc,festivalScale}}" />
    <!-- 操作区 -->
    <template is="operatingArea" />
    <!-- 贴纸区 -->
    <template is="materialArea" data="{{material}}" />
  </view>
</view>

获取微信用户头像:

涉及 API:wx.getUserProfile(Object object)

     用户主动触发定义的 getUserProfile 函数来打开小程序内置的授权提示窗口,在用户同意后成功拿到用户的 UserInfo 对象,从中解析到用户的头像信息,我们通过 hasUserInfo 变量的状态来控制未授权和已授权页面状态的显示。

getUserProfile(e) {
  wx.getUserProfile({
    desc: '展示用户头像',
    success: (res) => {
      const {
        material
      } = this.data;
      this.setData({
        userInfo: res.userInfo,
        hasUserInfo: res.userInfo != null,
        festivalSrc: material[0].url,
      });
    }
  })
}

贴纸移动、旋转、缩放:

     在 switch-rect.wxml 模板中定义了贴纸操作的容器,并在容器中显示当前选中的贴纸素材,将容器设置固定定位后通过实时改变 css 的选中角度、宽高及边距的数据来完成。

     容器事件的绑定包含了catchtouchstart、catchtouchmove、catchtouchend,右下角的选中按钮同样绑定了这三个属性。catch在这里表示会阻止事件向上冒泡事件的具体处理可以在文末的提供的源码地址查看。

保存新头像:

检查用户是否同意使用相册:

涉及 API:

  1. wx.getSetting(Object object)
  2. wx.openSetting(Object object)
  3. wx.authorize(Object object)

     使用 wx.getSetting 可以得到当前用户设置列表的权限状态,通过查看指定状态是否已授权来决定能否继续执行下一步,否则我们配置来提示框口来引导用户使用 wx.openSetting 跳转到设置页面手动操作授权。

wx.getSetting({
  success(res) {
    if (!res.authSetting[scope]) {
      wx.authorize({
        scope,
        success() {
          callback && callback(true);
        },
        fail() {
          self.setData({
            isOpenSetting: true,
          });
        }
      })
    } else {
      callback && callback(true);
    }
  },
  fail(err) {
    console.log(err)
  }
})

使用canvas绘制并输出新头像:

涉及 API:wx.saveImageToPhotosAlbum

     通过调用封装的 saveImage 函数,传入 canvasid 和尺寸数据可以得到合成后的图像信息。再调用 wx.saveImageToPhotosAlbum 将图像信息保存到用户的手机相册,至此首页功能的 MVP 版本就完结了~

saveImage(this.data, res.tempFilePath, {
  canvasid: 'festivalCanvas',
  width: 700,
  height: 700,
  offsetTop: 0,
  offsetLeft: 0,
}, (img) => {
  wx.hideLoading();
  if (img) {
    this.saveImageToPhotosAlbum(img)
  }
});

代码质量:

     在微信开发者工具中有一个代码质量检查的功能,我们可以通过提示的处理方案进行操作,我们这个小程序没有使用CDN来放贴纸素材,所以图片资源超出了默认的200k的限制,有条件的时候可以优化一下~。

上线发布~:

推送代码:

这里的推送代码指的是通过微信开发者工具将编写的代码打包上传到小程序平台做发布,并非代码管理~。

  1. 填写版本号;
  2. 填写版本描述;

提交审核:

推送代码后就可以在这里查看到了,核实没有问题就提交审核后耐心等待吧。

提交发布:

等审核通过后就可以再次来到小程序平台执行提交发布的操作了,发布以后可以先用过扫描进入正式版小程序,稍后才能在微信搜索到。

源码分享:

  1. 本文源码:CoolAvatar;
  2. 参考项目:whc_wechat_image_edit

搬运说明:

  1. 使用参考项目的贴纸容器缩放的实现,完成贴纸容器的坐标角度的处理;
  2. 使用参考项目的 canvas 导出图像工具类实现贴纸素材和用户头像的融合;

问题集锦:

样式覆盖问题

Q: 在使用 van-button 的时候发现通过在 app.wxss 中使用重新编写 css 类无法正常覆盖样式;

A:在 .van-button 前增加 view 标识可以完成,如下;

view .van-button {
  height: 75rpx;
}

自动执行授权函数?

Q:尝试在 onload 后直接调用 wx.getUserProfile 函数来减少操作,但没有正常弹出窗口?

A:页面设计未授权状态,让用户主动触发才能正常弹窗。

用户头像清晰度

Q:在获取到用户的头像信息后发现头像特别模糊,完全无法进行新头像的成?

A:UserInfo在说明 avatarUrl 提到了地址最后一段的含义,0、46、64、96、132 数值可选,0 代表 640x640 的正方形头像,46 表示 46x46 的正方形头像,剩余数值以此类推。默认132。所以我们需要将默认的 132 替换成 0 得到最高清的一张头像。

const index = res.userInfo.avatarUrl.lastIndexOf('/132');
if (index != -1) {
  res.userInfo.avatarUrl = res.userInfo.avatarUrl.substring(0, index) + '/0';
}

为什么不支持相册选择

Q:应开发用户自行拍照或上传相册中的图片来制作头像,为啥没有这个功能?

A:目前微信小程序提供的 wx.editImage(Object object)不支持按比例裁剪,这样的功能居然是准备酌情增加,非 1:1 的头像在选择后的效果太差。上面提到的参考项目中有关于等比例裁剪的实现,感兴趣的小伙伴可以尝试。

本地图像和授权拿到的用户头像区别

Q:在开发中相册选择的图像和授权得到的用户头像有什么区别,可以直接使用么?

A:我们在画布中操作的图片都是图像本身,授权得到的用户头像仅是图像的地址,所以我们需要使用 wx.downloadFile(Object object) 函数将头像资源下载后使用。

使用模板后样式为生效

Q:我也使用的项目来整合视图,可以样式为啥没有生效呢?

A:在微信小程序的开发文档有关于模板的使用但未提供样式的处理,我们需要在目标视图的 wxss 文件中使用 @import 导入模板的样式。

结语:

     第一次完整的小程序开发到上线就到此结束了,可以在微信搜索”夏日贴纸头像“来为自己的头像做一次换装吧。

     在体验了原生的小程序开发和跨小程序平台的开发模式后感觉还是原生的体验要更好,跨平台的支持在中间层转换的稳定性还有待提升,要不也不至于一个白屏的问题是通重新CV了一遍就搞定的吧~