canvas 的基本概念
什麼是 canvas
這裡先簡單說一下總結,對於 2D 的 canvas 在網頁頁面中,可以直接視為透過 JavaScript 動態操作的圖片,3D canvas 不在此文的泛圍中。
維基百科 – Canvas (HTML元素) 中部份內容提到如下。
Canvas 是一個 HTML 元素。可以利用 JavaScript 程式語言在該元素上繪圖,常見的應用包括繪製圖形及文字、影像處理、遊戲及動畫製作。
<canvas> 元素通常也被 WebGL 用來在網頁上顯示使用硬體加速繪製的 3D 圖形。
什麼是 WebGL?
WebGL (Web Graphics Library) 是一個透過瀏覽器渲染3D及2D圖像的 JavaScript API ,且不需要安裝任何插件。 WebGL 透過與OpenGL ES 2.0緊密連結的API,將3D圖像帶入HTML5中,並可透過canvas元素呈現於瀏覽器中。
出處:MDN 技術文件說明
canvas VS SVG
非常強大的 SVG(Scalable Vector Graphics,可伸縮的矢量標記圖)在早期是前端開發配合使用 div + css 來完成繪圖,沒有 canvas 的時候,在瀏覽器繪製圖形是比較複雜的,而在 canvas 出現之後,繪製 2D 圖形相對變得容易了。
canvas 本質上是一個與解析度相關的位圖畫布,也就註定了在不同解析度下,canvas 繪製的內容顯示的時候會有所不同。此外,canvas 繪製的內容不屬於任何 DOM 元素,在瀏覽器的元素查看器中也找不到,那自然無法檢測滑鼠點擊了canvas中的哪個內容,很顯然這兩方面,canvas 都是不如 SVG 的。
如果使用 CSS 設置 canvas 元素的尺寸,那可能會導致繪製出來的圖形變得扭曲,如長方形變正方形,圓形變橢圓等,這是因為畫布尺寸和元素尺寸是不一樣的,畫布會自動適應元素的尺寸,如果二者是成比例的,那麼畫布就會等比例縮放,不會出現扭曲。
SVG 有些問題:
- SVG 基於 XML,裡面的元素都認為 DOM 元素啟用 DOM 操作,SVG 每個繪製的圖像均被視為對象,若 SVG 對象屬性變化,瀏覽器會自動重現圖形。
- 過度使用 DOM 的都會變得很慢,複雜的 SVG 會導致渲染速度變慢。像地圖這類的應用首選是 SVG。
- 瀏覽器因窗口發生變化的重新渲染、元素尺寸位置變化、字體變化等等。
- 即使可以使用 DOM 操作,但操作的成本是更高的( DOM 和 JS 的實現是分開的)。
canvas 於 HTML 中的預設大小(寬 300 x 高 150 px)
MDN – Canvas 教學文件中部份內容提到如下。
<canvas> 是一個 HTML 元素,我們可以利用程式腳本在這個元素上繪圖(通常是用 JavaScript)。除了繪圖,我們還可以合成圖片或做一些簡單(或是不那麼簡單)的動畫。
<canvas> 元素是 WhatWG Web applications 1.0(也就是 HTML5)規範的一部分,目前所有主流的瀏覽器都已支援。
現今所有主流的瀏覽器都有支援。
預設的畫布大小是 300px * 150px(寬 * 高)。
對於 MDN – Canvas 教學文件 連結,有許多的 API 基本用法與觀念,值得一看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> body { padding: 10px; } #myCanvas { background-color: #000; } </style> </head> <body> <canvas id='myCanvas'></canvas> </body> </html> |
See the Pen canvas 於 HTML 中的預設大小(寬 300 x 高 150 px) by Jimmy_Wu (@Jimmy_Wu) on CodePen.
在 HTML 程式碼中只有單純的一個 <canvas id='myCanvas'> 元素,在 CSS 的部份只單純在 #myCanvas 元素上給了一個黑底背景色塊,在瀏覽器下都只有呈現出一個 300 x 150 大小的區塊,相較於 HTML 中其他相關的區塊與行內元素,嚴格來說可以算是獨立出來的一個元素所給予特定的預設樣式。
為何會提到 <canvas> 元素預設大小呢?這裡後續會提到,某些 canvas API 中的方法使用下,如果沒有給予特定的設定或是參數,會直接段元素預設大小的面積運算處理,而在瀏覽器做渲染畫面動作時,是會和 CSS 分開處理運算。
canvas 的容器特性,canvas 元素尺寸分標籤屬性與 CSS 二種方式定義,CSS 樣式定義尺寸會讓 canvas 產生變形
canvas 元素在前面提到,在瀏覽器中有預設尺寸 300 x 150 xp,這裡先暫提一下後面透過實作了解影響的部份。
以網頁控制元素尺寸,主要可分為二種:
- 元素標籤的高寬度屬性。
- CSS 的 style 或 class 樣式操作。
對於二種方式用於 canvas 元素會產不同影響,可先理解成 canvas 是較特別的 <img> 標籤,畫布大小是由 JS 定義,若由 class 或是 style 定義尺寸的話,會產生變形。
以下以四個部份,呈現出 canvas 元素在 JS 與 CSS 定義尺寸後的差別。
See the Pen Untitled by Jimmy_Wu (@Jimmy_Wu) on CodePen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>HTML 基本自定樣版</title> <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css' integrity='sha512-GQGU0fMMi238uA+a/bdWJfpUGKUkBdgfFdgBm72SUQ6BeyWjoY/ton0tEjH+OSH9iP4Dfh+7HM0I9f5eR0L/4w==' crossorigin='anonymous'/> <style> .w-400 { width: 400px; } .h-400 { height: 400px; } .bg-danger { background: red; } .resSize { max-width: 100%; } </style> </head> <body> <div class="container"> <div class="row text-center"> <div class="col-sm-6"> <img src="https://picsum.photos/id/237/200/300" /> <h4>img 元素引入圖片,直用圖片尺寸不加樣式與操作</h4> </div> <div class="col-sm-6"> <canvas class="js-myCanvas bg-danger resSize"></canvas> <h4>圖片引入 canvas 畫布,canvas 元素為瀏覽器預設尺寸 (300 x 150 px)</h4> </div> <div class="col-sm-6"> <canvas class="js-myCanvas bg-danger w-400 h-400 resSize"></canvas> <h4>圖片引入 canvas 畫布,透過樣式改變元素尺寸,canvas 畫布內的圖片繪製會跟著變形</h4> </div> <div class="col-sm-6"> <canvas class="js-myCanvas bg-danger resSize"></canvas> <h4>圖片引入 canvas 畫布,使用 JS 決定 canvas 元素的尺寸 (400 x 400 px),canvas 畫布內的圖片繪製不變形</h4> </div> </div> </div> <script> /** 線上開源圖庫 (https://picsum.photos/) * https://picsum.photos/id/237/200/300 */ let myCanvas = document.querySelectorAll('.js-myCanvas'); myCanvas.forEach((el) => { let ctx = el.getContext('2d'); let ctxImg = new Image(); ctxImg.src = 'https://picsum.photos/id/237/200/300'; ctxImg.onload = darw_ctxImg; function darw_ctxImg() { ctx.drawImage(ctxImg, 0, 0, ctxImg.width, ctxImg.height); console.log('ctxImg', ctxImg); } }); myCanvas[2].width = 400; myCanvas[2].height = 400; </script> </body> </html> |
canvas 元素透過 CSS 定義大小,如果沒有先透過標籤屬性的 width="" 或是 height="" 定義的話,會先以 canvas 的預設尺寸進行畫布內的繪製,也就是以 300 x 150 的尺寸來將置入的圖片繪製,而透過 CSS 再操作 canvas 元素尺寸,會以 canvas 所預設繪制出來的尺寸來進行變形處理,CSS 若有定義背景色,會於 canvas 元素以底色呈現,canvas 畫布本身預設為全透,除非有定義畫布的色彩。
canvas 的文字繪製
fillText() 填滿文字
1 | fillText(textToDraw, x, y [, maxWidth]) |
- textToDraw 文字內容
- x, y 指在 (x,y) 繪製填滿指定文字
- maxWidth 可繪製最大寬度
1 2 3 | var myCanvas = document.querySelector('#myCanvas'); const ctx = myCanvas.getContext('2d'); ctx.fillText("今天天氣好", 10, 100) |
See the Pen canvas 於 HTML 中的預設大小(寬 300 x 高 150 px) by Jimmy_Wu (@Jimmy_Wu) on CodePen.
以上例來說,透過 canvas 所繪的預設文字為黑色,級數大小非常的小,需要再進行設定。
.fillText() 方法,主要是針對宣告為 canvas 物件內入文字內容與定位。
ctx.font 與 ctx.fillStyle 自訂字型大小與色彩
1 2 3 4 5 | var myCanvas = document.querySelector("#myCanvas"); const ctx = myCanvas.getContext("2d"); ctx.font = "30px sans-serif"; // 字級與字型 ctx.fillStyle = "#00A0E9"; // 設定文字填滿顏色 ctx.fillText("今天天氣好", 10, 100); |
See the Pen canvas 文字繪製-宣告與基本的文字繪製-1 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
透過 ctx.font 屬性以賦值的方式 "30px sans-serif" 指定字級與字體,屬性值以字串設定於字體大小與字體,中間以空白字串隔開表示。
網頁預設字型與安全字體的基本知識可見 跨平台安全網頁字體 整理一文。
預載字型與圖片 (線上 webFont 字體實作與使用記錄)
使用 canvas 如果要引入線上的字體,因為繁體中文的字體需要較高的流量,又或是原本 google webFont 預設 link 引入方式,所使用的 rel="preconnect" 也會影像 JS 處理渲染畫面,需要先將字體資源完整引入後才執行 JS,才不會產生只以網頁安全字體呈現的情形產生。
Google webFont 預載字體:
- 使用方式內容 瀏覽器webFont與GoogleFont的設計和操作流程。
- preload、preconnect、prefetch 實作記錄說明, Google-webFont預載字體﹙HTML5-標籤rel:preload,preconnect,prefetch﹚一文。
其他參考來源:
IT 邦幫忙 -【Day06】Canvas-繪製文字
MDN – 使用canvas繪製文字 (API 的方法相關參數)。
繪製文字時因預設基線置底文字會偏上,透過 textBaseline 設定
textBaseline 預設值為 alphabetic (基線在文字底部),改成 top (基線在文字上部) 近一般文書排版
canvas 的文字預設為左上角開始計算,基線的部份預設為 alphabetic (中/英:字母的),基本上預設設定看到字母大寫的部份,都會底部開啟計算,也因為這預設 alphabetic 的關係,加上 canvas 座標開始計算也是由左至右 (正值向右疊加)、由上至下 (正值向下疊加) 的方式,如果一開始使用 .fillText() 繪製出文字第 2 和 3 的參數設定都為 0 的話,除了看不到到基線外,也只有幾個像是小寫的 g 有向下超過基線的比畫可以看的到外,大寫的字實基本上是看不到的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> <style> body { margin: 0; } </style> </head> <body> <canvas id="canvas" width="600" height="500"></canvas> <script> const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); ctx.font = "36px serif"; ctx.strokeStyle = "red"; /* 預設 ctx.textBaseline 為 alphabetic ----------------------------------------- */ ctx.moveTo(0, 0); ctx.lineTo(600, 0); ctx.stroke(); // ctx.textBaseline = "top"; // 修正預設的文字繪製 console.log( "ctx.textBaseline === 'alphabetic'", ctx.textBaseline === "alphabetic" ); // ctx.textBaseline === 'alphabetic' true ctx.fillText(`AaBbCcDdEeFfGg (預設為 {ctx.textBaseline})`, 0, 0); /* /預設 ctx.textBaseline 為 alphabetic ----------------------------------------- */ </script> </body> </html> |
See the Pen canvas-textBaseline–預設文字基線為 alphabetic by Jimmy_Wu (@Jimmy_Wu) on CodePen.
修正方式,將 ctx.textBaseline 的預設值改成 "top",基線調整到上方後也比較相近於一般的文書排版,此時印出在畫面的就會是 top 。
See the Pen canvas-textBaseline()–預設文字基線為 alphabetic by Jimmy_Wu (@Jimmy_Wu) on CodePen.
textBaseline 文字基線的 6 個設定
1 | ctx.textBaseline = "top" || "hanging" || "middle" || "alphabetic" || "ideographic" || "bottom"; |
以 MDN 官方文件加以修改,配合 canvas 基線與文字的定位產生繪製,透過 for 迴圈呈現出每個 textBaseline 設定,針對預設設定刻意多加垂直 10px 的座標值。
See the Pen canvas-textBaseline()–基線的預設與 6 個設定 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> <style> body { margin: 0; } </style> </head> <body> <canvas id="canvas" width="600" height="500"></canvas> <script> const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.font = '36px serif'; ctx.strokeStyle = 'red'; /* 預設 ctx.textBaseline 為 alphabetic ----------------------------------------- */ /* 刻意將基線定位多移 10xp */ ctx.moveTo(0, 10); ctx.lineTo(600, 10); ctx.stroke(); console.log("ctx.textBaseline === 'alphabetic'", ctx.textBaseline === 'alphabetic'); ctx.fillText(`AaBbCcDdEeFfGg (預設為 {ctx.textBaseline})`, 0, 10); /* /預設 ctx.textBaseline 為 alphabetic ----------------------------------------- */ const baselines = ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom']; baselines.forEach(function (baselineType, index) { ctx.textBaseline = baselineType; let y = 75 + index * 75; ctx.beginPath(); ctx.moveTo(0, y + 0.5); ctx.lineTo(600, y + 0.5); ctx.stroke(); ctx.fillText('AaBbCcDdEeFfGg (' + baselineType + ')', 0, y); }); </script> </body> </html> |
其他參考來源:
MDN – CanvasRenderingContext2D.textBaseline (CanvasRenderingContext2D.textBaseline 原理與說明)
Canvas.drawText繪製文字為什麼會偏上?
canvas 的文字換行與自動換行
特殊字元 \n
將字串換行
canvas 的繪製文字中的字串本質上是透過 JavaScript 的字串字面值 (String literals) 進型操作,在 JS 中的字串字串要換行的話,就需要以特殊字元 \n
的方式進行換行。
在使用 html 表單元素 input 或是 textarea 元素進行輸入字串,如果在 textarea 元素中給使用者輸入文字並透過 enter 字串換行,此時表單在字串換行的地方會以特殊字元 \n 做為字符,當再透過 JS 或是 HTML 解析後,呈現在畫面上就會產生換行。
特殊字元
\n
的換行的方式也不只限於 JS 中使用,包刮在終端機下指令所印出的字串內容,都可用特殊字元這樣的方式來將字串換行。
MDN – 字串裡的特殊字元文件連結 查看 \n 換行 (New line)。
canvas 的繪製文字使用 JavaScript 原型方式,自訂加入自動換行方法
在 canvas 的預設文字繪製,是沒有自動換行的功能 (或 API),簡單的理解就是字串除了透過 JS 操作特殊字元 \n
方式進行換行外,基本上就是無限的在 canvas 容器元素內,以字串的長度向右或向下進行增長的動作。
原先 canvas 沒有 API 的話,不表示就不能處理,這裡是使用 JS 的原型方式,在 CanvasRenderingContext2D 原型中掛上使用的方式,讓所有使用 canvas 的物件都能透過字串自動換行的方法進行自動判斷換行的長度,對應的條件除了 \n (特殊字元) 外,也與 ' ' (單一空白字元) 來做換行的動作。
例中故意以沒有空白字元的方式 加入wrapText方法,達到自動換行的排版方式, 進行操作,可見到字串在沒有 \n (特殊字元) 與 ' ' (單一空白字元) 的情形下,字串會穿出所繪製文字所定義的寬度,這樣的方式會相近於網頁排版的字串自動換行特性。
註:在沒有定義出所使用的字型,會因為瀏覽器預設安全字型的不同,對於自動換行所呈現的排版也會有些不同。
See the Pen canvas 繪製文字以 JavaScript 原型方式,自訂加入自動換行方法 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>canvas 繪製文字以 JavaScript 原型方式,自訂加入自動換行方法</title> <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> <style> body { margin: 0; } </style> </head> <body> <canvas id="myCanvas">您的瀏覽器不支援 canvas 標籤,請您更換瀏覽器!!</canvas> <script> /** 文字自動換行參考一: * 我正在使用JavaScript的原型功能,以便您可以從畫布上下文中調用該函數。 (17L) * http://hk.uwenku.com/question/p-sdmzzlfh-et.html */ CanvasRenderingContext2D.prototype.wrapText = function (text, x, y, maxWidth, lineHeight) { /** * @param text 文字字串、數值 * @param x 寬 * @param y 高 * @param maxWidth 最大寬 * @param lineHeight 行距 */ var lines = text.split('\n'); for (var i = 0; i < lines.length; i++) { var words = lines[i].split(' '); var line = ''; for (var n = 0; n < words.length; n++) { var testLine = line + words[n] + ' '; var metrics = this.measureText(testLine); var testWidth = metrics.width; if (testWidth > maxWidth && n > 0) { this.fillText(line, x, y); line = words[n] + ' '; y += lineHeight; } else { line = testLine; } } this.fillText(line, x, y); y += lineHeight; } }; let myCanvas = document.getElementById('myCanvas'); let ctx = myCanvas.getContext('2d'); // 指定渲染環境 2D myCanvas.width = 600; myCanvas.height = 600; ctx.fillStyle = 'rgb(120, 120, 120)'; ctx.fillRect(0, 0, myCanvas.width, myCanvas.height); /** 文字換行一:使用特殊字元 \n 將文字換行 * 字串裡的特殊字元 \n 換行 (https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Grammar_and_types#字串裡的特殊字元) * 預設繪製文字齊左 * 文字基線使用對文字上方 */ ctx.textBaseline = 'top'; // 基線使用上方 ctx.font = '30px sans-serif'; // 設定文字字型與大小 ctx.fillStyle = '#ffd823'; // 設定文字填滿顏色 ctx.strokeStyle = '#D50A17'; // 設定文字邊框 let textBorder = 10; // 文字換行邊界 ctx.wrapText('今天是\n星期一 \n星期一', 10, textBorder * 2, myCanvas.width - textBorder, 35); /** 文字換行二: * 繪製文字齊中 * 印出字串中多地方使用空白字串 ' ' 讓繪製文字進行自動換行 * \n 強制於 ',' 之後換行 */ ctx.textAlign = 'center'; ctx.font = '35px ' + 'Noto Serif SC'; // 設定文字字型 大小 ctx.fillStyle = '#00ff64'; // 設定文字填滿顏色 ctx.wrapText( 'aAbBcCdDeE 字串,透過 JS 原型方式, 加入wrapText方法,達到自動換行的排版方式,\n 這裡是使用 JS 特殊字元的換行方式進行換行', myCanvas.width / 2, 170, myCanvas.width - 70, 45 ); </script> </body> </html> |
canvas 文字繪製相關自動換行的文章:
canvas 的圖片繪製與 base64 轉換處理
圖片處理 .drawImage() 置入圖片到 canvas 中繪製 → .toDataURL() 方法轉出 canvas 成 base64 編碼的圖指定格式 (EX: "data:image/png;base64,iVBxxxxx" ) → 使用者下載出 base64 的圖檔,透過 file API 餺存下格式圖片檔 (EX: XXX.png 圖檔) 存於硬體等儲存空間中。
將來源圖片檔 (xxx.jpg…等格式檔案),以 .drawImage() 方法加入 canvas 繪製圖片
MDN – CanvasRenderingContext2D.drawImage()
1 2 3 | ctx.drawImage(image, dx, dy); ctx.drawImage(image, dx, dy, dWidth, dHeight); ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); |
使用 ctx.drawImage(image, dx, dy); 三個參數的方法,進行程式碼規劃,分別以動態操作 <img> 與 <canvas> 元素。。
See the Pen Untitled by Jimmy_Wu (@Jimmy_Wu) on CodePen.
canvas 繪製圖片於元素後,若圖片為跨域 (CORS) 連結,透過 crossorigin 設定讓之後的 .toDataURL() 方法順利轉出
較詳細可見 HTML5標簽的crossorigin屬性,提供元素CORS跨域設定 文章,針對非同網域 (CORS) 需在圖片上使用 crossorigin 的屬性設定,但瀏覽器所接收的網頁檔頭要是 access-control-allow-origin: * 再接 crossorigin="" 標籤設定才可以讓取下來的圖片給 canvas 轉換處理。
輸出成指定格式圖片 (jpeg 或 png 等…),以 HTMLCanvasElement.toDataURL() 方法,將 canvas 轉成 base64 編碼
這裡先簡單小結一下,將圖片以 canvas 轉成 base64 格式後,就可透過瀏覽器取得圖片檔案。
data URIs 可理解成檔案以一堆字串置於記憶體中用來使用的媒體檔
data URIs 是以 base64 格式編碼的方式,透過編碼將檔案以資料的型式來使用。
這樣說有點抽象,但可以先理解成編碼出來的一推字串,可不用見到檔案的實體資料 (不會在硬碟中存成檔案,直接是以字串置於記憶體中使用),就直接使用的方式。
canvas.toDataURL() 方法轉成 data URI 的 base64 編碼
1 | canvas.toDataURL(type, encoderOptions); |
type 選擇性 – 圖像格式的 DOMString. 預設為 image/png.
encoderOptions 選擇性 – 表示 image/jpeg 或是 image/webp 的圖像品質, 為 0 到 1 之間的 Number. 如果值不在上述範圍中, 將會使用預設值. 其他的會忽略.
MDN – HTMLCanvasElement.toDataURL() 文件提到方法內容如下:
HTMLCanvasElement.toDataURL() 方法回傳含有圖像和參數設置特定格式的 data URIs (預設 PNG). 回傳的圖像解析度為 96 dpi.
- 如果 canvas 的高度或是寬度為 0, 將會回傳字串 "data:,".
- 如果要求的圖像類型並非 image/png, 但是回傳的類型卻是 data:image/png, 表示要求的圖像類型並不支援.
- Chrome 也支援 image/webp 類型.
瀏覽器使用者端 canvas 或是 JavaScript 所繪製圖案,透過 canvas 的 HTMLCanvasElement.toDataURL() 方法,把 canvas 畫布中的圖案轉變成 base64 編碼格式的 png 圖檔以 Data URL 數據型式使用。
如果 canvas.toDataURL(); 不帶入參數直接使用方法,會是以 data:image/png 的格式轉出成 base64 圖片。
1 | <canvas id="canvas" width="5" height="5"></canvas> |
1 2 3 4 5 | var canvas = document.getElementById("canvas"); var dataURL = canvas.toDataURL(); console.log(dataURL); // "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNby // blAAAADElEQVQImWNgoBMAAABpAAFEI8ARAAAAAElFTkSuQmCC" |
HTMLCanvasElement.toDataURL() 方法 Deom 如下,使用 console.log() 查看轉成 base64 的結果。
See the Pen canvas 與 html img 使用 JavaScript 渲染 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
將 canvas 透過 .toDataURL() 方法轉出的 base64 做為路徑,指給網頁沒 src 的 img 標籤在瀏覽器呈現圖片
如果帶參數使用 .toDataURL(); 方法,可指定輸出的影像格式與影像品質等參數設定。
1 | canvas.toDataURL(type, encoderOptions); |
1 2 | var fullQuality = canvas.toDataURL("image/jpeg", 0.5); // data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ...9oADAMBAAIRAxEAPwD/AD/6AP/Z" |
See the Pen canvas 以 HTMLCanvasElement.toDataURL() 方法轉 base64 編碼 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
用JavaScript將Canvas內容轉化成圖片的方法 文章中介紹 (實作範例)
- 透過 <img id="ringoImage" alt="" src=" /wordpress/wp-content/uploads/2014/03/hacker.jpg " /> 的 .jpg 格式圖檔以 img 元素加入 DOM 結構。
- 再透過 JavaScript 以 #canvasHolder 動態產生 canvas 元素中介處理繪制圖片。
- 最後由 #pngHolder 將圖片由 .toDataURL() 方法,指定為 png 圖片格式的 base64 方式置於頁面連結 <img src="data:image/png;base64,iVBORw0KGgoA..."> 以 img 元素呈現畫頁面。
將畫布(canvas)圖像保存成本地圖片的方法 文章中介紹,整合 canvas2image.js, base64.js 二支插件 為主,並配合 .toDataURL() 的用法 (並帶有 canvas 轉出存成與轉換 png、bmp、jpeg 格式圖片檔實作範例可操作)。
如果要處理 base64 轉出的字串,要開頭的檔案類型等訊息移除,可以使用 .replace() 方法取出後方的二進位編碼。
12 var base64 = data:image/jpeg;base64,/9j/4gv4SUNDX1BST0ZJTEUAAQEAAAvoAAAAAAIAAABtbnRyUkd........base64 = obj.base64.replace(/^data:.*?;base64,/, ""); //使用這個方法就可以去除掉了
目前處理 File API map 位置 binary-to-String
File API map 流程圖連結 (上方圖片為連結轉存的圖片)
整理一下目前處理的流程,以附圖中的紅線就是處理的流程,已透過 base64 編碼成了是一組相似的二進制到文本(binary-to-text or binaryString)的編碼規則。
補充知識點 – base64 可處理的編碼 (Encode) 及解碼 (Decode) 方式:
MDN – Base64的編碼與解碼
JavaScript Base64 編碼(Encode)及解碼(Decode)
下載 base64 圖片檔案
以二個方式用來下載圖檔,透過點按行為的方式給使用者取得圖片。
概念與原理
二個方式的原理都差不多,canvas 由 base64 轉出成字串的型式 (binary-to-String),實際上就可以在瀏覽器上直接開啟圖檔,在頁面中產生出一個 html 的 a 標籤,以 .clcik (點按) 與 .download (下載) 行為,就可讓使用者點按或相關操作下,以下載的方式取得圖片檔案。
base64 圖片經由 .download (下載) 行為的過程,可先預設出存檔名稱,方便於瀏覽器下載方式直接以預設檔名的方式存檔,但如果有使用另存檔案的方式,會有介面呈現於瀏覽器的上,若使用者想在定義其他名稱,讓使用者另命名後才存下圖片到本機的磁碟中,成為一個圖片的實體檔案。
base64 圖片,在瀏覽器中可單純開啟圖片檔,另外也可以指定 <img> 元素,透過 src='' 做為來源指入於元素中呈現圖片。
下載方式一:手刻 base64 圖片下載
透過前面提的概念,將產生的實體圖檔存放於儲存空間, .download 設定下載圖檔的預設名稱並帶時間戳,圖檔的副檔名透過 base64 以截字串二次的方式,取得圖片格式做為使用。
See the Pen canvas 以 HTMLCanvasElement.toDataURL() 方法轉 base64 編碼,將 png 印出於網頁上 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>HTML 基本自定樣版</title> <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> <style> body { margin: 0; } #html_img, #myCanvas, #base64Img { max-width: 100%; } </style> </head> <body> <div style="display: flex; text-align: center"> <div style="width: 33.33%"> <img id="html_img" /> <h2>img 元素</h2> </div> <div style="width: 33.33%"> <canvas id="myCanvas"></canvas> <h2>canvas 繪製圖片</h2> </div> <div style="width: 33.33%"> <img id="base64Img" /> <h2>canvas .toDataURL() 方法轉出 base64 png 圖檔</h2> <button id="btn_downBase64Img">btn_downloadImg</button> </div> </div> <script> let myCanvasEL = document.getElementById('myCanvas'); let ctx = myCanvasEL.getContext('2d'); // 指定渲染環境 2D let html_img = document.getElementById('html_img'); let base64Img = document.getElementById('base64Img'); let imgSrcPath = 'https://images.pexels.com/photos/9009959/pexels-photo-9009959.jpeg?auto=compress&cs=tinysrgb&h=350&w=275'; html_img.src = imgSrcPath; let canvasImg = new Image(); let imgConvertDataUrl; canvasImg.src = imgSrcPath; canvasImg.crossOrigin = 'anonymous'; // console.log('canvasImg', canvasImg); canvasImg.onload = function () { // console.log('canvasImg.onload', canvasImg, this); // console.log('img.crossOrigin onload fun 內', canvasImg.crossOrigin); myCanvasEL.width = this.width; myCanvasEL.height = this.height; ctx.drawImage(this, 0, 0); dataURL = myCanvasEL.toDataURL(); imgConvertDataUrl = dataURL; console.log('dataURL', dataURL); base64Img.src = dataURL; }; /* 動態產生 a 連結觸發 * .click 事件 * .download 下載檔案 */ let btn_downBase64Img = document.getElementById('btn_downBase64Img'); btn_downBase64Img.onclick = function () { // data:image/png;base64, xxx... 分割 base64 圖片的字串 '/' 與 ';',取得圖檔格式 png 列為副檔名。 let imgType = dataURL.split('/', 2)[1].split(';',2)[0]; let link = document.createElement('a'); link.href = imgConvertDataUrl; link.id = 'downloadImg_link'; link.download = `canvas 轉 base64 圖片--${new Date().getTime()}.${imgType}`; document.body.appendChild(link); let downloadImg_linkEL = document.getElementById('downloadImg_link'); downloadImg_linkEL.click(); // 動態建立的 a 連結執行點按行為 downloadImg_linkEL.remove(); // 觸發 .click 行為後,立即移除動態建立的 a 連結 }; </script> </body> </html> |
其他參考文件:MDN – Uint8Array
下載方式二:download.js 套件,簡化處理 base64 圖片輸出成圖檔
透過 download.js 實作的按鈕下載圖片。
1 2 3 | <script src="http://danml.com/js/download2.js"></script> // or <script src="https://cdnjs.cloudflare.com/ajax/libs/downloadjs/1.4.8/download.min.js"></script> |
簡單以語法對應設定 base64 圖片的下載名稱與副檔名。
1 | download(data, strFileName, strMimeType); |
See the Pen canvas 轉 base64 編碼 png 圖片,在網頁上透過按鈕點按觸發下載 by Jimmy_Wu (@Jimmy_Wu) on CodePen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>HTML 基本自定樣版</title> <link rel="icon" href="data:;base64,iVBORw0KGgo=" /> <style> body { margin: 0; } #html_img, #myCanvas, #base64Img { max-width: 100%; } </style> </head> <body> <div style="display: flex; text-align: center"> <div style="width: 33.33%"> <img id="html_img" /> <h2>img 元素</h2> </div> <div style="width: 33.33%"> <canvas id="myCanvas"></canvas> <h2>canvas 繪製圖片</h2> </div> <div style="width: 33.33%"> <img id="base64Img" /> <h2>canvas .toDataURL() 方法轉出 base64 png 圖檔</h2> <button id="btn_downBase64Img">btn_downloadImg</button> </div> </div> <script src="http://danml.com/js/download2.js"></script> <script> let myCanvasEL = document.getElementById('myCanvas'); let ctx = myCanvasEL.getContext('2d'); // 指定渲染環境 2D let html_img = document.getElementById('html_img'); let base64Img = document.getElementById('base64Img'); let imgSrcPath = 'https://images.pexels.com/photos/9009959/pexels-photo-9009959.jpeg?auto=compress&cs=tinysrgb&h=350&w=275'; html_img.src = imgSrcPath; let canvasImg = new Image(); let imgConvertDataUrl; canvasImg.src = imgSrcPath; canvasImg.crossOrigin = 'anonymous'; canvasImg.onload = function () { myCanvasEL.width = this.width; myCanvasEL.height = this.height; ctx.drawImage(this, 0, 0); dataURL = myCanvasEL.toDataURL(); imgConvertDataUrl = dataURL; console.log('dataURL', dataURL); base64Img.src = dataURL; }; /* 動態產生 a 連結觸發 * .click 事件 * .download 下載檔案 */ let btn_downBase64Img = document.getElementById('btn_downBase64Img'); btn_downBase64Img.onclick = function () { // data:image/png;base64, xxx... 分割 base64 圖片的字串 '/' 與 ';',取得圖檔格式 png 列為副檔名。 let imgType = dataURL.split('/', 2)[1].split(';',2)[0]; download(dataURL, `canvas 轉 base64 圖片透過 downloadJS--${new Date().getTime()}`, imgType); }; </script> </body> </html> |
其他參考文章:
- Canvas2image.js GitHub
- Web開發之用canvas2image.js將canvas保存為圖片(實現頁面截圖下載功能)說明文章、gitHub-pages
整合 canvas2image.js、html2canvas.js、convertImgToBase64.js 插件,透過 canvas 原型製出繪畫功能。
其他相關實作資料
- JavaScript如何實現圖片處理與合成 說明文章、實作範例 (圖片合成: Example Git、圖片裁剪: Example Git、人像摳除: Example Git)
使用 mcanvas.js 插件 ( GitHub 使用文件、GitHub-pages) 實作
實作功能: 圖片的縮放、圖片的裁剪、圖片的合成、圖片與圖片的合成,例如貼紙,邊框,水印等、為圖片添加文字、為圖片添加基礎幾何圖形…