首頁 >微信小程式 >小程式開發 >小程式用canvas繪製海報的做法

小程式用canvas繪製海報的做法

hzc
hzc轉載
2020-06-17 09:18:314709瀏覽

2020年第一篇文章,年初忙著複習刷題一直沒空去寫東西,書看的越多感覺越技不如人,始終徘徊在小菜雞的行列中,最近項目裡正好有一個canvas的業務,突然又燃起了我一個UI前端的火種,記下了踩坑和思考。

踩坑

問題1:為什麼在canvas上畫圖片模糊?

在canvas上繪製圖片/文字的時候,我們設定canvas:375*667的寬高,會發現繪製出來的圖片很模糊,感覺像是一張解析度很差的圖片,文字看起來也會有疊影。

小程式用canvas繪製海報的做法

注意:物理像素是指手機螢幕上顯示的最小單元,而裝置獨立像素(邏輯像素)電腦裝置中的一個點,css 中設定的像素指的就是該像素。

原因:在前端開發中我們知道一個屬性叫做devicePixelRatio(裝置像素比),該屬性決定了在渲染介面時會用幾個(通常是2個)物理像素來渲染一個裝置獨立像素。

舉個例,一張100*100像素大小的圖片,在retina螢幕下,會用2個像素點去渲染圖片的一個像素點,相當於圖片放大了一倍,因此圖片會變得模糊,這也是1px在retina 螢幕上變粗的原因。

小程式用canvas繪製海報的做法

解決: 將canvas-width和canvas-height都放大2倍,在透過style將canvas的顯示width,height縮小2 倍.

例如:

<canvas width="320" height="180" style="width:160px;height:90px;"></canvas>

問題2:如何處理px和rpx的轉換?

rpx是小程式里特有的尺寸單位,可以依照螢幕的寬度進行自適應,而在iphone6/iphonex上,1rpx等於不同的px。所以很可能會導致在不同手機下,你的canvas展示不一致。

在繪製海報的之前,我們拿到的設計稿一般都是基於iphone6的2倍圖。而且從上一個問題的解決,我們知道canvas的大小也是2倍的,所以我們可以直接量取2倍圖的設計稿直接繪製canvas,而尺寸需要注意一下rpxtoPx.

/**
   * 
   * @param {*} rpx 
   * @param {*} int  //是否变成整数
   factor => 0.5 //iphone6
   pixelRatio => 2 像素比
   */
toPx(rpx, int) {
    if (int) {
      return parseInt(rpx * this.factor * this.pixelRatio)
    }
    return rpx * this.factor * this.pixelRatio
  }

問題3 :關於canvasContext.measureText計算純數字的時候手機上為0

在小程式中提供this.ctx.measureText(text).width去計算文字的長度,但是如果你全數字 的話,你會發現該API永遠都算成0.所以,最後採用類比measureText方法去計算文字長度。

measureText(text, fontSize = 10) {
    text = String(text)
    text = text.split('')
    let width = 0
    text.forEach(function(item) {
      if (/[a-zA-Z]/.test(item)) {
        width += 7
      } else if (/[0-9]/.test(item)) {
        width += 5.5
      } else if (/\./.test(item)) {
        width += 2.7
      } else if (/-/.test(item)) {
        width += 3.25
      } else if (/[\u4e00-\u9fa5]/.test(item)) { // 中文匹配
        width += 10
      } else if (/\(|\)/.test(item)) {
        width += 3.73
      } else if (/\s/.test(item)) {
        width += 2.5
      } else if (/%/.test(item)) {
        width += 8
      } else {
        width += 10
      }
    })
    return width * fontSize / 10
  }

問題4:如何保證一行字體的居中展示?多行呢?

字體的如果過長,會超出canvas畫布,造成繪製難看,這個時候我們就應該讓超出的部分變成...你可以設定一個width並且循環計算出文字的寬度,如果超出則利用substring截取後補充...即可。

let fillText=''
let width = 350
for (let i = 0; i <= text.length - 1; i++) { // 将文字转为数组,一行文字一个元素
        fillText = fillText + text[i]
        // 判断截断的位置
        if (this.measureText(fillText, this.toPx(fontSize, true)) >= width) {
          if (line === lineNum) {
            if (i !== text.length - 1) {
              fillText = fillText.substring(0, fillText.length - 1) + '...'
            }
          }
          if (line <= lineNum) {
            textArr.push(fillText)
          }
          fillText = &#39;&#39;
          line++
        } else {
          if (line <= lineNum) {
            if (i === text.length - 1) {
              textArr.push(fillText)
            }
          }
        }
      }

