257 lines
6.0 KiB
JavaScript
257 lines
6.0 KiB
JavaScript
|
|
// 头像组件逻辑
|
|||
|
|
Component({
|
|||
|
|
properties: {
|
|||
|
|
// 头像URL
|
|||
|
|
avatarUrl: {
|
|||
|
|
type: String,
|
|||
|
|
value: ''
|
|||
|
|
},
|
|||
|
|
// 头像尺寸(rpx)
|
|||
|
|
size: {
|
|||
|
|
type: Number,
|
|||
|
|
value: 120
|
|||
|
|
},
|
|||
|
|
// 占位符文本
|
|||
|
|
placeholderText: {
|
|||
|
|
type: String,
|
|||
|
|
value: '👤'
|
|||
|
|
},
|
|||
|
|
// 占位符文本大小
|
|||
|
|
textSize: {
|
|||
|
|
type: Number,
|
|||
|
|
value: 40
|
|||
|
|
},
|
|||
|
|
// 是否可编辑
|
|||
|
|
editable: {
|
|||
|
|
type: Boolean,
|
|||
|
|
value: false
|
|||
|
|
},
|
|||
|
|
// 头像形状:circle(圆形)或 square(方形)
|
|||
|
|
shape: {
|
|||
|
|
type: String,
|
|||
|
|
value: 'circle'
|
|||
|
|
},
|
|||
|
|
// 是否显示边框
|
|||
|
|
bordered: {
|
|||
|
|
type: Boolean,
|
|||
|
|
value: false
|
|||
|
|
},
|
|||
|
|
// 是否显示阴影
|
|||
|
|
shadow: {
|
|||
|
|
type: Boolean,
|
|||
|
|
value: false
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
data: {
|
|||
|
|
showAvatarModal: false // 是否显示头像选择弹窗
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
methods: {
|
|||
|
|
// 头像点击事件
|
|||
|
|
onAvatarTap() {
|
|||
|
|
if (this.properties.editable) {
|
|||
|
|
this.showAvatarModal();
|
|||
|
|
}
|
|||
|
|
this.triggerEvent('tap', {
|
|||
|
|
avatarUrl: this.properties.avatarUrl
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 显示头像选择弹窗
|
|||
|
|
showAvatarModal() {
|
|||
|
|
this.setData({
|
|||
|
|
showAvatarModal: true
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 隐藏头像选择弹窗
|
|||
|
|
hideAvatarModal() {
|
|||
|
|
this.setData({
|
|||
|
|
showAvatarModal: false
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 拍照
|
|||
|
|
takePhoto() {
|
|||
|
|
this.hideAvatarModal();
|
|||
|
|
|
|||
|
|
wx.chooseMedia({
|
|||
|
|
count: 1,
|
|||
|
|
mediaType: ['image'],
|
|||
|
|
sourceType: ['camera'],
|
|||
|
|
camera: 'front',
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.tempFiles && res.tempFiles.length > 0) {
|
|||
|
|
this.processImage(res.tempFiles[0].tempFilePath);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('拍照失败:', err);
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '拍照失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 从相册选择
|
|||
|
|
chooseImage() {
|
|||
|
|
this.hideAvatarModal();
|
|||
|
|
|
|||
|
|
wx.chooseMedia({
|
|||
|
|
count: 1,
|
|||
|
|
mediaType: ['image'],
|
|||
|
|
sourceType: ['album'],
|
|||
|
|
success: (res) => {
|
|||
|
|
if (res.tempFiles && res.tempFiles.length > 0) {
|
|||
|
|
this.processImage(res.tempFiles[0].tempFilePath);
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('选择图片失败:', err);
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '选择图片失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 使用默认头像
|
|||
|
|
useDefaultAvatar() {
|
|||
|
|
this.hideAvatarModal();
|
|||
|
|
|
|||
|
|
// 触发默认头像选择事件
|
|||
|
|
this.triggerEvent('change', {
|
|||
|
|
avatarUrl: '',
|
|||
|
|
isDefault: true
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 处理图片
|
|||
|
|
processImage(tempFilePath) {
|
|||
|
|
wx.showLoading({
|
|||
|
|
title: '处理中...'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 压缩图片
|
|||
|
|
wx.compressImage({
|
|||
|
|
src: tempFilePath,
|
|||
|
|
quality: 80,
|
|||
|
|
success: (res) => {
|
|||
|
|
// 裁剪图片为正方形
|
|||
|
|
this.cropImage(res.tempFilePath);
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('图片压缩失败:', err);
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '图片处理失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 裁剪图片
|
|||
|
|
cropImage(tempFilePath) {
|
|||
|
|
// 获取图片信息
|
|||
|
|
wx.getImageInfo({
|
|||
|
|
src: tempFilePath,
|
|||
|
|
success: (infoRes) => {
|
|||
|
|
const { width, height } = infoRes;
|
|||
|
|
const size = Math.min(width, height);
|
|||
|
|
const x = (width - size) / 2;
|
|||
|
|
const y = (height - size) / 2;
|
|||
|
|
|
|||
|
|
// 创建画布进行裁剪
|
|||
|
|
const ctx = wx.createCanvasContext('avatar-canvas');
|
|||
|
|
|
|||
|
|
// 绘制圆形裁剪区域
|
|||
|
|
ctx.save();
|
|||
|
|
ctx.beginPath();
|
|||
|
|
ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
|
|||
|
|
ctx.clip();
|
|||
|
|
|
|||
|
|
// 绘制图片
|
|||
|
|
ctx.drawImage(tempFilePath, x, y, size, size, 0, 0, size, size);
|
|||
|
|
ctx.restore();
|
|||
|
|
|
|||
|
|
// 将画布内容导出为图片
|
|||
|
|
ctx.draw(false, () => {
|
|||
|
|
wx.canvasToTempFilePath({
|
|||
|
|
canvasId: 'avatar-canvas',
|
|||
|
|
success: (canvasRes) => {
|
|||
|
|
wx.hideLoading();
|
|||
|
|
|
|||
|
|
// 触发头像改变事件
|
|||
|
|
this.triggerEvent('change', {
|
|||
|
|
avatarUrl: canvasRes.tempFilePath,
|
|||
|
|
isDefault: false
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('画布导出失败:', err);
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '头像处理失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
console.error('获取图片信息失败:', err);
|
|||
|
|
wx.hideLoading();
|
|||
|
|
wx.showToast({
|
|||
|
|
title: '图片处理失败',
|
|||
|
|
icon: 'none'
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
// 生成头像缩略图
|
|||
|
|
generateThumbnail(avatarUrl, size = 60) {
|
|||
|
|
return new Promise((resolve, reject) => {
|
|||
|
|
if (!avatarUrl) {
|
|||
|
|
resolve('');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wx.getImageInfo({
|
|||
|
|
src: avatarUrl,
|
|||
|
|
success: (infoRes) => {
|
|||
|
|
const ctx = wx.createCanvasContext('thumbnail-canvas');
|
|||
|
|
|
|||
|
|
// 绘制圆形缩略图
|
|||
|
|
ctx.save();
|
|||
|
|
ctx.beginPath();
|
|||
|
|
ctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
|
|||
|
|
ctx.clip();
|
|||
|
|
ctx.drawImage(avatarUrl, 0, 0, size, size);
|
|||
|
|
ctx.restore();
|
|||
|
|
|
|||
|
|
ctx.draw(false, () => {
|
|||
|
|
wx.canvasToTempFilePath({
|
|||
|
|
canvasId: 'thumbnail-canvas',
|
|||
|
|
destWidth: size,
|
|||
|
|
destHeight: size,
|
|||
|
|
success: (res) => {
|
|||
|
|
resolve(res.tempFilePath);
|
|||
|
|
},
|
|||
|
|
fail: (err) => {
|
|||
|
|
reject(err);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
fail: reject
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|