蹭一波国庆热度,国庆头像小程序搞起来,这里是使用uniapp开发的小程序,自己封装一个组件,一天时间了解以及上手实现。
先说遇到的问题吧~~
-
实现的思路是什么?
实现的思路就是使用canvas画布去生成图片,渲染效果图,获取微信头像,将素材添加到头像上面,使用uniapp的api去保存图片到本地\
-
使用什么技术实现?
canvas+uniapp+wx.downloadFile\
-
canvas的api怎么使用?
将获取到图片以及素材放到canvas里面\
this.ctx.drawImage(this.avatarUrl, 0, 0, this.canvasSide, this.canvasSide)
this.ctx.drawImage(img, 0, 0, this.canvasSide,
this.canvasSide)this.ctx.draw()
4.小程序上线后,服务器的图片怎么解决渲染问题?
由于素材太大,导致小程序无法上传,所以借助服务器,由接口获取素材数据,遇到的问题是canvas需要将其下载到本地之后才能渲染,由于本地编译的时候开启不校验合法域名,所以一切正常,但是上线体验服真机测试的时候发现,canvas无法渲染,将其不勾选也发现有同样的问题,一开始的想法是将其图片的域名加上就可以了,然后发现并没有效果,找了一段时间发现将其不校验取消即可报错,这个时候将其漏掉的域名加上就可以了\
5.canvas渲染的图片模糊怎么办呢?
这个问题目前还在解决中,思路是将其画布放大,是因为获取图片的时候像素太低导致图片模糊,具体的解决方案,后续跟进解决,后面会加入uniapp截取图片尺寸上传,\
话不多说,上代码吧,\
<template>
<view class="wrapper" :style="{height: windowHeight + 'px'}">
<view class="title">
<text class="animation" :key="index" :style="'--t:' + index" v-for="(word,index) in title">{{ word }}</text>
</view>
<view class="box">
<view class="img-wrap">
<canvas class="canvas" canvas-id="canvas"></canvas>
</view>
<view class="save-btn">
<span class="btn" type="default" @tap="getUserInfo">获取头像</span>
<span class="btn" type="default" @tap="upload">上传图片</span>
<span class="btn" type="default" @tap="save">保存头像</span>
</view>
</view>
<view class="select">
<view class="select-text">
<span :class="{select_textActive: item.id == index}" v-for="item in imgList"
@tap="handleCategore(item.id)">{{item.name}}</span>
</view>
<view class="select-list">
<view class="select-pic" :class="{select_active: id == index}" v-for="(source,index) in sources"
:key="index" @tap="handle(source.src,index)">
<image class="avatar" :src="avatarUrl"></image>
<image class="avatar" :src="source.src"></image>
</view>
</view>
</view>
<view class="preview-wrap" :style="{display: newAvatar ? 'block' : 'none'}" @tap="newAvatar = ''">
<view class="preview" @tap.stop="(()=>{return false})">
<view class="new-avatar">
<image :src="newAvatar"></image>
</view>
<view class="hint">长按头像保存图片</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
imgList: {
type: Array,
required: true
},
title: {
type: String
},
},
data() {
return {
avatarUrl: '',
target: '',
windowHeight: 0,
//画笔
ctx: null,
canvasSide: 0,
id: 0,
// #ifdef H5
newAvatar: '',
// #endif
canvas: '',
index: 1
}
},
computed: {
sources(picList = []) {
picList = this.imgList.find(item => {
console.log(item);
return item.id == this.index
}).picList
return picList.map((item, index) => {
return {
id: index,
src: item
}
})
}
},
created() {
uni.getSystemInfo({
success: (res) => {
this.windowHeight = res.windowHeight;
}
})
},
mounted() {
uni.createSelectorQuery().in(this).select('.canvas').boundingClientRect(data => {
console.log(data.width,'chushihua ')
this.canvasSide = data.width;
this.ctx = uni.createCanvasContext('canvas', this);
// #ifdef MP
uni.getUserInfo({
provider: 'weixin',
success: (res) => {
uni.getImageInfo({
src: res.userInfo.avatarUrl,
success: (image) => {
var that = this
wx.downloadFile({ //下载
url: that.sources[0].src, //服务器上的pdf地址
success: function(res1) {
console.log('初始化', res1)
//将其下载后赋值给canvas
that.avatarUrl = image.path
// 绘制canvas
that.target = res1.tempFilePath
that.ctx = uni.createCanvasContext(
'canvas', that);
that.drawImage(that.target)
}
})
}
})
}
})
// #endif
}).exec();
},
methods: {
handle(src, index) {
// this.target = src;
this.id = index;
this.drawImage(src)
this.downloadFile(src)
console.log(this.target, '切换')
},
handleCategore(id) {
this.index = id
let src = ''
if (this.id > this.sources.length) {
//如果切换的分类时,index>数组的长度就默认选最后一张图片
this.id = this.sources.length - 1
src = this.sources[this.id].src
} else {
src = this.sources.find(item => {
//切换分类时实时响应
return item.id == this.id
}).src
}
this.downloadFile(src)
},
downloadFile(src){
var that = this
wx.downloadFile({ //下载
url: src, //服务器上的pdf地址
success: function(res1) {
console.log('初始化', res1)
//将其下载后赋值给canvas
that.target = res1.tempFilePath
that.ctx = uni.createCanvasContext(
'canvas', that);
that.drawImage(that.target)
}
})
},
drawImage(img) {
this.ctx.drawImage(this.avatarUrl, 0, 0, this.canvasSide, this.canvasSide)
this.ctx.drawImage(img, 0, 0, this.canvasSide, this.canvasSide)
this.ctx.draw()
},
save() {
uni.canvasToTempFilePath({
width: this.canvasSide,
height: this.canvasSide,
canvasId: 'canvas',
success: (res) => {
// #ifndef H5
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: function() {
uni.showToast({
title: '保存成功'
});
},
fail: (err) => {
console.log(err)
uni.showToast({
title: '保存失败'
});
}
});
// #endif
// #ifdef H5
this.newAvatar = res.tempFilePath;
// #endif
}
}, this)
},
upload() {
uni.chooseImage({
count: 1,
success: (res) => {
this.avatarUrl = res.tempFilePaths[0];
this.ctx = uni.createCanvasContext('canvas', this);
this.drawImage(this.target)
}
})
},
getUserInfo() {
uni.showModal({
title: '温馨提示',
content: '亲,授权微信登录后才能正常使用小程序功能',
success: (res) => {
if (res.confirm) {
uni.getUserProfile({
desc: "获取个人信息",
success: (res) => {
this.userList = res.userInfo;
var that = this
wx.downloadFile({ //下载
url: res.userInfo.avatarUrl, //服务器上的pdf地址
success: function(res1) {
//将其下载后赋值给canvas
that.avatarUrl = res1.tempFilePath
// 绘制canvas
that.ctx = uni.createCanvasContext(
'canvas', that);
that.drawImage(that.target)
}
})
}
})
}
}
})
},
},
}
</script>
<style>
.wrapper {
display: flex;
flex-direction: column;
background-image: linear-gradient(#50D0BC, #35b8e9);
padding: 20rpx;
}
.box {
display: flex;
justify-content: center;
margin: 100rpx 0;
}
.title {
font-size: 40rpx;
margin: 30px 0;
color: #d90003;
text-align: center;
}
.img-wrap {
width: 150px;
height: 150px;
margin-right: 60rpx;
}
.canvas {
width: 150px;
height: 150px;
}
.avatar {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.select {
display: flex;
flex-direction: column;
padding: 40rpx;
background-color: #fff;
border-radius: 16rpx;
}
.select-text span {
padding: 0 10px 10px 10px;
margin-bottom: 10px;
}
.select_textActive {
border-bottom: 1px solid #35d4d0;
}
.select-list,
.select-text {
display: flex;
overflow-x: auto;
}
.select-pic {
flex-shrink: 0;
position: relative;
width: 140rpx;
height: 140rpx;
border: 4rpx solid transparent;
}
.select_active {
border-color: #35d4d0;
}
.save-btn {
color: #ffffff;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.btn {
background-color: #35d4d0;
margin: 0 20rpx;
padding: 20rpx;
border-radius: 20px;
}
.preview-wrap {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .8);
}
.preview {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
width: 80%;
height: 60%;
border-radius: 12px;
background-color: #fff;
}
.new-avatar {
width: 500rpx;
height: 500rpx;
margin: 40rpx auto 0;
border: 1px solid #b3e6e6;
border-radius: 4px;
overflow: hidden;
}
.new-avatar image {
width: 100%;
height: 100%;
}
.hint {
font-size: 40rpx;
text-align: center;
margin: 30rpx auto 0;
}
.animation {
animation: tiaodong 0.75s cubic-bezier(0.05, 0, 0.2, 1) infinite alternate;
display: inline-block;
transform: translate3d(0, 0, 0);
animation-delay: calc(0.1s * var(--t));
animation-play-state: running;
}
@keyframes tiaodong {
0%,
40%,
100% {
transform: translate3d(0, 0, 0);
}
20% {
transform: translate3d(0, -10px, 0);
}
}
</style>
使用方法\
### 微信国庆修改头像
使用方法
//直接引入
<avatar :imgList="sources"></avatar>
| 参数 | 类型 | 是否必填 |
| ---- | ---- | ---- |
| imgList | Array | 必填 |
| title | String | 选填 |
数据格式
sources: [{
id: 1,
name: '热门',
picList: [
'图片路径g',
'图片路径',
'图片路径',
'图片路径'
]
},
{
id: 2,
name: '国庆节',
picList: [
'图片路径',
'图片路径',
'图片路径',
]
}
],
title: '领取您的专属头像'
实现的效果\
源码已同步uniapp插件市场\
ext.dcloud.net.cn/plugin?id=9…