文字劇中展示計算公式:

居中在canvas中可以用(canvas的寬度-文字寬度)/2 x (x為字體的x軸的推移)

let w = this.measureText(text, this.toPx(fontSize, true))
this.ctx.fillText(text, this.toPx((this.canvas.width - w) / 2 + x), this.toPx(y + (lineHeight || fontSize) * index))

問題5:在小程式中如何處理網路圖?

關於在小程式裡使用網路圖片,例如cdn上的圖片,是需要down到微信本地進行 LRU 管理,讓後續繪製同樣圖片時,節省下載時間。所以首先需要你在微信小程式的後台配置downloadFile合法域名,其次你可以在canvas繪製之前,最好提前去down圖片,等待圖片下載好了,再開始繪製,以避免一些繪製失敗的問題。

問題6:在 IDE 中可設定 base64 的圖片資料來繪製,但真機上無用?

先把 base64 轉換成 Uint8ClampedArray 格式。然後再透過 wx.canvasPutImageData(OBJECT, this) 繪製到畫布上,然後將畫布匯出為圖片。

問題6:如何畫出一個圓角圖片?

問題7:關於wx.canvasToTempFilePath

使用Canvas 繪圖成功後,直接呼叫該方法產生圖片,在IDE上沒有問題,但在真機上會出現產生的圖片不完整的情況,可以使用一個setTimeout來解決這個問題。

this.ctx.draw(false, () => {
        setTimeout(() => {
            Taro.canvasToTempFilePath({
              canvasId: 'canvasid',
              success: async(res) => {
                this.props.onSavePoster(res.tempFilePath)//回调事件
                // 清空画布
                this.ctx.clearRect(0, 0, canvas_width, canvas_height)
              },
              fail: (err) => {
                console.log(err)
              }
            }, this.$scope)
          }, time)
    })

問題8:關於canvasContext.font

fontsize 不能使用小數 如果設定 font 中字體大小部分包含小數,則會導致整個 font 設定無效。

問題9:安卓下字體渲染錯位?

小程式用canvas繪製海報的做法

這個問題出現在安卓手機上,ios表現正常。一開始看到這個問題,摸不著頭腦,為什麼有的正常居中有的卻往前了很多。後面發現是安卓下this.ctx.setTextAlign(textAlign) 預設是為center,所以導致了錯亂,改成left後就正常了。

問題10:繪製一個折線圖

小程式用canvas繪製海報的做法

#利用canvas繪製一個簡單的折線圖,只需要利用lineTo moveTo兩個API將點連接即可。利用createLinearGradient繪製陰影。

思考

思考1:用json配置表產生海報的限制

#現在的海報產生只需要依照設計稿去量取尺寸就可以,但是量取的過程還是很繁瑣的,在設計稿量不到的地方還需要手動微調。 後續還可以做一個web端使用拖曳的方式去完成設計稿的事情,自動產生json應用到小程式的海報上。

思考2:後端產生海報的限制

海報一開始是後端同學生成的,優點是不需要前端繪製時間,也不需要去踩微信API的坑,接口返回拿到url即可展示,但是在後端產生出來的效果不佳,畢竟這種工作更加前端一些。

思考3:前端生成海報的限制

前端生成海報的時候我發現耗時更長,包括圖片的下載本地而且還需要給安卓一個特意寫一個setTimeout去確保繪製正常。各種相容性問題、手機的dpr、安卓和ios等不間斷彩蛋踩到你頭禿~ 哈哈哈哈~

彩蛋

採用了最新的canvas-2d背景圖確無法繪製全部?

在canvas開發的過程中,小程式裡一直有一束微光提醒我。

小程式用canvas繪製海報的做法

我也試了試最新的canvas2d的api,的確同步了web端,寫法也更流暢,在開發者工具中看是一切正常,跑在手機上則,只顯示寬度的一半在各種機型下測試也是一樣。

小程式用canvas繪製海報的做法

後面改成原始的canvas就又好了。 。 。具體原因也還沒在微信社群裡找到,後續迭代升級的時候再研究阿吧啊吧啊。

小程式用canvas繪製海報的做法

推薦教學:《JS教學

以上是小程式用canvas繪製海報的做法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除