用戶
 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

掃一掃,登錄網站

小程序社區 首頁 教程 查看內容

汉诺威96vs沃夫斯堡:小程序 canvas 生成海報 - 解決屏幕圖片失真,解決無法使用外網圖片 ...

汉诺威96升级 www.wikuss.com.cn Rolan 2019-9-30 00:58

源代碼在最下方 最終結果 canvas(畫布) 元素用于在網頁上繪制圖形?;際且桓鼉匭吻?,您可以控制其每一像素。canvas 擁有多種繪制路徑、矩形、圓形、字符以及添加圖像的方法。 注意 需要注意的是,目前的ca ...

源代碼在最下方

最終結果

canvas(畫布) 元素用于在網頁上繪制圖形?;際且桓鼉匭吻?,您可以控制其每一像素。canvas 擁有多種繪制路徑、矩形、圓形、字符以及添加圖像的方法。

注意

需要注意的是,目前的canvas可以簡單分為兩種。一種是傳統網頁中的canvas,一種是汉诺威96升级中的canvas。兩者的功能是完全一樣的。只是標簽的樣式,和 api 略有區別而已。目前我們主要講解小程序中的canvas。

canvas 的應用場景

  1. 在線游戲

  2. 在線圖表

  3. 頁面特效

  4. 廣告

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

    1. 點我加速


    2. 頭像紅旗


    3. 海報日歷


    4. 其他



簡單體驗

我們來畫一條直線

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

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

編寫標簽

默認的寬高 為 300px * 150 px

不同于普通的標簽,必須要提供一個屬性 canvas-id,用于在 js中獲取該對象(不是dom對象?。。。?/p>

<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內部坐標而言

定個起點

    // 定起點
    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();
  }
})
復制代碼

效果

內置的其他規則圖形

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();
復制代碼

效果

畫圓弧

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;
  }
復制代碼

效果

畫實心的矩形

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();
復制代碼

效果

畫文字

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();
復制代碼

效果


設置線條粗細

**特別要注意 **,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();
復制代碼

效果


設置填充顏色

**特別要注意 **,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();
復制代碼

效果


設置文本大小

**特別要注意 **,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();
復制代碼

效果

進階

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

但是需要先做一點技術鋪墊

主要用到的api有:

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

基本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來描繪圖片時,就不存在 rpx、vw、%這些相對單位了。只能依靠手動來計算。如,在 canvas中,畫出一個大小為 屏幕寬的一半 屏幕高的一半的矩形?

為什么說不模糊

問題的原因還是因為 手機的屏幕 都是高清屏,具體的原因可以參照 鏈接

如我們想要生成圖片大小為 100px * 100px,那么就需要將 canvas的大小設置為 width = 圖片的寬度 * 設備像素比

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

文件目錄

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

首頁 index

index

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

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的寬和高(參照第29、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

分享至 : QQ空間
收藏
原作者: 那個不懂 來自: 掘金