canvas從入門到豬頭

關注公衆號「執鳶者」,回覆「資料」獲取500G資料(各「兵種」均有),還有專業交流羣等你一塊兒來瀟灑。(哈哈)

經過本文你將瞭解canvas簡介及其比較經常使用的方法,並利用canvas實現一個小豬頭。html

1、Canvas簡介

1.1 什麼是canvas

Canvas(畫布)是在HTML5中新增的標籤用於在網頁實時生成圖像,能夠操做圖像內容,是一個能夠用JavaScript操做的位圖(bitmap)。

1.2 canvas的座標系統

canvas的座標系統以下圖所示,其具備以下特色:
  • x軸正方向向右、y軸正方向向下
  • 畫布的原點在左上角
  • 橫縱座標單位爲像素
  • 每一個軸的最小單元爲一個像素(柵格)

1.3 canvas的繪製流程

  1. 建立一個<canvas></canvas>標籤
  2. 獲取canvas元素對應的DOM對象,這是一個Canvas對象
  3. 調用Canvas對象的getContext()方法,該方法返回一個CanvasRenderingContext2D對象,該對象便可繪製圖形
  4. 調用CanvasRenderingContext2D對象的方法繪圖

1.4 canvas的應用領域

canvas這個神奇的東西有不少領域能夠獲得應用,下面咱們一塊兒嘮一嘮。
  1. 遊戲:canvas 在基於 Web 的圖像顯示方面比 Flash 更加立體、更加精巧,canvas 遊戲在流暢度和跨平臺方面更優秀,例如這25款canvas遊戲
  2. 可視化的庫:Echart
  3. banner廣告:Canvas 實現動態的廣告效果很是合適
  4. 圖形編輯器:後續Photoshop可以100%基於Web實現
  5. 微信讀書、騰訊文檔均是經過canvas實現

2、基礎功能

經過第一章對canvas有了初步的認識,本章就按照一我的繪製一幅畫的思路進行演化,逐步瞭解canvas的基本功能,從而更好的使用它實現一些酷炫的效果。

2.1 座標系選擇

當要繪製一幅畫時首先要肯定座標系,只有選擇好了座標系以後纔好動筆,在canvas中默認座標系在左上角(X軸正方向向右、Y軸正方向向下),但是有的時候須要變換座標系才能更方便的實現所需的效果,此時須要變換座標系,canvas提供瞭如下幾種變換座標系的方式:
  1. translate(dx,dy):平移座標系。至關於把原來位於(0,0)位置的座標原點平移到(dx、dy)點。
  2. rotate(angle):旋轉座標系。該方法控制座標系統順時針旋轉angle弧度。
  3. scale(sx,sy):縮放座標系。該方法控制座標系統水平方向上縮放sx,垂直方向上縮放sy。
  4. transform(a,b,c,d,e,f):容許縮放、旋轉、移動並傾斜當前的環境座標系,其中a表示水平縮放、b表示水平切斜、c表示垂直切斜、d表示垂直縮放、e表示水平移動、f表示垂直移動。

function main() {
    const canvas = document.getElementById('canvasId');
    const ctx = canvas.getContext('2d');
    ctx.lineWidth = 4;
    // 默認
    ctx.save();
    ctx.strokeStyle = '#F00';
    drawCoordiante(ctx);
    ctx.restore();

    // 平移
    ctx.save();
    ctx.translate(150, 150);
    ctx.strokeStyle = '#0F0';
    drawCoordiante(ctx);
    ctx.restore();

    // 旋轉
    ctx.save();
    ctx.translate(300, 300);
    ctx.rotate(-Math.PI / 2);
    ctx.strokeStyle = '#00F';
    drawCoordiante(ctx);
    ctx.restore();

    // 縮放
    ctx.save();
    ctx.translate(400, 400);
    ctx.rotate(-Math.PI / 2);
    ctx.scale(0.5, 0.5);
    ctx.strokeStyle = '#000';
    drawCoordiante(ctx);
    ctx.restore();
}

function drawCoordiante(ctx) {
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(120, 0);
    ctx.moveTo(0, 0);
    ctx.lineTo(0, 80);
    ctx.closePath();
    ctx.stroke();
}

main();

2.2 圖形繪製

座標系選擇好了以後,就要開始動筆創做大做了,那麼canvas到底容許繪製哪些內容呢?
  1. 直線
function drawLine(ctx, startX, startY, endX, endY) {
    ctx.moveTo(startX, startY);
    ctx.lineTo(endX, endY);
    ctx.stroke();
}
  1. 圓弧
