小程序 canvas 生成海報 一次搞掂

小程序 canvas 生成海報 - 解決屏幕圖片失真,解決沒法使用外網圖片

源代碼在最下方javascript

最終結果

2019-04-30090218

canvas(畫布) 元素用於在網頁上繪製圖形。畫布是一個矩形區域,您能夠控制其每一像素。canvas 擁有多種繪製路徑、矩形、圓形、字符以及添加圖像的方法。css

注意

須要注意的是,目前的canvas能夠簡單分爲兩種。一種是傳統網頁中的canvas,一種是小程序中的canvas。二者的功能是徹底同樣的。只是標籤的樣式,和 api 略有區別而已。目前咱們主要講解小程序中的canvas。html

canvas 的應用場景

  1. 在線遊戲java

  2. 在線圖表git

  3. 頁面特效github

  4. 廣告canvas

  5. 圖片合成 小程序中常見小程序

    1. 點我加速api


      141e77ea75e046709e45115209ba7d9
    2. 頭像紅旗bash


      u=1152834417,249646381&fm=11&gp=0

    3. 海報日曆


      ceeedd30772bdcbe211c7852f701a4a
    4. 其餘


    1569634970242

簡單體驗

咱們來畫一條直線

在canvas中,把畫直線的步驟分解爲如下幾步:

  1. 編寫標籤
  2. 獲取畫布實例
  3. 定起點
  4. 鏈接終點
  5. 連線 (也叫描邊)
  6. 上色

編寫標籤

默認的寬高 爲 300px * 150 px

不一樣於普通的標籤,必需要提供一個屬性 canvas-id,用於在 js中獲取該對象(不是dom對象!!!)

<canvas canvas-id="firstCanvas"></canvas>
複製代碼

獲取畫布實例

經過 canvas-id 來獲取

該實例 不是dom元素,能夠理解爲另外一種對象如 Math Date String等便可

index.js

Page({
  onLoad() {
    // 1 獲取畫布上下文對象
    const context = wx.createCanvasContext("firstCanvas");
    console.log(context);
  }
})
複製代碼

點起點

在canvas中,存在一個座標系 以下圖:

咱們在canvas中所講的座標都是相對於canvas內部座標而言

繪圖1

定個起點

// 定起點
    context.moveTo(10, 10);
複製代碼

定終點

// 定終點
	context.lineTo(300,150);
複製代碼

連線

// 連線
    context.stroke();
複製代碼

上色

// 上色
    context.draw();
複製代碼

完整代碼

index.wxml

<!-- 1 寫標籤 -->
<canvas canvas-id="firstCanvas"></canvas>
複製代碼

index.js

Page({
  onLoad() {
    // 2 獲取畫布上下文對象
    const context = wx.createCanvasContext("firstCanvas");
    // 3 定起點
    context.moveTo(10, 10);
    // 4 定終點
    context.lineTo(300,150);
    // 5 連線
    context.stroke();
    // 6 上色
    context.draw();
  }
})
複製代碼

效果

1569657291546

內置的其餘規則圖形

canvas中還封裝了畫規則圖形的方法,如:

  1. 畫空心的矩形

  2. 畫圓弧

  3. 畫實心的矩形

  4. 畫文字(把字符串畫上去)

畫矩形

CanvasContext.strokeRect(number x, number y, number width, number height)

CanvasContext.strokeRect(畫在畫布的X,畫在畫布的Y,畫多寬,畫多高)

// 1 獲取畫布上下文對象
    const context = wx.createCanvasContext("firstCanvas");
    // 2 調用canvas內置的畫「矩形」的方法
    context.strokeRect(10, 10, 100, 100);
    // 3 上色 
    context.draw();
複製代碼

效果

1569657340868

畫圓弧

CanvasContext.arc(number x, number y, number r, number sAngle, number eAngle, boolean counterclockwise)

CanvasContext.arc(圓心的橫座標X,圓心的縱座標Y, 半徑的長度, 開始的弧度, 結束的弧度, ?是否反向來畫)

代碼

drawArc() {
    // 1 獲取畫布上下文對象
    const context = wx.createCanvasContext("firstCanvas");
    // context.arc(圓心的橫座標X,圓心的縱座標Y, 半徑的長度, 開始的弧度, 結束的弧度);
    // 2 調用內置的畫 「圓弧」 的方法
    context.arc(100, 100, 100, this.angleToArc(0), this.angleToArc(90));
    // 3 開始描邊
    context.stroke();
    // 4 上色
    context.draw();
  },
  /** * 將角度轉爲弧度 * @param {number} angle 角度 */
  angleToArc(angle) {
    return angle * Math.PI / 180;
  }
