Cet article vous permettra de parler du développement du composant de sauvegarde d'image Applet WeChat. J'espère qu'il sera utile à tout le monde !
De nombreux mini-programmes WeChat permettent aux utilisateurs de partager des activités en enregistrant des affiches pour informer davantage de personnes de leurs mini-programmes. J'ai dû rencontrer cela lors du développement de mini-programmes. [Recommandations d'apprentissage associées : Tutoriel de développement de mini-programmes]
Aujourd'hui, je vais partager la fonction de sauvegarde des affiches dans un petit programme que j'ai réalisé dans l'entreprise. Tout d’abord, permettez-moi de décrire quels étaient les besoins de l’entreprise auparavant. Le mini-programme en ligne de l'entreprise aura pour objectif à long terme de promouvoir de nouveaux utilisateurs. Chaque utilisateur doit avoir sa propre affiche, et la promotion via des affiches personnelles n'est qu'un moyen simple.
Après avoir reçu la tâche, je suis d'abord allé sur Internet universel pour faire des recherches, mais mon frère aîné m'a dit qu'il avait fait quelque chose de similaire, mais c'était juste pour terminer la tâche, donc le code était très compliqué, puis il Je l'ai cherché dans le code d'autres projets. Je l'ai cherché et trouvé pour moi~~~ Mais le temps qui m'était imparti était serré et les tâches étaient lourdes, j'ai donc dû faire quelques ajustements et les soumettre d'abord. Après cela, j'ai suivi les articles en ligne et suivi les pièges étape par étape, et j'ai implémenté étape par étape un composant pour enregistrer les affiches.
Idée
Tout d'abord, permettez-moi de préciser que le composant utilise uniapp, qui implémente spécifiquement les fonctions de base consistant à dessiner des images, à dessiner du texte et à enregistrer des affiches dans des albums. Celles-ci sont tout à fait suffisantes pendant le développement.
Dessinez des affiches sur toile. Utilisez uni.canvasToTempFilePath
pour convertir le canevas dessiné en image. Utilisez uni.saveImageToPhotosAlbum
pour enregistrer les images dans le chemin temporaire local vers l'album du téléphone mobile. Mon idée est d'encapsuler toutes les méthodes utilisées dans des composants et d'utiliser uniquement le composant parent pour appeler les méthodes qui doivent être utilisées et ajuster les paramètres pertinents. Pour une utilisation spécifique, vous pouvez consulter l'exemple de codeuni.canvasToTempFilePath
将绘制好的 canvas转为图片。通过uni.saveImageToPhotosAlbum
将本地临时路径的图片保存至手机相册中。而我的想法是将所有采用的方法全部封装到组件中,只通过父组件去调用需要使用的方法和调整相关的参数即可。 具体使用可以查看示例代码
通过canvas绘制海报内容的顺序先后问题
通过使用promise对象决定绘制海报内容的顺序先后。promise.all()
方法进行canvas最后一步的绘画操作 context.draw()
注意uni.getImageInfo()
Lors du dessin d'images et d'avatars, le composant obtient les informations pertinentes de l'image via
uni.getImageInfo()
. La condition préalable pour appeler cette méthode avec succès est que le nom de domaine téléchargé et. Le nom de domaine de téléchargement doit être configuré en arrière-plan de l'applet WeChat. Bien entendu, il est préférable de configurer le nom de domaine de la demande avec le nom de domaine uploadFile pour éviter les erreurs. Cependant, le conseil officiel est de configurer la liste blanche des noms de domaine de téléchargement, mais les informations sur l'image ne peuvent pas être obtenues, ce qui est un gros piège.
S'il n'y a pas de configuration pertinente, ouvrez l'outil de débogage de vconsole pendant le débogage ou la version d'essai, la version officielle, etc. uni.getImageInfo() peut obtenir des informations sur l'image une fois la vconsole fermée, uni.getImageInfo() échouera, ce qui est également un piège.
-
Cette méthode de composant, introduction variable
- props
- canvasInfo Objet (obligatoire)
- canvasWidth toile largeur
canvasHeight toile hauteur
-
can vasI d identifiant de toile
- isFullScreen Lorsque Boolean
est vrai, cela signifie que le canevas est le plein écran de l'écran du téléphone mobile et que la largeur et la hauteur définies par canvasInfo ne seront pas valides.
default est false
- methods
- canvasInit(callback) initialisation du canevas, toutes les opérations du canevas doivent être effectuées dans sa fonction de rappel.
- drawCanvasImage(context, src, _imageWidth, _imageHeight, dx, dy) Dessinez une image sur toile
- drawCircularAvatar(context, url, _circularX, _circularY, _circularR) Dessinez une image circulaire sur toile
- drawText (options) Dessinez du texte sur une ou plusieurs lignes sur la toile
startDrawToImage (contexte, promiseArr, rappel) Utilisez draw() sur la toile pour dessiner
posterToPhotosAlbum (filePath) Enregistrer dans l'album du téléphone portable
Échantillon code
<template>
<view>
<view class="savePosterItem">
<image v-show="tempFilePath" :src="tempFilePath"></image>
<save-poster-com v-show="!tempFilePath" ref="savePoster" :canvasInfo="canvasInfo"></save-poster-com>
</view>
<button class="savePosterBtn" type="primary" @click="saveBtnFun">保存海报</button>
</view>
</template>
<script>
import SavePosterCom from '@/components/SavePosterCom/SavePosterCom.vue'
export default {
components: {
SavePosterCom
},
data() {
return {
canvasInfo: {
canvasWidth: 620,
canvasHeight: 950,
canvasId: 'save-poster'
},
tempFilePath: '',
canvasBgUrl: 'https://images.pexels.com/photos/4065617/pexels-photo-4065617.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
avatarUrl: 'https://p9-passport.byteacctimg.com/img/user-avatar/4dbf31fa6dec9c65b78a70d28d843c04~300x300.image'
}
},
onLoad() {
let {
drawCanvasImage,
drawCircularAvatar,
drawText
} = this.$refs.savePoster.$options.methods
this.$refs.savePoster.canvasInit(({
context,
comThis
}) => {
// 获取画布宽高
let canvasWH = comThis.canvasWH
// 绘制海报背景图
let promise_1 = drawCanvasImage(context, this.canvasBgUrl, canvasWH.canvasWidth, canvasWH.canvasHeight)
// 必须先绘制玩海报背景图 再去操作其他绘制内容
promise_1.then(res => {
let promise_2 = drawCircularAvatar(context, this.avatarUrl, canvasWH.canvasWidth / 2, canvasWH.canvasHeight /
7, 70)
let promise_3 = drawText({
context: context,
text: '皮皮虾仁',
dx: (canvasWH.canvasWidth / 2) + 60,
dy: canvasWH.canvasHeight / 4,
fontSize: 30,
fontColor: '#5D4037'
})
let promise_4 = drawCanvasImage(context, this.avatarUrl, 150, 150, (canvasWH.canvasWidth / 2) + 85, (canvasWH.canvasHeight -
165))
this.$refs.savePoster.startDrawToImage(context, [promise_1,promise_2,promise_4], (tempFilePath) => {
this.tempFilePath = tempFilePath
})
})
})
},
methods: {
saveBtnFun() {
uni.showModal({
title: '保存海报',
content: '海报将被保存至相册中',
confirmText: '保存',
success: (res) => {
if(res.confirm) {
this.$refs.savePoster.posterToPhotosAlbum(this.tempFilePath)
}
}
})
}
}
}
</script>
<style>
.savePosterItem {
text-align: center;
}
.savePosterItem > image {
width: 620rpx;
height: 950rpx;
}
.savePosterBtn {
margin-top: 40rpx;
width: 80%;
}
</style>
Copier après la connexion
Code source du composant<template>
<view>
<canvas :canvas-id="canvasInfo.canvasId" :style="{width: canvasWH.canvasWidth + 'px', height: canvasWH.canvasHeight + 'px'}"></canvas>
</view>
</template>
<script>
export default {
name: 'savePosterCom',
data() {
return {
userPhoneWHInfo: {},
canvasWH: {
canvasWidth: 0,
canvasHeight: 0
}
}
},
props: {
// 决定保存下来的图片的宽高
canvasInfo: {
type: Object,
default: () => {
return {
canvasWidth: 0,
canvasHeight: 0,
canvasId: 'canvasId'
}
}
},
// canvas画布是不是全屏,默认是false。 false时使用必须传 canvasInfo
isFullScreen: Boolean
},
created() {
this.userPhoneWHInfo = this.getPhoneSystemInfo()
if (this.isFullScreen) { // 画布全屏
this.canvasWH.canvasWidth = this.userPhoneWHInfo.windowWidth
this.canvasWH.canvasHeight = this.userPhoneWHInfo.windowHeight
} else { // 指定宽高
this.canvasWH.canvasWidth = this.canvasInfo.canvasWidth
this.canvasWH.canvasHeight = this.canvasInfo.canvasHeight
}
},
mounted() {},
methods: {
/**
* 获取用户手机屏幕信息
*/
getPhoneSystemInfo() {
const res = uni.getSystemInfoSync();
return {
windowWidth: res.windowWidth,
windowHeight: res.windowHeight
}
},
/** 获取 CanvasContext实例
* @param {String} canvasId
*/
getCanvasContextInit(canvasId) {
return uni.createCanvasContext(canvasId, this)
},
/** 保存海报组件初始化
* @param {Function} callback(context) 回调函数
*/
canvasInit(callback) {
let context = this.getCanvasContextInit(this.canvasInfo.canvasId)
if (context) {
callback({
context: context,
comThis: this
})
}
},
/** 将上诉的绘制画到画布中 并且 将画布导出为图片
* @param context 画布
* @param {Promise[]} 存放Promise的数组
* @param {Function} callback 保存图片后执行的回调函数(本地图片临时路径)
*/
startDrawToImage(context, promiseArr, callback) {
// 将之前在绘图上下文中的描述(路径、变形、样式)画到 canvas 中
let canvasId = this.canvasInfo.canvasId
let tempFilePath = ''
Promise.all(promiseArr).then(res => {
context.draw(false, async () => {
callback(await this.canvasToImage(canvasId))
})
})
},
/**
* 在canvas绘制一张图片
* @param context 画布
* @param src 图片资源
* @param _imageWidth 图片宽度
* @param _imageHeight 图片高度
*/
drawCanvasImage(context, src, _imageWidth, _imageHeight, dx, dy) {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: src,
success: res => {
context.drawImage(res.path, (dx - _imageWidth), (dy - _imageHeight), _imageWidth, _imageHeight)
resolve(context)
},
})
})
},
/** 绘制一个圆形头像
* @param context 画布
* @param url 图片地址
* @param _circularX 圆心X坐标
* @param _circularY 圆心Y坐标
* @param _circularR 圆半径
*/
drawCircularAvatar(context, url, _circularX, _circularY, _circularR) {
let dx = _circularX - _circularR;
let dy = _circularY - _circularR;
let dwidth = _circularR * 2;
let dheight = _circularR * 2
return new Promise((resolve, reject) => {
uni.downloadFile({
url: url,
success: res => {
context.save()
context.beginPath()
// _circularX圆的x坐标 _circularY圆的y坐标 _circularR圆的半径
context.arc(_circularX, _circularY, _circularR, 0, 2 * Math.PI)
context.clip()
// dx: 图像的左上角在目标canvas上 X 轴的位置
// dy: 图像的左上角在目标canvas上 Y 轴的位置
// dwidth: 在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放
// dheight: 在目标画布上绘制图像的高度,允许对绘制的图像进行缩放
context.drawImage(res.tempFilePath, dx, dy, dwidth, dheight)
context.restore()
// context.draw()
resolve(context)
}
})
})
},
/** 绘制多行文本 注:, 和 空格都算一个字
* @param context 画布
* @param text 需要被绘制的文本
* @param dx 左上角x坐标
* @param dy 右上角y坐标
* @param rowStrnum 每行多少个字 (默认为text字体个数->单行)
* @param fontSize 文字大小 (默认16)
* @param fontColor 文字颜色 (默认black)
* @param lineHeight 单行文本行高 (默认0)
*/
drawText(options) {
let {
context,
text,
dx,
dy,
rowStrnum = text.length,
lineHeight = 0,
fontSize = 16,
fontColor = 'black'
} = options
return new Promise((resolve, reject) => {
context.setFontSize(fontSize)
context.setFillStyle(fontColor)
context.setTextBaseline('middle')
// 获取需要绘制的文本宽度
let textWidth = Number(context.measureText(text).width)
// console.log('textWidth',textWidth)
// 获取文本的字数
let textNum = text.length
// 获取行数 向上取整
let lineNum = Math.ceil(textNum / rowStrnum)
// console.log('textNum',textNum)
// console.log('lineNum',lineNum)
for (let i = 0; i < lineNum; i++) {
let sliceText = text.slice(i * rowStrnum, (i + 1) * rowStrnum)
// fillText 的 dx = 文字最左边的距离到屏幕政策的距离
context.fillText(sliceText, dx - textWidth, dy + i * lineHeight);
}
resolve(context)
})
},
/** 将画布导出为图片
* @param canvasId 画布标识
*/
canvasToImage(canvasId) {
return new Promise((resolve, reject) => {
uni.canvasToTempFilePath({
canvasId: canvasId, // 画布标识
success: res => {
// 在H5平台下,tempFilePath 为 base64
resolve(res.tempFilePath)
},
fail: err => {
console.log('err', err)
reject(err)
}
}, this)
})
},
/** 保存生成的图片到本地相册中
* @param {String} filePath 图片临时路劲
*/
posterToPhotosAlbum(filePath) {
console.log('filePath',filePath)
uni.showLoading({
title: '保存中...'
})
uni.saveImageToPhotosAlbum({
filePath: filePath,
success: (res) => {
uni.showToast({
title: '保存成功,请前往手机相册中查看',
mask: true,
icon: 'none',
duration: 2000
})
},
fail: (err) => {
console.log('err',err)
if (err.errMsg.includes('deny')||err.errMsg.includes('denied')) { // 用户选择拒绝
this.openSetting()
} else if (err.errMsg.includes('fail cancel')) { // 用户在保存图片时 取消了
uni.showToast({
title: '已取消保存,无法保存至相册',
mask: true,
icon: 'none',
duration: 2000
})
return
}
},
complete: () => {
uni.hideLoading()
}
})
},
/**
* 打开摄像头设置权限页面
*/
openSetting() {
uni.showModal({
title: '温馨提示',
content: '保存图片至相册中,需要您同意添加访问相册权限',
cancelText: '拒绝',
confirmText: '同意',
success: res => {
if (res.confirm) {
uni.openSetting({
success: settingdata => {
if (settingdata.authSetting['scope.writePhotosAlbum']) {
console.log('获取权限成功,给出再次点击图片保存到相册的提示。')
uni.showToast({
title: '授权成功,请再次点击保存',
icon: 'none',
duration: 2000,
})
} else {
console.log('获取权限失败,给出不给权限就无法正常使用的提示')
uni.showToast({
title: '需要访问相册权限',
icon: 'none',
duration: 2000,
})
}
},
fail: (res) => {
console.log('err', err)
}
})
} else {
uni.showToast({
title: '已拒绝授权,无法保存至相册',
mask: true,
icon: 'none',
duration: 2000
})
return
}
}
})
}
}
}
</script>
<style>
</style>
Copier après la connexion
Effet🎜🎜🎜🎜Pour plus de connaissances sur la programmation, veuillez visiter : 🎜Introduction à la programmation🎜 ! ! 🎜Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!