function drawCircle(ctx, x, y, R, startAngle, endAngle) {
    ctx.arc(x, y, R, startAngle, endAngle);
    ctx.stroke();
}
  1. 曲線
// 貝濟埃曲線
function drawBezierCurve(ctx, cpX1, cpY1, cpX, cpY2, endX, endY) {
    ctx.bezierCurveTo(cpX1, cpY1, cpX, cpY2, endX, endY);
    ctx.stroke();
}
// 二次曲線
function drawQuadraticCurve(ctx, cpX, cpY, endX, endY) {
    ctx.quadraticCurveTo(cpX, cpY, endX, endY);
    ctx.stroke();
}
  1. 矩形
// 填充矩形
function drawFillRect(ctx, x, y, width, height) {
    ctx.fillRect(x, y, width, height);
}
// 邊框矩形
function drawStrokeRect(ctx, x, y, width, height) {
    ctx.strokeRect( x, y, width, height);
}
  1. 字符串
// 填充字符串
function drawFillText(ctx, text, x, y) {
    ctx.fillText(text, x, y);
}
// 邊框字符串
function drawStrokeText(ctx, text, x, y) {
    ctx.strokeText(text, x, y);
}
  1. 複雜圖形繪製——路徑
// 利用路徑繪製
function drawFigureByPath(ctx) {
    ctx.beginPath();
    ctx.moveTo(100, 400);
    ctx.lineTo(200, 450);
    ctx.lineTo(150, 480);
    ctx.closePath();
    ctx.fill();
}

function main() {
    const canvas = document.getElementById('canvasId');
    const ctx = canvas.getContext('2d');
    ctx.lineWidth = 2;
    ctx.strokeStyle = '#F00';
    ctx.fillStyle = '#F00';
    ctx.font = 'normal 50px 宋體';
    drawLine(ctx, 50, 10, 150, 10);
    ctx.moveTo(150, 100);
    drawCircle(ctx, 100, 100, 50, 0, Math.PI);
    ctx.moveTo(300, 100);
    drawCircle(ctx, 250, 100, 50, 0, Math.PI * 2);
    ctx.moveTo(350, 150);
    drawBezierCurve(ctx, 200, 200, 450, 250, 300, 300);
    ctx.moveTo(50, 250);
    drawQuadraticCurve(ctx, 50, 400, 80, 400);
    drawFillRect(ctx, 100, 300, 100, 50);
    drawStrokeRect(ctx, 300, 300, 100, 50);
    drawFillText(ctx, 'I', 100, 400);
    drawStrokeText(ctx, 'I', 300, 400);
    drawFigureByPath(ctx);
}

2.3 填充風格

利用canvas繪製圖形時勢必要上點顏料,經過設置fillStyle屬性便可設置對應的顏料,對於顏料值主要有如下四種:純顏色、線性漸變顏色、徑向漸變顏色、位圖。
  1. 純顏色
function useColorFill(ctx) {
    ctx.save();
    ctx.fillStyle = '#F00';
    ctx.fillRect(10, 10, 100, 100);
    ctx.restore();
}
  1. 線性漸變顏色
function useLinearGradientFill(ctx) {
    ctx.save();
    const lg = ctx.createLinearGradient(110, 10, 210, 10);
    lg.addColorStop(0.2, '#F00');
    lg.addColorStop(0.5, '#0F0');
    lg.addColorStop(0.9, '#00F');
    ctx.fillStyle = lg;
    ctx.fillRect(120, 10, 100, 100);
    ctx.restore();
}
  1. 徑向漸變顏色
function useRadialGradientFill(ctx) {
    ctx.save();
    const lg = ctx.createRadialGradient(260, 60, 10, 260, 60, 60);
    lg.addColorStop(0.2, '#F00');
    lg.addColorStop(0.5, '#0F0');
    lg.addColorStop(0.9, '#00F');
    ctx.fillStyle = lg;
    ctx.fillRect(230, 10, 100, 100);
    ctx.restore();
}
  1. 位圖填充
function useImageFill(ctx) {
    ctx.save();
    const image = new Image();
    image.src = 'https://dss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=442547030,98631113&fm=58';
    image.onload = function () {
        // 建立位圖填充
        const imgPattern = ctx.createPattern(image, 'repeat');
        ctx.fillStyle = imgPattern;
        ctx.fillRect(340, 10, 100, 100);
        ctx.restore();
    }
}

2.4 臨時保存