複製代碼

效果

1569657957559

畫實心的矩形

CanvasContext.fillRect(number x, number y, number width, number height)

// 1 獲取畫布上下文對象
    const context = wx.createCanvasContext("firstCanvas");
    // 2 調用canvas內置的 畫填充 「矩形」的方法
    context.fillRect(10, 10, 100, 100);
    // 3 上色 
    context.draw();
複製代碼

效果

1569658217231

畫文字

CanvasContext.strokeText(string text, number x, number y, number maxWidth)

CanvasContext.strokeText(要繪製的文本, 文本起始點的 x 軸座標, number y, 須要繪製的最大寬度,可選)

代碼

// 1 獲取畫布上下文對象
    const context = wx.createCanvasContext("firstCanvas");
    // 2 畫 「文字」
    context.strokeText("hello world", 100, 100);
    // 3 上色 
    context.draw();
複製代碼

效果

1569658659539


設置樣式

通過以上的演示咱們也發現,線條的顏色一直是黑色,這確定是沒法知足咱們騷跳的心的。如今來學習一下關於設置canvas線條樣式相關API。

  1. 設置線條顏色
  2. 設置線條粗細
  3. 設置填充顏色
  4. 設置文本大小

設置線條顏色

**特別要注意 **,setStrokeStyle是個函數,1.9.90版本後中止維護,使用如下的方式來修改。

  1. CanvasContext.setStrokeStyle("red") 已過期,不推薦
  2. CanvasContext.strokeStyle="red"; 正解

代碼

const context = wx.createCanvasContext("firstCanvas");
    context.moveTo(10, 10);
    context.lineTo(300, 150);
    // 5 修改顏色 須要在stroke以前修改
    context.strokeStyle = "red";
    context.stroke();
    context.draw();
複製代碼

效果

1569663241332


設置線條粗細

**特別要注意 **,setLineWidth 是個函數,1.9.90版本後中止維護,使用如下的方式來修改。

  1. CanvasContext.setLineWidth(20) 已過期,不推薦
  2. CanvasContext.lineWidth=20; 正解

代碼

const context = wx.createCanvasContext("firstCanvas");
    context.moveTo(10, 10);
    context.lineTo(300, 150);
    // 設置線條寬度
    context.lineWidth = 20;
    context.stroke();
    context.draw();
複製代碼

效果

1569663745450


設置填充顏色

**特別要注意 **,setFillStyle 是個函數,1.9.90版本後中止維護,使用如下的方式來修改。

  1. CanvasContext.setFillStyle("red") 已過期,不推薦
  2. CanvasContext.fillStyle="red"; 正解

代碼

const context = wx.createCanvasContext("firstCanvas");
    // 設置填充顏色
    context.fillStyle = "red";
    context.fillRect(10, 10, 100, 100);
    context.draw();
複製代碼

效果

1569663956116


設置文本大小

**特別要注意 **,setFontSize 是個函數,1.9.90版本後中止維護,使用如下的方式來修改。

  1. CanvasContext.setFontSize("20") 已過期,不推薦
  2. CanvasContext.font="sans-serif"; 正解
  3. font 當前字體樣式的屬性。符合 CSS font 語法 的 DOMString 字符串,至少須要提供字體大小和字體族名。默認值爲 10px sans-serif。

代碼

const context = wx.createCanvasContext("firstCanvas");
    // 必需要同時提供 字號 和 字體
    context.font="10px sans-serif";
    context.strokeText("10px", 10, 10);
    // 必需要同時提供 字號 和 字體
    context.font="50px sans-serif";
    context.strokeText("50px", 50, 100);
    // 必需要同時提供 字號 和 字體
    context.font="80px sans-serif";
    context.strokeText("80px", 80, 180);
    context.draw();
複製代碼

效果

1569664436438

進階

在本環節主要講解稍微複雜一點的功能。要實現如下功能

可是須要先作一點技術鋪墊

主要用到的api有:

  1. 獲取系統信息
  2. 選擇相冊圖片
  3. 獲取網絡圖片信息
  4. canvas 描繪 圖片到畫布上
  5. 將畫布保存成一張圖片
  6. 將圖片下載到本地

1569678652709

基本API

如下api是實現以上案例所必須的

獲取系統信息

獲取屏幕大小、設備像素比等

代碼

