小程序篇-canvas使用小技巧

前面說的話

某產(chǎn)品:這個(gè)版本我想要做個(gè)我們的內(nèi)容可以生成海報(bào)的功能,

用戶可以保存海報(bào)石洗,然后在朋友圈分享幢泼,

巴拉巴拉

。劲腿。旭绒。

。焦人。

挥吵。

,現(xiàn)在UI還在設(shè)計(jì)一款比較漂亮的海報(bào)花椭。忽匈。。

某前端(心里想):希望布局不要太復(fù)雜矿辽,不然用canvas生成的話丹允,又得加班加點(diǎn)了。
(眉頭一皺):嗯袋倔,可以雕蔽,到時(shí)候UI出來設(shè)計(jì)稿的時(shí)候,我就開始動(dòng)工宾娜。


然后UI出圖了批狐。【我們UI一般把圖上傳至 藍(lán)湖 這樣比較好看一些尺寸信息,下面的圖片也是從 藍(lán)湖 上截圖的】

設(shè)計(jì)稿圖

感覺還不錯(cuò)前塔,不會(huì)很復(fù)雜的樣子嚣艇,(以這張海報(bào)先舉個(gè)例子)

看到這幅圖的時(shí)候,你要做的幾件事:

1.首先問問UI华弓,這是不是最終版本

2.確定好是最終版本后食零,我們開始在這個(gè)海報(bào)頁上區(qū)分,哪些是變量寂屏,哪些是固定元素/切圖贰谣,哪些是UI需要提供的切圖

3.確定好之后娜搂,我們就可以開始動(dòng)工了


接下來我們就來看看幾個(gè)需要實(shí)現(xiàn)的技術(shù)點(diǎn)

1.海報(bào)中間的圖片需要怎么樣去控制,才能顯示得“好(保持縱橫比縮放圖片冈爹,只保證圖片的短邊能完全顯示出來也就是說涌攻,圖片通常只在水平或垂直方向是完整的,另一個(gè)方向?qū)?huì)發(fā)生截取频伤。)”?

2.底部的文字如何實(shí)現(xiàn)換行芝此?

3.分享自@xxx 的文字如果太長怎么辦憋肖?


我們一一來解決這些問題吧

基礎(chǔ)準(zhǔn)備,設(shè)計(jì)稿一般都為750*1334婚苹,一般來說我們會(huì)讓設(shè)計(jì)稿標(biāo)注各個(gè)元素的間距岸更,這個(gè)間距是px單位的,

但是小程序是rpx單位的膊升,怎么建立起對應(yīng)關(guān)系怎炊,而又在開發(fā)中提升效率呢

舉個(gè)例子
首先我們創(chuàng)建一個(gè)叫做poster的小程序頁面
我們在wxml,這里有三個(gè)變量廓译,分別是cardCreateImgUrl【canvas生成的圖片路徑】评肆、WIDTH【設(shè)計(jì)稿的寬度】、HEIGHT【設(shè)計(jì)稿的高度】

<!-- ... -->
<view id="posterWrp">
  <view class="cardCreateWrp">
     <image src="{{cardCreateImgUrl}}" mode="aspectFill" class="imgCardWrp" bindload="bindload" 
style="width:{{WIDTH}}rpx;height:{{HEIGHT}}rpx;"></image> 
  </view>
  <canvas class="myCanvas" canvas-id="myCanvas" style="width:{{WIDTH}}px;height:{{HEIGHT}}px;" ></canvas>
</view>
<!-- ... -->

wxss中非区,我們把canvas的top設(shè)置成-9999px 瓜挽,
其實(shí)canvas是存在頁面的,只是我們把它用這種方式隱藏了征绸,
這樣做的目的久橙,就相當(dāng)于canvas我們現(xiàn)在只用來作為一個(gè)畫圖工具,
我們畫好canvas并把它生成的圖片路徑賦值到image標(biāo)簽的src來進(jìn)行顯示管怠,
這就是我們提到的提高開發(fā)效率的第一步淆衷,
我們現(xiàn)在看到的是一張已經(jīng)生成好的圖片,而不是canvas

