微信小程序之生成图片保存到相册
微信小程序之生成圖片保存到相冊
需求概要
電商項目中需要將自己小店的商品帶上自己的小程序碼生成海報,保存到本地,然后分享到萬能的朋友圈,QQ空間,微博等等來廣而告之…
如下圖,三種海報格式輪播展示,左滑右滑切換到海報,點擊下面保存圖片按鈕,將當前海報保存到手機相冊
思路
1、需要商品信息,用戶信息以及小程序碼。
2、使用swiper組件展示海報,
3、將海報通過wx.createCanvasContext繪制到畫布canvas組件。
4、使用canvasToTempFilePath 將canvas海報保存到本地臨時文件路徑;
5、使用saveImageToPhotosAlbum將圖片保存到本地相冊
6、根據(jù)swiper組件的current屬性判斷當前保存的海報
解決方案
按照思路逐步實現(xiàn):
商品信息,用戶信息以及小程序碼
1.商品信息通過導航事件傳遞到海報頁,在此我使用的是模擬數(shù)據(jù);
2.用戶信息通過本地存儲wx.setStorageSync 到緩存。
// index.js//事件處理函數(shù)navToShare: function () {// 模擬數(shù)據(jù)var data = {thumb_images: ['https://cbu01.alicdn.com/img/ibank/2018/544/692/8567296445_882293189.400x400.jpg','https://cbu01.alicdn.com/img/ibank/2018/971/643/8581346179_882293189.400x400.jpg','https://cbu01.alicdn.com/img/ibank/2018/184/392/8567293481_882293189.400x400.jpg'],name: '2018夏季新款鏤空圓領(lǐng)蝙蝠短袖t恤女裝韓版寬松棉小衫上衣批發(fā)潮',price: 198,}wx.navigateTo({url: '../poster/poster?data=' + encodeURIComponent(JSON.stringify(data))})},3.在海報頁面onLoad函數(shù)的參數(shù)中獲取商品信息
4.在海報頁面獲取本地緩存中的用戶信息wx.getStorageSync
5.因為canvas繪制圖片不支持跨域圖片,所以先使用getImageInfo將網(wǎng)絡圖片返回圖片的本地路徑,
// poster.jsonLoad: function(options) {var data = JSON.parse(decodeURIComponent(options.data));var userinfo;// 獲取本地存儲的用戶頭像和昵稱 userinfo = wx.getStorageSync('userInfo');console.log('用戶信息', userinfo)// 渲染頁面this.setData({avatar_url: userinfo.avatarUrl,nickname: userinfo.nickName,thumb_images: data.thumb_images,pro_price: data.price,pro_name: data.name,})// 保存網(wǎng)絡圖片到本地 用于canvas繪制圖片wx.getImageInfo({src: userinfo.avatarUrl,success: (res) => {tmpAvatarUrl = res.path;}});// 保存產(chǎn)品圖到本地 用于canvas繪制圖片var thumbs = data.thumb_images;tmpThumbs = []; // 先清空,再添加新的產(chǎn)品圖thumbs.forEach((item, i) => {wx.getImageInfo({src: item,success: (res) => {tmpThumbs.push(res.path)}})});},6.小程序碼由后端生成,前端通過POST請求將data傳入,返回小程序碼url,使用 wx.getImageInfo保存到本地
// 封裝后的POST方法 wxRequest.postRequest(url, data).then(res => {if (res.data.error_code == 0) {// 保存小程序碼到本地 用于canvas繪制圖片wx.getImageInfo({src: res.data.qrcode,success: (result) => {this.setData({poster_qrcode: result.path})}});} })使用swiper組件展示海報
在這個項目中我是將頁面渲染和canvas繪制分開的,因為小程序單位rpx自動適配各種設備屏幕。而canvas繪制單位是px。我沒有做px和rpx之間的計算,保存px單位固定大小的圖片也不錯。
<view class='poster_swiper'><swiper bindchange="shareChange" current="{{current}}" circular="{{circular}}" previous-margin="100rpx" next-margin="100rpx" class="swiper_share"><swiper-item class="swiper_item1">// 根據(jù)設計渲染頁面</swiper-item><swiper-item class="swiper_item2" wx:if="{{thumb_images.length>1}}">// 根據(jù)設計渲染頁面</swiper-item><swiper-item class="swiper_item3" wx:if="{{thumb_images.length>2}}">// 根據(jù)設計渲染頁面</swiper-item></swiper> </view>這里要用到swiper的幾個屬性列出來
將海報通過wx.createCanvasContext繪制到畫布canvas組件。。
1.在wxml中添加canvas組件,設置canvas-id以便于wx.createCanvasContext繪制畫布
<canvas class='canvas-poster' canvas-id='canvasposter'></canvas>定義樣式固定定位到可視區(qū)以外,不影響可視區(qū)展示。
.canvas-poster {position: fixed;width: 280px;height: 450px;top: 100%;left: 100%;overflow: hidden; }三種海報分別繪制,具體看注釋
/*一張產(chǎn)品圖*/drawPosterOne: function() {var ctx = wx.createCanvasContext('canvasposter');// ctx.clearRect(0, 0, 280, 450);/* 繪制背景*/ctx.rect(0, 0, 280, 450);ctx.setFillStyle('white');ctx.fillRect(0, 0, 280, 450);/*繪制店名*/ctx.setFontSize(16);ctx.setFillStyle('#333');ctx.textAlign = "center";ctx.fillText(this.data.nickname + '的小店', 140, 70);ctx.restore();/*繪制產(chǎn)品圖*/ctx.drawImage(tmpThumbs[0], 35, 90, 210, 210);/* 繪制產(chǎn)品名稱背景*/ctx.setFillStyle('#FF8409');ctx.fillRect(35, 300, 210, 60);/*繪制產(chǎn)品名稱*/ctx.setFontSize(12);ctx.setFillStyle('#ffffff');ctx.textAlign = "left";ctx.fillText(this.data.pro_name.substr(0, 18), 45, 322);ctx.restore();ctx.setFontSize(12);ctx.setFillStyle('#ffffff');ctx.textAlign = "left";ctx.fillText(this.data.pro_name.substr(18, 20), 45, 344);ctx.restore();/* 繪制線框*/ctx.setLineDash([1, 3], 1);ctx.beginPath();ctx.moveTo(35, 375);ctx.lineTo(160, 375);ctx.moveTo(35, 435);ctx.lineTo(160, 435);ctx.setStrokeStyle('#979797');ctx.stroke();ctx.restore();/*繪制文字*/ctx.setFontSize(14);ctx.setFillStyle('#333333');ctx.textAlign = "left";ctx.fillText('¥', 35, 400);ctx.setFontSize(18);ctx.fillText(this.data.pro_price, 50, 400);ctx.setFontSize(11);ctx.setFillStyle('#666666');ctx.fillText(this.data.poster_qrtext, 35, 420);ctx.restore();/*繪制二維碼*/ctx.drawImage(this.data.poster_qrcode, 185, 370, 60, 60);ctx.restore();/*圓形頭像*/ctx.save()ctx.beginPath();ctx.arc(140, 30, 20, 0, 2 * Math.PI)ctx.setFillStyle('#fff')ctx.fill()ctx.clip()ctx.drawImage(tmpAvatarUrl, 120, 10, 40, 40)ctx.restore()ctx.draw(false, this.getTempFilePath);},/*兩張產(chǎn)品圖*/drawPosterTwo: function() {var ctx = wx.createCanvasContext('canvasposter');/* 繪制背景*/ctx.rect(0, 0, 280, 450);ctx.setFillStyle('white');ctx.fillRect(0, 0, 280, 450);/*繪制店名*/ctx.setFontSize(14);ctx.setFillStyle('#333');ctx.textAlign = "left";ctx.fillText(this.data.nickname + '的小店', 65, 36);ctx.restore();/* 繪制虛線框*/ctx.setLineDash([4, 1], 1);ctx.beginPath();ctx.moveTo(25, 60);ctx.lineTo(255, 60);ctx.moveTo(25, 325);ctx.lineTo(255, 325);ctx.setStrokeStyle('#979797');ctx.stroke();ctx.restore();/*繪制產(chǎn)品名稱*/ctx.setFontSize(12);ctx.setFillStyle('#333');ctx.textAlign = "left";ctx.fillText(this.data.pro_name.substr(0, 13), 25, 82);ctx.setFontSize(12);ctx.setFillStyle('#333');ctx.fillText(this.data.pro_name.substr(13, 12) + '...', 25, 100);ctx.restore();/*繪制文字*/ctx.setFontSize(14);ctx.setFillStyle('#333333');ctx.textAlign = "left";ctx.fillText('¥', 190, 90);ctx.setFontSize(16);ctx.fillText(this.data.pro_price, 205, 90);ctx.restore();ctx.setFontSize(10);ctx.setFillStyle('#666666');ctx.textAlign = "center";ctx.fillText(this.data.poster_qrtext, 140, 420);ctx.restore();/*繪制產(chǎn)品圖*/ctx.drawImage(tmpThumbs[0], 25, 115, 110, 150);ctx.drawImage(tmpThumbs[1], 145, 115, 110, 150);ctx.restore();/*繪制文字*/ctx.setFontSize(12);ctx.setFillStyle('#333333');ctx.textAlign = "left";ctx.fillText(this.data.slogan1, 25, 290);ctx.fillText(this.data.slogan2, 25, 308);ctx.restore();/*繪制二維碼*/ctx.drawImage(this.data.poster_qrcode, 110, 330, 70, 70);ctx.restore();/*圓形頭像*/ctx.save()ctx.beginPath();ctx.arc(35, 30, 20, 0, 2 * Math.PI)ctx.setFillStyle('#fff')ctx.fill()ctx.clip()ctx.drawImage(tmpAvatarUrl, 15, 10, 40, 40)ctx.restore()ctx.draw(false, this.getTempFilePath);},/*三張產(chǎn)品圖*/drawPosterThree: function() {var ctx = wx.createCanvasContext('canvasposter');/* 繪制背景*/ctx.rect(0, 0, 280, 450);ctx.setFillStyle('white');ctx.fillRect(0, 0, 280, 450);/*繪制店名*/ctx.setFontSize(16);ctx.setFillStyle('#333');ctx.textAlign = "center";ctx.fillText(this.data.nickname + '的小店', 140, 70);ctx.restore();/* 繪制虛線框*/ctx.beginPath()ctx.setLineDash([4, 1], 1);ctx.beginPath();ctx.moveTo(20, 230);ctx.lineTo(145, 230);ctx.lineTo(145, 305);ctx.lineTo(40, 305);/*左下角圓角 ctx.arcTo( , 左下角左邊坐標,左上角左邊坐標,半徑)*/ctx.arcTo(20, 305, 20, 230, 20);ctx.moveTo(20, 230);ctx.lineTo(20, 285);ctx.setStrokeStyle('#333333')ctx.stroke()ctx.setStrokeStyle('#979797');ctx.stroke();ctx.restore();/*繪制產(chǎn)品名稱*/ctx.setFontSize(12);ctx.setFillStyle('#333');ctx.textAlign = "left";ctx.fillText(this.data.pro_name.substr(0, 9), 30, 250);ctx.setFontSize(12);ctx.setFillStyle('#333');ctx.fillText(this.data.pro_name.substr(9, 8) + '...', 30, 268);ctx.restore();/*繪制文字*/ctx.setFontSize(14);ctx.setFillStyle('#333333');ctx.textAlign = "left";ctx.fillText('¥', 30, 290);ctx.setFontSize(16);ctx.fillText(this.data.pro_price, 45, 290);ctx.restore();ctx.setFontSize(10);ctx.setFillStyle('#666666');ctx.textAlign = "center";ctx.fillText(this.data.poster_qrtext, 140, 420);ctx.restore();/*繪制產(chǎn)品圖*/ctx.drawImage(tmpThumbs[0], 20, 90, 125, 125);ctx.drawImage(tmpThumbs[1], 160, 90, 100, 100);ctx.drawImage(tmpThumbs[2], 160, 205, 100, 100);ctx.restore();ctx.restore();/*繪制二維碼*/ctx.drawImage(this.data.poster_qrcode, 110, 330, 70, 70);ctx.restore();/*圓形頭像*/ctx.save()ctx.beginPath();ctx.arc(140, 30, 20, 0, 2 * Math.PI)ctx.setFillStyle('#fff')ctx.fill()ctx.clip()ctx.drawImage(tmpAvatarUrl, 120, 10, 40, 40)ctx.restore()ctx.draw(false, this.getTempFilePath);},繪制中用到的數(shù)據(jù)如下
var tmpAvatarUrl = ""; /*用于繪制頭像*/ var tmpThumbs = []; /*用于繪制產(chǎn)品圖*/ var drawing = false; /*避免多次點擊保存按鈕*/ Page({/*** 頁面的初始數(shù)據(jù)*/data: {circular: true, // swiper 是否采用銜接滑動current: 0, // swiper 當前所在滑塊的 indexavatar_url: '', // 渲染頭像nickname: '', // 渲染昵稱poster_qrcode: '/images/poster_qrcode.png', // 小程序碼poster_qrtext: '長按識別,即可查看商品',pro_name: '', //產(chǎn)品名pro_price: '', // 產(chǎn)品價格slogan1: '我的小店上新了,', // 標語 1slogan2: '快來一起快來一起看看吧!', // 標語 2thumb_images: [] // 渲染圖片},使用canvasToTempFilePath 將canvas海報保存到本地臨時文件路徑;
//獲取臨時路徑getTempFilePath: function() {wx.canvasToTempFilePath({canvasId: 'canvasposter',success: (res) => {this.saveImageToPhotosAlbum(res.tempFilePath)}})},使用saveImageToPhotosAlbum將圖片保存到本地相冊
//保存至相冊saveImageToPhotosAlbum: function(imgUrl) {if (imgUrl) {wx.saveImageToPhotosAlbum({filePath: imgUrl,success: (res) => {wx.showToast({title: '保存成功',icon: 'success',duration: 2000})drawing = false},fail: (err) => {wx.showToast({title: '保存失敗',icon: 'none',duration: 2000})drawing = false}})}else{wx.showToast({title: '繪制中……',icon: 'loading',duration: 3000})}},注意canvas繪制需要時間,所以設置 drawing 防止繪制被打斷
根據(jù)swiper組件的current屬性判斷當前保存的海報
1.首先根據(jù) change 事件設置current
shareChange: function(e) {if (e.detail.source == 'touch') {this.setData({current: e.detail.current})}},2.通過點擊按鈕執(zhí)行savePoster保存海報到手機相冊
<view class="common_btn" catchtap="savePoster"><text>保存圖片</text></view>判斷是否獲取相冊授權(quán),已獲得權(quán)限直接繪制,若未獲得權(quán)限需提示用戶前去設置授權(quán)
/*保存海報到手機相冊*/savePoster: function(e) {var that = this;var current = this.data.current;//獲取相冊授權(quán)wx.getSetting({success(res) {if (!res.authSetting['scope.writePhotosAlbum']) {wx.authorize({scope: 'scope.writePhotosAlbum',success() { //這里是用戶同意授權(quán)后的回調(diào)that.drawPoster(current);},fail() { //這里是用戶拒絕授權(quán)后的回調(diào)wx.showModal({title: '提示',content: '若不打開授權(quán),則無法將圖片保存在相冊中!',showCancel: true,cancelText: '去授權(quán)',cancelColor: '#000000',confirmText: '暫不授權(quán)',confirmColor: '#3CC51F',success: function(res) {if (res) {wx.openSetting({//調(diào)起客戶端小程序設置界面,返回用戶設置的操作結(jié)果。})} else {// console.log('用戶點擊取消')}}})}})} else { //用戶已經(jīng)授權(quán)過了 that.drawPoster(current);}}})},3.根據(jù)current判斷當前海報繪制對應海報
/* 繪制海報*/drawPoster: function(current) {if(drawing){wx.showToast({title: '繪制中……',icon: 'loading',duration: 3000}) }else{drawing = true;// loading // 根據(jù)swiper當前所在滑塊的 index判斷繪制對應海報switch (current) {case 0:this.drawPosterOne()break;case 1:this.drawPosterTwo()break;case 2:this.drawPosterThree()break;}}},保存到手機相冊的海報如下:
總結(jié)
以上是生活随笔為你收集整理的微信小程序之生成图片保存到相册的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在微信小程序上,帮助中心界面实现类似手风
- 下一篇: vue项目搜索历史功能的实现