用一隻畫筆在畫某個美女時,突然來了靈感須要畫另外一個帥哥,這個時候又不想放棄這個美女,則就須要將當前畫美女的顏料、座標等狀態進行暫存,等到畫完帥哥後恢復狀態進行美女的繪製。在canvas中能夠經過save()和restore()方法實現,調用save()方法後將這一時刻的設置放到一個暫存棧中,而後能夠放心的修改上下文,在須要繪製以前的上下文時,能夠調用restore()方法。
// 從左往右是依次繪製(中間爲用新的樣式填充,最後一個是恢復到原來樣式填充)
function main() {
    const canvas = document.getElementById('canvasId');
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = '#F00';
    ctx.fillRect(10, 10, 100, 100);
    ctx.save();
    ctx.fillStyle = '#0F0';
    ctx.fillRect(150, 10, 100, 100);
    ctx.restore();
    ctx.fillRect(290, 10, 100, 100);
}

2.5 引入外部圖像

有的時候須要引入外部圖片,而後對外部圖片進行像素級別的處理,最後進行保存。
  1. 繪製圖像:drawImage
  2. 取得圖像數據:getIamgeData
  3. 將修改後的數據從新填充到Canvas中:putImageData
  4. 輸出位圖:toDataURL

function main() {
    const canvas = document.getElementById('canvasId');
    const ctx = canvas.getContext('2d');
    const image = document.getElementById('image');
    // 繪製圖像
    ctx.drawImage(image, 0, 0);
    // 獲取圖像數據
    const imageData = ctx.getImageData(0, 0, image.width, image.height);
    const data = imageData.data;
    for (let i = 0, len = data.length; i < len; i += 4) {
        const red = data[i];
        const green = data[i + 1];
        const blue = data[i + 2];

        const average = Math.floor((red + green + blue) / 3);

        data[i] = average;
        data[i + 1] = average;
        data[i + 2] = average;
    }

    imageData.data = data;
    ctx.putImageData(imageData, 0, 0);
    document.getElementById('result').src = canvas.toDataURL('image/png');
}

3、豬頭實戰

學習了這麼多,就利用canvas繪製一個豬頭吧,畢竟每一個程序員身邊確定有一個陪伴本身的小胖豬,嘿嘿。
function main() {
    const canvas = document.getElementById('canvasId');
    const ctx = canvas.getContext('2d');
    ctx.lineWidth = 4;
    ctx.strokeStyle = '#000';
    ctx.fillStyle = '#ffd8e1';
    ctx.translate(260, 20);
    drawPigEar(ctx);
    ctx.save();
    ctx.rotate(Math.PI / 2);
    drawPigEar(ctx);
    ctx.restore();
    drawPigFace(ctx);
    ctx.save();
    ctx.translate(-100, -100);
    drawPigEye(ctx);
    ctx.restore();
    ctx.save();
    ctx.translate(100, -100);
    drawPigEye(ctx);
    ctx.restore();
    ctx.save();
    ctx.translate(0, 60);
    drawPigNose(ctx);
    ctx.restore();
}

function drawPigEar(ctx) {
    ctx.save();
    ctx.beginPath();
    ctx.arc(-250, 0, 250, 0, -Math.PI / 2, true);
    ctx.arc(0, -250, 250, -Math.PI, Math.PI / 2, true);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
    ctx.restore();
}

function drawPigFace(ctx) {
    ctx.save();
    ctx.beginPath();
    ctx.arc(0, 0, 250, 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();
    ctx.restore();
}

function drawPigEye(ctx) {
    ctx.save();
    ctx.fillStyle = '#000';
    ctx.beginPath();
    ctx.arc(0, 0, 20, 0, Math.PI * 2);
    ctx.closePath();
    ctx.fill();
    ctx.restore();
}

function drawPigNose(ctx) {
    ctx.save();
    ctx.fillStyle = '#fca7aa';
    ctx.beginPath();
    ctx.ellipse(0, 0, 150, 100, 0, 0, Math.PI * 2);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
    ctx.save();
    ctx.translate(-60, 0);
    drawPigNostrils(ctx);
    ctx.restore();
    ctx.save();
    ctx.translate(60, 0);
    drawPigNostrils(ctx);
    ctx.restore();
    ctx.restore();
}

function drawPigNostrils(ctx) {
    ctx.save();
    ctx.fillStyle = '#b55151';
    ctx.beginPath();
    ctx.ellipse(0, 0, 40, 60, 0, 0, Math.PI * 2);
    ctx.closePath();
    ctx.fill();
    ctx.stroke();
    ctx.restore();
}

main();

1.若是以爲這篇文章還不錯,來個分享、點贊吧,讓更多的人也看到前端

2.關注公衆號執鳶者,領取學習資料(前端「多兵種」資料),按期爲你推送原創深度好文html5

相關文章
相關標籤/搜索