1. 程式人生 > 其它 >微信小程式-漸變頭像、聖誕帽頭像製作

微信小程式-漸變頭像、聖誕帽頭像製作

1、背景

今年國慶 漸變頭像著實火了一把,看到微信裡面的好友,很多都換上了新顏。

如上圖所示,一個漸變的頭像。

作為碼農,看到上面的效果,首先會想到這個是怎麼實現的?我可不可以?

於是就有了今天這篇文章,記錄一下自己動手做一個頭像製作小工具。

話不多說,上效果圖。

製作國慶頭像的效果圖:

操作步驟:

1、獲取頭像。

2、選擇熱門圖片。

3、儲存頭像。

製作聖誕頭像效果圖:

操作步驟:

1、獲取當前微信的使用者頭像。

2、選擇喜歡的聖誕帽、調整聖誕帽角度。

3、儲存圖片。

好了上面就是最終實現的效果,感興趣的小夥伴可以掃描下面的小程式二維碼進行體驗:

2、實現原理

先介紹下實現原理,可以看到我們最終生成的頭像是由兩部分組成。

微信頭像 + 選擇的聖誕帽或者國旗組成。

在小程式中要實現這種將兩張圖片或者多張圖片疊加到一起生成一張圖片,用到了

Canvas元件。

官方文件連結如下:

CanvasContext | 微信開放文件微信開發者平臺文件https://developers.weixin.qq.com/miniprogram/dev/api/canvas/CanvasContext.html在我們這裡主要用到了兩個API:

1)drawImage(imageResource, dx, dy, dWidth, dHeight)

將圖片繪製到畫布上

引數介紹:

string imageResource

所要繪製的圖片資源(網路圖片要通過 getImageInfo / downloadFile 先下載)

number dx

imageResource的左上角在目標 canvas 上 x 軸的位置

number dy

imageResource的左上角在目標 canvas 上 y 軸的位置

number dWidth

在目標畫布上繪製imageResource的寬度,允許對繪製的imageResource進行縮放

number dHeight

在目標畫布上繪製imageResource的高度,允許對繪製的imageResource進行縮放

2)draw(boolean reserve, function callback)

將之前在繪圖上下文中的描述(路徑、變形、樣式)畫到 canvas 中。

引數

boolean reserve

本次繪製是否接著上一次繪製。即 reserve 引數為 false,則在本次呼叫繪製之前 native 層會先清空畫布再繼續繪製;若 reserve 引數為 true,則保留當前畫布上的內容,本次呼叫 drawCanvas 繪製的內容覆蓋在上面,預設 false。

function callback

繪製完成後執行的回撥函式

掌握了上面的兩個api就可以實現將兩張圖片疊加到一起了。

3、程式碼實現

工程結構:

images: 存放圖片資源;

pages:主要的實現,guoqing 資料夾實現國慶頭像製作; chrismas 資料夾實現聖誕頭像製作;

1)國慶頭像製作程式碼

佈局檔案:guoqing.wxml

<!--pages/guoqing/guoqing.wxml-->
<!-- 畫布大小按需定製 這裡我按照背景圖的尺寸定的  -->
<view style="margin-top:60px;margin-bottom:40px">
  <image src="../../images/20190906-logo2.png" height="50px" class="header"></image>
</view>

<view class="hot-biz" style="width: 90%;margin: 0 auto;border-radius: 10px;margin-bottom:15px;"> 
  <view class="hot-top">
    <view class="tx">
      熱門
    </view>
  </view>

  <view class="hot-item-list">
    <scroll-view scroll-x>
      <view class="hot-biz-list" >
        <view class="item" wx:for="{{list}}" wx:key="id">
          <image bindtap='selectImg' data-id='{{item}}' data-src='../../images/hat{{item}}.png' src="../../images/hat{{item}}.png" mode='aspectFill'></image>
        </view>
      </view>
    </scroll-view>
  </view>
</view>

<view class="canvas-view">

<view style="width:150px;margin-left:20px;border: 2px solid #ffffff;">
  <canvas canvas-id="shareImg" style="width:150px;"></canvas>