wx.getSystemInfo({
  success (res) {
    console.log(res.model)
    console.log(res.pixelRatio)
    console.log(res.windowWidth)
    console.log(res.windowHeight)
    console.log(res.language)
    console.log(res.version)
    console.log(res.platform)
  }
})
複製代碼

選擇相冊圖片

從本地相冊選擇圖片或使用相機拍照

代碼

wx.chooseImage({
  count: 1,// 最多能夠選擇的圖片張數
  sizeType: ['original', 'compressed'],// 所選的圖片的尺寸
  sourceType: ['album', 'camera'],// 選擇圖片的來源
  success (res) {
    // tempFilePath能夠做爲img標籤的src屬性顯示圖片
    const tempFilePaths = res.tempFilePaths
  }
})
複製代碼

代碼

wx.getSystemInfo({
  success (res) {
    console.log(res.model)
    console.log(res.pixelRatio)
    console.log(res.windowWidth)
    console.log(res.windowHeight)
    console.log(res.language)
    console.log(res.version)
    console.log(res.platform)
  }
})
複製代碼

獲取網絡圖片信息

獲取圖片信息。網絡圖片需先配置download域名才能生效。

canvas提供了將圖片畫到畫布上的功能,可是要求所提供的圖片必須是外網下的圖片

所以能夠藉助該方法將網絡圖片變成本地圖片,同時返回該圖片的信息

代碼

wx.getImageInfo({
  src: 'cloud://c-73e071.632d-c-73e071/92637.jpg',
  success (res) {
    console.log(res.width)
    console.log(res.height)
  }
})
複製代碼

繪製圖像到畫布

不能使用本地圖片,要使用外網圖片的 必需要先 使用 wx.getImageInfo 下載到本地

有三個版本的寫法:

  • drawImage(imageResource, dx, dy)
  • drawImage(imageResource, dx, dy, dWidth, dHeight)
  • drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
  • 說明drawImage(圖片路徑, 原圖的x, 原圖的y, 原圖的寬度, 原圖的高度, 畫布的x, 畫布的y, 畫多寬, 畫多高)

代碼

context.drawImage('xxxx.jpg', 0, 0,100, 100);
複製代碼

將畫布保存成一張圖片

draw() 回調裏調用該方法才能保證圖片導出成功

代碼

wx.canvasToTempFilePath({
  x: 100,
  y: 200,
  width: 50,
  height: 50,
  destWidth: 100,
  destHeight: 100,
  canvasId: 'myCanvas',
  success(res) {
    console.log(res.tempFilePath)
  }
})
複製代碼

將圖片下載到本地

保存圖片到系統相冊

代碼

wx.saveImageToPhotosAlbum({
  success(res) { }
})
複製代碼

案例實現

其實要實現同樣案例,最麻煩的不是這些API的調用,而是如何根據不一樣的圖片,合成比例合適不模糊的圖片

爲何說比例合適

由於在canvas中,只支持 px 單位,那麼在使用javascript來描繪圖片時,就不存在 rpxvw%這些相對單位了。只能依靠手動來計算。如,在 canvas中,畫出一個大小爲 屏幕寬的一半 屏幕高的一半的矩形?

爲何說不模糊

問題的緣由仍是由於 手機的屏幕 都是高清屏,具體的緣由能夠參照 連接

如咱們想要生成圖片大小爲 100px * 100px,那麼就須要將 canvas的大小設置爲 width = 圖片的寬度 * 設備像素比

height = 圖片的高度 * 設備像素比

文件目錄

  1. index 首頁
  2. result 合成圖片的頁面

1569681221628

首頁 index

index

1569681395410

pages/index/index.wxml

<!-- 用來顯示 被選擇的圖片的 -->
<image mode="widthFix" src="{{src}}"></image>
<!-- 選擇相冊圖片 -->
<button bindtap="handleTap">選擇圖片</button>
<!-- 跳轉到 結果頁面 -->
<button bindtap="handleCreateFlag">生成小紅旗</button>
複製代碼

pages/index/index.js

主要實現3個功能

  1. 點擊 「選擇圖片」 將選擇的圖片打印到頁面上
  2. 將被 選擇的圖片 顯示的頁面上
  3. 點擊 「生成紅旗」,跳轉到結果頁面(在結果頁面完成生成)
Page({
  data: {
    src: ""
  },
  // 選擇圖片
  handleTap() {
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success: (result) => {
        this.setData({
          src: result.tempFilePaths[0]
        })
        // 保存圖片路徑
        wx.setStorageSync('src', this.data.src);
      }
    });
  },
  // 生成紅旗
  handleCreateFlag() {
    // 跳轉到結果頁面
    wx.navigateTo({
      url: '/pages/result/index'
    });
  }
})
複製代碼

