這篇文章主要講解了“怎么在小程序中實現保存圖片組件功能”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“怎么在小程序中實現保存圖片組件功能”吧!
首先聲明下組件采用的是uniapp,具體實現了可以繪制圖片、繪制文字以及保存海報至相冊的基本功能,在開發中這些也完全夠用了。
通過canvas繪制海報。通過uni.canvasToTempFilePath 將繪制好的 canvas轉為圖片。通過uni.saveImageToPhotosAlbum 將本地臨時路徑的圖片保存至手機相冊中。而我的想法是將所有采用的方法全部封裝到組件中,只通過父組件去調用需要使用的方法和調整相關的參數即可。 具體使用可以查看示例代碼
通過使用promise對象決定繪制海報內容的順序先后。promise.all()方法進行canvas最后一步的繪畫操作 context.draw()
在繪制圖片 和 頭像時,組件通過uni.getImageInfo() 去獲取圖片的相關信息,調用該方法成功的前提是需要在微信小程序后臺配置download域名和request域名當然最好把uploadFile域名也一起配置,防止出差錯。但是官方給出的提示是配置download域名白名單即可,但是獲取不到圖片信息,這算是一個大坑了。
如果沒有進行相關配置,在調試時 或者 體驗版 正式版等 打開了vconsole調試工具。uni.getImageInfo() 是可以獲取到圖片信息的,一旦關閉了vconsole uni.getImageInfo() 將會fail, 也是個坑。
canvasInfo Object (必需)
canvasWidth 畫布寬度
canvasHeight 畫布高度
canvasId 畫布標識
isFullScreen Boolean
為ture時表示畫布為手機屏幕全屏,canvasInfo設置的寬高將失效。
默認為 false
canvasInit(callback) canvas初始化,所有有關畫布canvas操作需在其回調函數操作。
drawCanvasImage(context, src, _imageWidth, _imageHeight, dx, dy) 在canvas繪制一張圖片
drawCircularAvatar(context, url, _circularX, _circularY, _circularR) 在canvas繪制一張圓形圖片
drawText(options) 在canvas繪制單行、多行文本
startDrawToImage(context, promiseArr, callback) 將canvas操作draw()進行繪制
posterToPhotosAlbum(filePath) 保存至手機相冊
<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><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>感謝各位的閱讀,以上就是“怎么在小程序中實現保存圖片組件功能”的內容了,經過本文的學習后,相信大家對怎么在小程序中實現保存圖片組件功能這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。