</view>
  
  
<!-- 預覽區域  -->
<view class='canvas-view-right'>  
    <button bindtap="getUserProfile" class="btn1">獲取頭像</button>
    <button bindtap="save" class="btn1" disabled="{{!hasUserInfo}}">儲存頭像</button>
    <button open-type="share" bindtap='handleShare' class="btn1">分享好友</button>
</view>

</view>


樣式檔案: guoqing.wxss

/* pages/guoqing/guoqing.wxss */
page{
   background: #FF5651;
   display: flex;
   flex-direction: column;
   align-items: center;
   align-content: center;
  }
  
  .header{
    width: 315px!important;
    height: 125px!important;
  }

  .canvas-view{
    width: 100%;
    align-content: center;
    align-items: center;
    text-align: center;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
  }

  .canvas-view-right{
    display: flex;
    flex-direction: column;
    margin: 10px;
  }
  
  .btn1{
    background-color:#EB9A41;
    border-radius: 50px;
    color:#ffffff;
    width: 130px!important;
    height: 40px!important;
    font-size: 32rpx;
    height: 50rpx;
    display: flex;
    justify-content: center;
    margin-top: 10px;
  }
  
  /* list公共 */
  .hot-biz{
    margin-top: 10px;
    background: #fff;
  }
  .hot-biz .tx{
    font-size: 15px;
    margin-left: 10px;
    padding: 9px 0;
    font-weight: 700;
    color: #FF5651;
  }
  .hot-top{
    display: flex;
  }
  
  /* 熱門桌布 */
  .hot-item-list{
    margin: 0 auto;
    width: 94%;
    margin-bottom: 20px;
    align-items: center;
  }
  .hot-biz-list { 
    display: flex; 
    justify-content: space-between; 
    height: 100%;
    align-items: center;
    /* flex-wrap: wrap; */
  }
  .hot-biz-list .item { 
    width: 50px;  
    flex-direction: column; 
    align-items: center; 
    height: 50px;
    padding-right: 8px;
  }
  .hot-biz-list image { 
    width: 50px; 
    height: 50px;
    border-radius:5px;
    margin: 0 auto;
    display: block;
    border:1px solid rgb(235, 235, 245);
  }
  /* end */

邏輯檔案: guoqing.js

// pages/guoqing/guoqing.js
const ctx = wx.createCanvasContext('shareImg');
const app = getApp();

