微信小程序利用Canvas繪制圖片和豎排文字詳解
前言
閑暇時(shí)間抽個(gè)空寫了個(gè)三國殺武將手冊的小程序,中間有個(gè)需求設(shè)計(jì)的是合成武將皮膚圖、豎排的武將姓名、以及小程序碼,然后提供保存圖片到相冊,最終讓用戶可以分享到朋友圈或其他平臺。合成圖片應(yīng)該按照 Canvas 的文檔來做都沒什么問題,主要是有個(gè)豎排文字的需求,這里和大家分享一下。
正文
首先放一張最終保存到相冊的圖片吧~
自我感覺良好,至少達(dá)到了我自己的預(yù)期吧~~~
下面讓我們一步一步來看看如何實(shí)現(xiàn)的吧。
整個(gè)圖片分為三個(gè)部分:
- 武將圖片
- 小程序碼
- 武將文字信息
先來看一下 wxml 里面的代碼,主要是放了一個(gè) canvas 標(biāo)簽,控制了一下高度和寬度屬性。
<view> <canvas class='share-canvas' style="width:100%;height:{{canvasHeight}}px" canvas-id="share_canvas"></canvas> </view>
武將圖片
drawHeroImage: function (path) { var that = this; // 拿到canvas context let ctx = wx.createCanvasContext('share_canvas'); // 為了保證圖片比例以及繪制的位置,先要拿到圖片的大小 wx.getImageInfo({ src: path, success: function (res) { // 計(jì)算圖片占比信息 let maxWidth = Math.min(res.width, that.data.canvasWidth * 0.65); let radio = maxWidth / res.width; let offsetY = (that.data.canvasHeight - res.height * radio) / 2; console.log('offsetY=' + offsetY); that.setData({ imageWidth: res.width * radio, imageHeight: res.height * radio, offsetY: offsetY, }); // 繪制canvas背景,不屬于繪制圖片部分 ctx.setFillStyle('white') ctx.fillRect(0, 0, that.data.canvasWidth, that.data.canvasHeight); // 繪制武將圖片,path是本地路徑,不可以傳網(wǎng)絡(luò)url,如果是網(wǎng)絡(luò)圖片需要先下載 ctx.drawImage(path, 10, offsetY, res.width * radio, res.height * radio) // 繪制小程序碼 that.drawQrCodeImage(ctx); // 繪制勢力漢字:吳 that.drawInfluence(ctx, that.data.hero.HERO.INFLUENCE); // 繪制武將姓名:陸遜 that.drawName(ctx, that.data.hero.HERO.NAME); // 繪制武將稱號:江陵侯 that.drawHorner(ctx, that.data.hero.HERO.HORNER); // 最終調(diào)用draw函數(shù),生成預(yù)覽圖 // 一個(gè)坑點(diǎn):只能調(diào)用一次,否則后面的會覆蓋前面的 ctx.draw(); } }); }
小程序碼
小程序碼和武將圖片是一個(gè)類型,無非就是需要計(jì)算繪制的位置,這里就不再展示相關(guān)代碼了。
武將文字信息
從剛剛的代碼可以看出,我分了3個(gè)部分來繪制,其實(shí) 吳 和 陸遜 應(yīng)該是可以放到一起的,但是我在繪制的時(shí)候發(fā)現(xiàn),空格在繪制的時(shí)候會引起異常,導(dǎo)致空格后面的文字無法繪制出來,所以我這里 吳 和 陸遜 中間的空白是靠位置偏移來做的。
這里就展示一下如何繪制武將稱號的。
// 繪制武將稱號:江陵侯 drawHorner: function (ctx, text) { // 設(shè)置字號 ctx.setFontSize(26); // 設(shè)置字體顏色 ctx.setFillStyle("#000000"); // 計(jì)算繪制起點(diǎn) let x = this.data.offsetX + 35; let y = this.data.offsetY + 10; console.log('drawHorner' + text); console.log(x); console.log(y); // 繪制豎排文字,這里是個(gè)Util函數(shù),具體實(shí)現(xiàn)請繼續(xù)看 Canvas.drawTextVertical(ctx, text, x, y); }
繪制豎排文字從網(wǎng)上找了個(gè)開源的代碼,需要看原理的請看這里
當(dāng)然我這里為了適用小程序做了些改動(dòng),函數(shù)原型是這樣子的:
CanvasRenderingContext2D.prototype.letterSpacingText = function (text, x, y, letterSpacing)
原諒我不是很會 js ,完全不懂這是個(gè)什么語法,看了一會沒弄懂,感覺像是給類添加新的屬性,不管他。
不管白貓黑貓,能抓到耗子就是好貓
改造后的函數(shù)像下面的樣子:
canvas.js /** * @author zhangxinxu(.com) * @licence MIT * @description http://www.zhangxinxu.com/wordpress/?p=7362 */ function drawTextVertical(context, text, x, y) { var arrText = text.split(''); var arrWidth = arrText.map(function (letter) { return 26; // 這里為了找到那個(gè)空格的 bug 做了許多努力,不過似乎是白費(fèi)力了 // const metrics = context.measureText(letter); // console.log(metrics); // const width = metrics.width; // return width; }); var align = context.textAlign; var baseline = context.textBaseline; if (align == 'left') { x = x + Math.max.apply(null, arrWidth) / 2; } else if (align == 'right') { x = x - Math.max.apply(null, arrWidth) / 2; } if (baseline == 'bottom' || baseline == 'alphabetic' || baseline == 'ideographic') { y = y - arrWidth[0] / 2; } else if (baseline == 'top' || baseline == 'hanging') { y = y + arrWidth[0] / 2; } context.textAlign = 'center'; context.textBaseline = 'middle'; // 開始逐字繪制 arrText.forEach(function (letter, index) { // 確定下一個(gè)字符的縱坐標(biāo)位置 var letterWidth = arrWidth[index]; // 是否需要旋轉(zhuǎn)判斷 var code = letter.charCodeAt(0); if (code <= 256) { context.translate(x, y); // 英文字符,旋轉(zhuǎn)90° context.rotate(90 * Math.PI / 180); context.translate(-x, -y); } else if (index > 0 && text.charCodeAt(index - 1) < 256) { // y修正 y = y + arrWidth[index - 1] / 2; } context.fillText(letter, x, y); // 旋轉(zhuǎn)坐標(biāo)系還原成初始態(tài) context.setTransform(1, 0, 0, 1, 0, 0); // 確定下一個(gè)字符的縱坐標(biāo)位置 var letterWidth = arrWidth[index]; y = y + letterWidth; }); // 水平垂直對齊方式還原 context.textAlign = align; context.textBaseline = baseline; } module.exports = { drawTextVertical: drawTextVertical }
繪制網(wǎng)絡(luò)圖片
由于網(wǎng)絡(luò)圖片無法直接繪制,所以需要先下載到本地,然后再按住本地圖片繪制的流程走一遍。
downloadHeroImage: function () { // 微信不支持非https的圖片下載,這里了個(gè)替換 let url = this.data.hero.HERO.ICON.replace(/http/, "https"); var that = this; wx.downloadFile({ url: url, success: function (res) { // 下載成功后拿到圖片的路徑,然后開始繪制 var path = res.tempFilePath; that.drawHeroImage(path); }, fail: function (res) { console.log(res) } }); }
保存圖片
說了這么多,自然少不了最終的一步,將繪制到 canvas 的圖片保存到手機(jī)相冊,這里需要用戶授權(quán),你需要自己處理。
用的是微信給我們提供的接口 wx.canvasToTempFilePath 。需要我們傳入起點(diǎn)坐標(biāo) (x, y)和畫布大小 (width, height) 以及 canvasId 。
saveShareImage: function () { wx.showLoading({ title: '正在保存圖片..', }); let that = this; wx.canvasToTempFilePath({ x: 0, y: 0, width: that.data.canvasWidth, height: that.data.canvasHeight, canvasId: 'share_canvas', success: function (res) { wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success(res) { console.log(res); wx.showToast({ title: '保存到相冊成功', duration: 1500, }) }, fail(res) { console.log(res) wx.showToast({ title: '保存到相冊失敗', icon: 'fail' }) }, complete(res) { console.log(res) } }) } }) }
開源
本著開源的精神,源碼已經(jīng)放在 Github 上,大家可以去上面查看具體代碼。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
[全兼容哦]--實(shí)用、簡潔、炫酷的頁面轉(zhuǎn)入效果loing
[全兼容哦]--實(shí)用、簡潔、炫酷的頁面轉(zhuǎn)入效果loing...2007-05-05js實(shí)現(xiàn)各種復(fù)制到剪貼板的方法(分享)
下面小編就為大家?guī)硪黄猨s實(shí)現(xiàn)各種復(fù)制到剪貼板的方法(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10js、jquery圖片動(dòng)畫、動(dòng)態(tài)切換示例代碼
這篇文章主要介紹了通過js、jquery實(shí)現(xiàn)的圖片動(dòng)畫、圖片動(dòng)態(tài)切換 ,需要的朋友可以參考下2014-06-06配置Grunt的Task時(shí)通配符支持和動(dòng)態(tài)生成文件名問題
這篇文章主要介紹了配置Grunt的Task時(shí)通配符支持和動(dòng)態(tài)生成文件名問題,需要的朋友可以參考下2015-09-09JS匿名函數(shù)和匿名自執(zhí)行函數(shù)概念與用法分析
這篇文章主要介紹了JS匿名函數(shù)和匿名自執(zhí)行函數(shù)概念與用法,結(jié)合實(shí)例形式分析了匿名函數(shù)和匿名自執(zhí)行函數(shù)的概念、功能、應(yīng)用場景及相關(guān)使用技巧,需要的朋友可以參考下2018-03-03Three.js如何實(shí)現(xiàn)霧化效果示例代碼
霧化效果是3D的比較常見的特性,在游戲中見到的煙霧、爆炸火焰以及白云等效果都是霧化的結(jié)果,下面這篇文章主要給大家介紹了關(guān)于Three.js如何實(shí)現(xiàn)霧化效果的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09JavaScript實(shí)現(xiàn)節(jié)點(diǎn)的刪除與序號重建實(shí)例
這篇文章主要介紹了JavaScript實(shí)現(xiàn)節(jié)點(diǎn)的刪除與序號重建方法,涉及javascript針對頁面節(jié)點(diǎn)的刪除與遍歷技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-08-08