草庐IT

微信小程序开发--个性化头像生成(国庆渐变头像、圣诞帽头像)

startTry 2023-03-28 原文

 1、背景

今年国庆 渐变头像着实火了一把,看到微信里面的好友,很多都换上了新颜。

 如上图所示,一个渐变的头像。

作为码农,看到上面的效果,首先会想到这个是怎么实现的?我可不可以?

于是就有了今天这篇文章,记录一下自己动手做一个头像制作小工具。

话不多说,上效果图。

制作国庆头像的效果图:

操作步骤:

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​


4、体验二维码

对下,下面是头像制作小程序的二维码,想制作圣诞头像的小伙伴可以扫描体验哟~

码字不易,如果对你有所帮助,不要忘记点个赞哟~


有关微信小程序开发--个性化头像生成(国庆渐变头像、圣诞帽头像)的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  3. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  4. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  5. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  6. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  7. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  8. 微信小程序通过字典表匹配对应数据 - 2

    前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立

  9. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  10. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

随机推荐