Page({

    /**
     * 頁面的初始資料
     */
    data: {
      prurl: '',
  
      defaultImg: 0,
  
      userInfo: {},
      hasUserInfo: false,
  
      list: [
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
      ]
    },
  
    selectImg: function(e){
      var current = e.target.dataset.id;
      console.log(current);
      this.setData({
        defaultImg: current,
        prurl: ''
      });
      console.log("this:",this.data.userInfo);
      if(this.data.userInfo.avatarUrl){
        this.drawImg(this.data.userInfo.avatarUrl);
      } else {
        this.initCanvas(this.data.defaultImg);
      }
    },
  
    // 初始化
    initCanvas(index){
      let that = this;
      //主要就是計算好各個圖文的位置
      let num = 150;
      // ctx.drawImage(res[0].path, 0, 0, num, num)
      ctx.drawImage(`../../images/hat${index}.png`, 0, 0, num, num)
      ctx.stroke()
      ctx.draw(false, () => {
        wx.canvasToTempFilePath({
          x: 0,
          y: 0,
          width: num,
          height: num,
          destWidth: 960,
          destHeight: 960,
          canvasId: 'shareImg',
          success: function(res) {
            that.setData({
              prurl: res.tempFilePath
            })
            // wx.hideLoading()
          },
          fail: function(res) {
            wx.hideLoading()
          }
        })
      })
    },
  
  
    // 推薦使用wx.getUserProfile獲取使用者資訊,開發者每次通過該介面獲取使用者個人資訊均需使用者確認
    // 開發者妥善保管使用者快速填寫的頭像暱稱,避免重複彈窗
    getUserProfile(e) {
      let that = this;
      if(!that.data.userInfo.avatarUrl){
        console.log('-- 1 --');
        wx.getUserProfile({
          desc: '僅用於生成頭像使用', // 宣告獲取使用者個人資訊後的用途,後續會展示在彈窗中,請謹慎填寫
          success: (res) => {
            //獲取高清使用者頭像
            var url = res.userInfo.avatarUrl;
            while (!isNaN(parseInt(url.substring(url.length - 1, url.length)))) {
              url = url.substring(0, url.length - 1)
            }
            url = url.substring(0, url.length - 1) + "/0";
            res.userInfo.avatarUrl = url;
            console.log(JSON.stringify(res.userInfo));
            that.setData({
              userInfo: res.userInfo,
              hasUserInfo: true
            })

            that.drawImg(res.userInfo.avatarUrl);
            app.globalData.userInfo = res.userInfo;
          }
        });
      }else if(that.data.userInfo.avatarUrl){
        console.log('-- 2 --');
        that.drawImg(that.data.userInfo.avatarUrl);
      }
  
    },
    
  
    drawImg(avatarUrl){
      let that = this;
      console.log("-- drawImg --");
      // `${that.data.userInfo.avatarUrl}`
      let promise1 = new Promise(function(resolve, reject) {
        wx.getImageInfo({
          src: avatarUrl,
          success: function(res) {
            console.log("promise1", res)
            resolve(res);
          }
        })
      });
      var index = that.data.defaultImg;
      // ../../images/head${index}.png
      // hat0.png  avg.jpg
      let promise2 = new Promise(function(resolve, reject) {
        wx.getImageInfo({
          src: `../../images/hat${index}.png`,
          success: function(res) {
            console.log(res)
            resolve(res);
          }
        })
      });
      Promise.all([
        promise1, promise2
      ]).then(res => {
        console.log("Promise.all", res)
        //主要就是計算好各個圖文的位置
        let num = 150;
        ctx.drawImage(res[0].path, 0, 0, num, num)
        ctx.drawImage('../../' + res[1].path, 0, 0, num, num)
        ctx.stroke()
        ctx.draw(false, () => {
          wx.canvasToTempFilePath({
            x: 0,
            y: 0,
            width: num,
            height: num,
            destWidth: 960,
            destHeight: 960,
            canvasId: 'shareImg',
            success: function(res) {
              that.setData({
                prurl: res.tempFilePath
              })
              // wx.hideLoading()
            },
            fail: function(res) {
              wx.hideLoading()
            }
          })
        })
      })
  
    },
  
    handleShare: function(){
      console.log('handleShare method');
      this.onShareAppMessage();
    },
  
    save: function() {
      var that = this;
      if(!that.data.prurl){
        wx.showToast({
          title: '請先生成專屬頭像',
        })
        return;
      }
      wx.saveImageToPhotosAlbum({
        filePath: that.data.prurl,
        success(res) {
          wx.showModal({
            content: '圖片已儲存到相簿!',
            showCancel: false,
            success: function(res) {
              if (res.confirm) {
                console.log('使用者點選確定');
              }
            }
          })
        }
      })
    },
  
  
  
    /**
     * 生命週期函式--監聽頁面載入
     */
    onLoad: function (options) {
      this.initCanvas(this.data.defaultImg);
    },
  
    /**
     * 使用者點選右上角分享
     */
    onShareAppMessage: function () {
      return {
        title: '領取你的國慶專屬頭像',
        success: function (res) {
          // 轉發成功
          console.log("轉發成功:" + JSON.stringify(res));
        },
        fail: function (res) {
          // 轉發失敗
          console.log("轉發失敗:" + JSON.stringify(res));
        }
      }
    },
  
    /**
     * 使用者點選右上角分享朋友圈
     */
    onShareTimeline(){
      
    }
  })

好了,國慶頭像製作的主要程式碼就這麼多,程式碼已經上傳到github上了,感興趣的小夥伴可以到github上檢視使用:

GitHub - YMAndroid/photoDemo

對下,下面是頭像製作小程式的二維碼,想製作聖誕頭像的小夥伴可以掃描體驗喲~