// ...
.cardCreateWrp image {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
.myCanvas {
  position: absolute;
  top: -9999px;
  left: 50%;
  transform: translate(-50%, -50%);
}
// ...

js中渤弛,
我們根據(jù)設(shè)計(jì)稿的寬高來設(shè)置

海報(bào)圖片

data: {
  // ...
  WIDTH: 750,
  HEIGHT: 1148,
  cardCreateImgUrl: ''
  // ...
},

我們在一開始畫canvas的時(shí)候就要把canvas生成圖片了

// ...
// 前面是一些canvans的操作祝拯,略過
// ...
ctx.draw(false, () => {
  // 生成圖片
  wxp.canvasToTempFilePath({
    canvasId: 'myCanvas'
  }).then(({
    tempFilePath
  }) => {
    this.setData({
      cardCreateImgUrl: tempFilePath
    });
  });
});

這樣子的話,我們一開始就是一張圖片暮芭,邊畫畫布的時(shí)候鹿驼,就能看到圖片生成的樣子。

這些都是準(zhǔn)備工作辕宏,準(zhǔn)備好了之后我們就可以開始解決我們在畫圖過程中的技術(shù)點(diǎn)了畜晰。


1.海報(bào)中間的圖片需要怎么樣去控制,才能顯示得“好(保持縱橫比縮放圖片瑞筐,只保證圖片的短邊能完全顯示出來凄鼻。也就是說,圖片通常只在水平或垂直方向是完整的,另一個(gè)方向?qū)?huì)發(fā)生截取块蚌。)”闰非?

這里就要涉及到畫布的clip()方法和我們用js去計(jì)算了,
不清楚clip的話可以先去微信小程序官網(wǎng)api了解一下峭范,
了解以后我們來看以下的代碼

設(shè)計(jì)圖的圖片寬高是這樣的 621px财松、668px

距離頂部和左部是這樣的 251px、66px

代碼就不多解釋了纱控,反正就是這些計(jì)算就是讓圖片能更好的顯示

// ...
wxp.getImageInfo({
  // 七牛云做壓縮
  src: `${data.imageUrl}?imageView2/1/w/621/h/668/q/50`
}).then((res) => {
  // 計(jì)算海報(bào)寬高 
  const scale1 = res.width / res.height;
  const scale2 = 621 / 668;
  let drawW = 0,
      drawH = 0,
      mt = 0,
      ml = 0;
  if (scale1 > scale2) {
    drawH = 668;
    drawW = 668 * scale1;
    ml = (621 - drawW) / 2;
  } else {
    drawW = 621;
    drawH = drawW / scale1;
    mt = (668 - drawH) / 2;
  }

  ctx.save();
  ctx.beginPath();
  ctx.strokeStyle = "rgba(0,0,0,0)";
  ctx.rect(66, 251, 621, 668);
  ctx.closePath();
  ctx.stroke();

  ctx.clip();
  // 畫圖片
  ctx.drawImage(res.path, ml + 66, mt + 251, drawW, drawH);
  ctx.restore();
  
 // ...
});
// ...

這樣子辆毡,我們第一個(gè)技術(shù)點(diǎn)就解決了


2.底部的文字如何實(shí)現(xiàn)換行
我們都知道canvas不像我們在寫html頁面時(shí),如果文字太長了甜害,會(huì)自動(dòng)換行舶掖。
所以我們應(yīng)該怎么做呢
這里我們用到了 measureText() 這個(gè)方法

我們把文字過長換行的方法封裝了一下
我們創(chuàng)建了一個(gè)名為canvas-text-break.js 的js文件


/**
 * @desc canvas文字過長換行腳本
 * @param {Object} ctx canvas對象
 * @param {String} text 文字
 * @param {Number} x 距離左邊的寬度
 * @param {Number} y 距離右邊的寬度
 * @param {Number} w 文本區(qū)域的寬度
 * @param {Object} fontStyle 文本的字體風(fēng)格/位置,有默認(rèn)值
 */
const CTB = ({
  ctx,
  text,
  x,
  y,
  w,
  fontStyle: {
    lineHeight = 60,
    textAlign = 'left',
    textBaseline = 'top',
    font = 'normal 40px arial',
    fillStyle = '#000000'
  }
}) => {
  ctx.save();
  ctx.font = font;
  ctx.fillStyle = fillStyle;
  ctx.textAlign = textAlign;
  ctx.textBaseline = textBaseline;
  const chr = text.split('');
  const row = [];
  let temp = '';

  /*
  判斷如果末尾是尔店,眨攘!∠荩》 就不要換行
  判斷如果末尾是《 就要換行
  */
  for (let a = 0; a < chr.length; a++) {
    if (ctx.measureText(temp).width < w) { } else {
      if (/[鲫售,。避诽!》]/im.test(chr[a])) {
        // console.log(`我是${chr[a]},我在末尾,我不換行`);
        temp += ` ${chr[a]}`;
        // 跳過這個(gè)字符
        a++;
      }
      if (/[《]/im.test(chr[a - 1])) {
        // console.log(`我是${chr[a-1]},我在末尾,我要換行`);
        // 刪除這個(gè)字符
        temp = temp.substr(0, temp.length - 1);
        a--;
      }

      row.push(temp);
      temp = '';
    }

    temp += chr[a] ? chr[a] : '';
  }
  row.push(temp);
  for (let b = 0; b < row.length; b++) {
    ctx.fillText(row[b], x, y + b * lineHeight);
  }
  ctx.restore();
};
export default CTB;