結果頁面 result

result

1569681567259

result/index.wxml

3個標籤

  1. canvas 標籤,經過定位將其隱藏
  2. image 標籤,用來顯示合成的圖片
  3. button 標籤,用來點擊 下載圖片
<!-- canvas 標籤-->
<canvas class="cas" style="width:{{canvasWidth}};height:{{canvasHeight}};" canvas-id="firstCanvas"></canvas>
<!-- 用來顯示 合成成功的圖片 -->
<image class="res_image" mode="widthFix" src="{{resSrc}}"></image>
<!-- 點擊下載圖片 -->
<button bindtap="handleSave">下載圖片</button>
複製代碼

result/index.wxss

兩個樣式

  1. 把canvas藏起來(由於是 原生組件,因此它的層級比通常的標籤都要高(定位+zindex也沒法解決))
  2. 設置圖片標籤的樣式
page {
  overflow-x: hidden;
  overflow-y: auto;
  width: 100vw;
  height: 100vh;
}
.cas {
  position: absolute;
  top: 1000vw;
  left: 1000vh;
  z-index: -1;
  opacity: 0;
}
.res_image {
  width: 100%;
  display: block;
}

複製代碼

result/index.js

易錯點:

  1. 外網的圖片,須要先將圖片服務器添加到白名單中(不然真機調試會失敗)
  2. 沒有動態設置 canvas的寬和高(參照第2九、31行)
import regeneratorRuntime from '../../lib/runtime/runtime';
import { getImageInfo, canvasToTempFilePath, saveImageToPhotosAlbum } from "../../wxAsync/index.js";
Page({
  data: {
    // 默認的canvas的寬度
    canvasWidth: 1,
    // 默認的canvas高度
    canvasHeight: 1,
    // 最終生成的圖片路徑
    resSrc: ""
  },
  // 全局變量
  saveImgSrc: "",
  async onLoad() {
    // 紅旗圖片
    const flagSrc = "https://632d-c-73e071-1252056196.tcb.qcloud.la/3434.jpg?sign=a4f1c2106d1e61551829c2f99820c0ba&t=1569678566";
    // const baseSrc = "https://632d-c-73e071-1252056196.tcb.qcloud.la/92637.jpg?sign=8952d1eaa69a35510418fe25dc25d6c5&t=1569678606";
    // 上個頁面選擇的圖片路徑 柯南圖片
    const baseSrc = wx.getStorageSync("src");
    // 設備像素比
    const { pixelRatio } = wx.getSystemInfoSync();

    // 獲取 畫布實例
    const context = wx.createCanvasContext('firstCanvas');
    console.log(context);
    // 下載到本地的 柯南圖片
    const baseImg = await getImageInfo(baseSrc);
    // 下載到本地的 紅旗圖片
    const flagImg = await getImageInfo(flagSrc);
    // 將canvas的寬度設置中 圖片的寬度
    const canvasWidth = baseImg.width + "px";
    // 將canvas的寬度設置中 圖片的高度
    const canvasHeight = baseImg.height + "px";
    // setData 函數用於將數據從邏輯層發送到視圖層(異步),同時改變對應的 this.data 的值(同步)。
    // 所以須要將 描繪 圖片的步驟寫在回調中,不然 真機調試有bug!
    this.setData({ canvasWidth, canvasHeight }, () => {
      // 若是個別機型出現圖片失敗錯誤,能夠加上定時器。
      setTimeout(() => {
        // 先將柯南 描繪到畫布上
        context.drawImage(baseImg.path, 0, 0, baseImg.width, baseImg.height);
        // 把紅旗 描繪到畫布上
        context.drawImage(flagImg.path, baseImg.width - (pixelRatio * 50), baseImg.height - (pixelRatio * 50), (pixelRatio * 50), (pixelRatio * 50));
        context.draw(true, async () => {
          // 將 畫布生成 成圖片
          const res1 = await canvasToTempFilePath({
            canvasId: "firstCanvas"
          });
          // 讓圖片顯示 合成後的效果
          this.setData({ resSrc: res1.tempFilePath })
          // 保存起來,當點擊保存圖片時調用
          this.saveImgSrc = res1.tempFilePath;
        });
      }, 100);
    });
  },

  // 點擊保存圖片
  handleSave() {
    saveImageToPhotosAlbum(this.saveImgSrc);
  }
})

複製代碼

github地址

https://github.com/itcastWsy/AppletPoster.git
複製代碼
相關文章
相關標籤/搜索