文字的相關(guān)信息

根據(jù)上面這個(gè)圖龟虎,我們就可以獲取信息

  x: 64,
  y: 988,
  w: 350,
  fontStyle: {
    lineHeight: 39,
    textAlign: 'left',
    textBaseline: 'top',
    font: 'normal 23px arial',
    fontSize: 23,
    fillStyle: '#000000'
  }

然后在js中我們這樣子做

import CTB from '../../utils/canvas-text-break';
let ctx = null;
// ...

// ...
ctx = wx.createCanvasContext('myCanvas');
const text = '你來人間一趟,你要看看太陽沙庐。和你的心上人一起走在街上鲤妥。';
// ...
CTB({
  ctx,
  text,
  x: 64,
  y: 988,
  w: 350,?
  fontStyle: {
    lineHeight: 39,
    textAlign: 'left',
    textBaseline: 'top',
    font: 'normal 23px arial',
    fontSize: 23,
    fillStyle: '#000000'
  }
});

這樣子,我們的第二個(gè)技術(shù)點(diǎn)也解決了


3.分享自@xxx 的文字如果太長怎么辦拱雏?
這個(gè)問題比較簡單


文字距離頂部和右部是這樣的 1065px棉安、66px

我們在js中這樣寫,記得
ctx.textBaseline = 'top';
ctx.textAlign = 'right';
這樣子的話文字多的話铸抑,就會(huì)往左邊延伸了贡耽,這個(gè)原理和我們在操作html文字的原理是類似的

ctx.save();
ctx.font = 'bold 25px arial';
ctx.fillStyle = '#000000';
ctx.textBaseline = 'top';
ctx.textAlign = 'right';
const {
  nickName
} = wx.getStorageSync('user');
ctx.fillText(`分享自 @${nickName}`, WIDTH - 63, 1065);
ctx.restore();

這樣子,我們所有的技術(shù)點(diǎn)都解決了


注:wxp為我自己封裝的對象鹊汛,并不是wx的對象蒲赂,在這里 小程序篇-wx自帶api接入Promise,提升編程體驗(yàn)

更新:可能有些人比較習(xí)慣看demo刁憋,這里分享一個(gè) 代碼片段

以上只是分享一些小技巧而已滥嘴,我們在實(shí)際操作中肯定遇到的場景會(huì)更復(fù)雜,這些都是一些基礎(chǔ)要知道的至耻。
——尼古拉斯·峰

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末若皱,一起剝皮案震驚了整個(gè)濱河市镊叁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌走触,老刑警劉巖晦譬,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異互广,居然都是意外死亡敛腌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門兜辞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來迎瞧,“玉大人,你說我怎么就攤上這事逸吵。” “怎么了缝裁?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵扫皱,是天一觀的道長。 經(jīng)常有香客問我捷绑,道長韩脑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任粹污,我火速辦了婚禮段多,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘壮吩。我一直安慰自己进苍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布鸭叙。 她就那樣靜靜地躺著觉啊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沈贝。 梳的紋絲不亂的頭發(fā)上杠人,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音宋下,去河邊找鬼嗡善。 笑死,一個(gè)胖子當(dāng)著我的面吹牛学歧,可吹牛的內(nèi)容都是我干的罩引。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼撩满,長吁一口氣:“原來是場噩夢啊……” “哼蜒程!你這毒婦竟也來了绅你?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤昭躺,失蹤者是張志新(化名)和其女友劉穎忌锯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體领炫,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡偶垮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帝洪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片似舵。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖葱峡,靈堂內(nèi)的尸體忽然破棺而出砚哗,到底是詐尸還是另有隱情,我是刑警寧澤砰奕,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布蛛芥,位于F島的核電站,受9級特大地震影響军援,放射性物質(zhì)發(fā)生泄漏仅淑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一胸哥、第九天 我趴在偏房一處隱蔽的房頂上張望涯竟。 院中可真熱鬧,春花似錦空厌、人聲如沸庐船。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽醉鳖。三九已至普办,卻和暖如春萎坷,著一層夾襖步出監(jiān)牢的瞬間菩浙,已是汗流浹背聚凹。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工预鬓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昂勒,地道東北人勇哗。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓杠茬,卻偏偏與公主長得像琳拨,于是被迫代替她去往敵國和親瞭恰。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容