[深刻14] canvas

導航

[深刻01] 執行上下文
[深刻02] 原型鏈
[深刻03] 繼承
[深刻04] 事件循環
[深刻05] 柯里化 偏函數 函數記憶
[深刻06] 隱式轉換 和 運算符
[深刻07] 瀏覽器緩存機制(http緩存機制)
[深刻08] 前端安全
[深刻09] 深淺拷貝
[深刻10] Debounce Throttle
[深刻11] 前端路由
[深刻12] 前端模塊化
[深刻13] 觀察者模式 發佈訂閱模式 雙向數據綁定
[深刻14] canvas
[深刻15] webSocket
[深刻16] webpack
[深刻17] http 和 https
[深刻18] CSS-interview
[react] Hookscss

[部署01] Nginx
[部署02] Docker 部署vue項目
[部署03] gitlab-CIhtml

[源碼-webpack01-前置知識] AST抽象語法樹
[源碼-webpack02-前置知識] Tapable
[源碼-webpack03] 手寫webpack - compiler簡單編譯流程前端

前置知識

一些單詞

canvas:畫布

triangle:三角形
rectangle:矩形

arc:弧
anti:反對,反
clockwise:順時針方向
anticlockwise:逆時針方向

curve:曲線
quadratic:平方的
複製代碼

弧長 弧度

  • 弧度 = 弧長 / 半徑
弧度 = 弧長 / 半徑

圓的弧長 = 2PI * R   //即周長

1°的弧長 = 2PI * R / 360 = PI * R / 180

1°的弧度 = PI / 180
複製代碼

canvas

屬性

  • 只有兩個屬性:with 和 height 默認的width=300,height=150
<canvas
  id="canvas" width="200" height="200"
  style="border: 1px solid red;"
></canvas>

(1) 指定width 和 height的方式有三種
1. 標準方式:canvas 標籤自帶的 width 和 height 屬性
2. css方式
3. js方式: domTarget.width  和  domTarget.height
複製代碼

在不支持canvas的瀏覽器中顯示 (替換內容)

  • 替換內容:寫在canvas便籤內
  • 不支持的瀏覽器將顯示替換內容,而支持的瀏覽器會忽略標籤內的內容
  • 注意:canvas必須有結束標籤,若是沒有,後面的內容將被認爲是替換的內容
<canvas id="canvas" width="200" height="200">
  替換的內容
  // <img src="images/clock.png" width="150" height="150" alt=""/>
</canvas>
複製代碼

渲染上下文 - the render context

  • getContext() 方法
    • 獲取 ( 渲染上下文 ) 和 ( 繪畫功能 )
    • 參數:表示( 上下文的格式 ) 2d3d

如何判斷瀏覽器是否支持canvas標籤

var canvas = document.getElementById('canvas');
if (canvas.getContext) { // ------------------------ 經過判斷 getContext 方法是否存在來判斷
  console.log('你的瀏覽器支持Canvas!');
} else {
  console.log('你的瀏覽器不支持Canvas!');
}
複製代碼

模塊

<canvas
    id="canvas" width="200" height="200"
    style="border: 1px solid red;"
  >
    替換的內容
  </canvas>
<script>
  window.addEventListener('load', draw, false)
  // load事件:在頁面加載完成時候觸發,包括DOM,圖片,視頻等全部資源加載完畢時執行
  // DOMContentLoaded:在DOM加載完成時觸發
  // 或者 <body onload="draw();">...</body>
  
  function draw() {
    var canvas = document.getElementById('canvas');
    if (canvas.getContext) { // ------------------------ getCotext方法存在,說明瀏覽器支持canvas
      console.log('你的瀏覽器支持Canvas!');
      var ctx = canvas.getContext('2d');
      // 實現繪畫的邏輯...
    } else {
      console.log('你的瀏覽器不支持Canvas!');
    }
  }
複製代碼

繪製的過程

  • 先定義狀態,後繪製

繪製 矩形 rectangle:矩形

  • fillRect(x, y, width, height) -------- 填充矩形,x y 表示矩形左上角的座標,原點是左上角 0 0 位置
  • strokeRect(x, y, width, height) ---- 邊框矩形
  • clearRect(x, y, width, height) ------ 清除矩形區域,使其清除部分徹底透明
// 矩形

ctx.fillRect(300, 100, 100, 100) // 填充矩形
ctx.clearRect(350, 150, 30, 30); // 清除矩形區域,使其清除部分徹底透明
ctx.strokeRect(400, 200, 50, 50) // 矩形邊框
複製代碼

畫 一條 直線 和 一個 三角形

  • beginPath() ---------- 新建路徑,新建後能夠繪製
  • closePath() ----------- 閉合路徑,閉合後能夠從新繪製,( 非必須,從新beginPath也行 )
  • stroke() --------------- 用線條繪製
  • fill() ------------------- 填充繪製
  • moveTo() ------------- 起始位置
  • lineTo() --------------- 繪製直線,是一個點
  • lineWidth:----------- 直線的寬度,是一個數字
  • lineCap:------------- 直線的末端線帽樣式,'round'
  • save()---------------- 保存整個環境
  • restore() ------------ 恢復以前的環境,如旋轉canvas後,須要恢復以前的環境,否則全部後續的繪圖都會旋轉
function drawLineAndTriangle(ctx) {
  // 三角形
  ctx.beginPath() //------------------------------------------ 一個路徑的開始
  ctx.moveTo(100, 100) // ----- 起始點
  ctx.lineTo(80, 120) // ------ 直線的第二個點
  ctx.lineTo(120, 120) // ----- 直線的第三個點
  ctx.closePath() // ----------------------------------------- 一個路徑的結束
  ctx.lineWidth = 4 // -------- 直線的寬度
  ctx.strokeStyle = 'red' // ------ 直線的顏色,須要在繪畫前設置
  ctx.stroke() // --------------------------------------------- 描邊 (繪製)
  ctx.fillStyle= 'yellow' // ------ 填充的顏色,須要在繪畫前設置
  ctx.fill() // ----------------------------------------------- 填充 (繪製)

  // 直線
  ctx.beginPath()
  ctx.moveTo(30, 30)
  ctx.lineTo(100, 30)
  ctx.closePath()
  ctx.lineWidth = 2 !!!!!!!!!!!!!!!!!!!
  ctx.strokeStyle = 'blue'
  ctx.stroke()
}
複製代碼

弧線

  • arc(x, y, radius, startAngle, endAngle, anticlockwise)
    • 以x,y爲圓心,radius爲半徑, startAngle和endAngle爲角度,anticlockwise爲方向的圓弧(圓)
    • anticlockwise:布爾值,表示是否逆時針方向,默認的方向是順時針
    • startAngle, endAngle表明的是( 弧度 ),而不是角度
    • 注意:起始角度爲三點鐘位置,而且是以弧度計算的
    • 弧度=(Math.PI/180)*角度
  • arcTo(x1, y1, x2, y2, radius)
    • 根據給定的控制點和半徑畫一段圓弧,再以直線鏈接兩個控制點。
  • arc:是弧的意思
// 圓弧
ctx.beginPath()
ctx.arc(200, 150, 40, 90 * Math.PI/180, 1.5 * Math.PI, false)
ctx.stroke()

// 圓
ctx.beginPath()
ctx.arc(200, 350, 40, 0, 2 * Math.PI, false)
ctx.fill()
複製代碼

畫一個笑臉

function drawLineAndTriangle(ctx) {

  ctx.beginPath()
  ctx.arc(300, 300, 200, 0, 2 * Math.PI) // --------------------- 大圓
  ctx.stroke()
  // ctx.closePath() 可要可不要

  ctx.beginPath()
  ctx.arc(250, 200, 6, 0, 2* Math.PI) // ------------------------ 左眼
  ctx.stroke()

  ctx.beginPath()
  ctx.arc(350, 200, 6, 0, 2* Math.PI) // ------------------------ 右眼
  ctx.stroke()

  ctx.beginPath()
  ctx.arc(300, 300, 150, 0, 1 * Math.PI) ------------------------- 嘴
  ctx.stroke()
}
複製代碼

二次貝塞爾曲線,三次貝塞爾曲線

  • quadraticCurveTo(cp1x, cp1y, x, y) ------------------ 繪製二次貝塞爾曲線
    • cp1x,cp1y爲一個控制點,x,y爲結束點
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) --------- 繪製三次貝塞爾曲線
    • cp1x,cp1y爲控制點一
    • cp2x,cp2y爲控制點二
    • x,y爲結束點
  • 注意:起始點經過moveTo()獲取

color

  • fillStyle
  • strokeStyle

globalAlpha - 透明度

  • globalAlpha:設置透明度 0-1之間
  • 注意:globalAlpha 和 fillStyle 和 strokeStyle 須要在繪畫前設置

lineCap:線段終點的樣子

  • butt
  • round:圓頭
  • square
ctx.beginPath();
  ctx.moveTo(100, 100)
  ctx.lineTo(100, 300)
  ctx.strokeStyle = 'red'
  ctx.lineWidth = 20
  ctx.lineCap = 'round' // ------------------ 設置線段終點的樣子爲圓形
  // 注意:全部的狀態設置都必須在 stroke繪畫前面
  ctx.stroke()
複製代碼

實例1:canvas實現生成一張圖片保存到本地

drawImage

  • context.drawImage(img,x,y)
  • context.drawImage(img, x, y, width, height)
  • context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
  • img:圖片
  • x:在畫布上放置圖像的 x 座標
  • y:在畫布上放置圖像的 y 座標
  • witdh:圖像的寬度
  • height:圖像的高度
  • sx:剪切圖像的 x 座標
  • sy:剪切圖像的 y 座標
  • swidth:剪切圖像的寬度
  • sheight:剪切圖像的高度
  • 注意:剪切部分是去掉的部分,不顯示

fillText 和 font

  • context.fillText(text,x,y,maxWidth) 在畫布上繪製填色的文本
    • tetx文本,xy座標,maxWidth容許的最大文本寬度
  • context.font = "" 設置或返回字體屬性

canvas.toDataURL()

  • canvas.toDataURL(type, encoderOptions):返回一個包含圖片展現的URI
  • type:圖片的類型 image/png
  • encoderOptions:圖片質量
  • 返回值:一個包含圖片展現的URI,默認爲 PNG 格式,圖片的分辨率爲96dpi

new Image()

  • new Image(width, height) 用於生成 HTMLImageElement 實例
  • 注意:用js生成的img實例,並不在文檔中,須要手動插入
  • 屬性:src,currentSrc
  • 方法:
    • onload:圖像加載完成,會觸發onload屬性指定的回調函數
    • onerror:圖像加載完成,同時也會觸發onerror屬性指定的回調函數
new Image(width, heght)

 mounted() {
    const limg = require('../images/1.jpg');
    const img = new Image(200, 200);   ------------- 參數分別是 width 和 height
    img.src = limg;    ---------------- 除了src,還有currentSrc表示當前src,由於src能夠動態指定
    img.onload = function() {
      console.log('加載完成');
      document.body.appendChild(img);   -------------- 插入文檔
    }
    img.onerror = function() {
      console.log('錯誤')
    }
  }
複製代碼
------
canvas實現生成圖片保存到本地


實例:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #imgContainer {
      width: 400px;
      height: 400px;
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <div>
    <img src="./002.jpg" alt="002.jpg" id="imgx">
    <button id="button">生成圖片</button>

    <div id="imgContainer"></div>
  </div>
  <script>
    window.onload = function() {
      const imgx = document.getElementById('imgx')
      const button = document.getElementById('button')
      const imgContainer = document.getElementById('imgContainer')

      button.addEventListener('click', clickButton, false)
      function clickButton() {
        combine()
      }
      function combine() {
        const canvas = document.createElement('canvas') // -------- 建立canvas標籤
        canvas.width = 500
        canvas.height = 500
        canvas.style = "border: 1px solid red"
        document.documentElement.appendChild(canvas) // ------------ 添加到HTML的DOM中

        const context = canvas.getContext('2d') // ----------------- 獲取渲染上下文和繪畫功能
        context.drawImage(imgx, 0, 0, 300, 300) // ----------------- drawImage() 生成圖片
        
        context.fillStyle = 'white';
        context.font = '30px Georgia';
        context.fillText('生成的圖片', 60, 60) // ------------------- 填充文字

        const currentUrl = canvas.toDataURL('image/png') // -------- toDataURL() 返回圖片的 URI
        imgContainer.innerHTML = `<img src=${currentUrl}>` // ------ 填充內容
      }
    }
  </script>
</body>
</html>

複製代碼

實例2:實現一個粒子文字動畫

getImageData()

  • context.getImageData(x,y,width,height)
  • getImageData() 返回 ( ImageData對象 ),獲取畫布指定矩形的 像素數據

putImageData()

  • 將 ImageData對象 繪製到 canvas 上

ImageData對象

  • 包含 width height data 三個屬性
  • data屬性:是一個數組( Uint8ClampedArray ),包含以 RGBA 順序的數據,數據使用 0 至 255(包含)的整數表示
    • 圖像是二維的,由height決定行數,width決定列數
    • R - 紅色 (0-255)
    • G - 綠色 (0-255)
    • B - 藍色 (0-255)
    • A - alpha 通道 (0-255; 0 是透明的, 255 是徹底可見的)
    • alpha的值大於128,即爲有顏色的點
    • 注意:data中點的排列順序是 從左到右,從上到下的順序,而每一個點佔數組的四個成員

context.save() 和 context.restore()

  • context.save()用來保存canvas的狀態
    • save後能夠調用canvas的平移,縮放,旋轉,裁剪等操做
  • context.restore()
    • 恢復以前保存的狀態

實現靜態粒子文字

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    class CanvasPartiular {
      constructor() {
        this.clientWidth = null // html寬度
        this.clientHeight = null // html高度
        this.sx = null // canvas的drawImage畫圖的x座標,同時也是圖片的寬度
        this.sy = null // canvas的drawImage畫圖的y座標,同時也是圖片的高度
        this.canvas = null // canvas實例
        this.context = null // canvas的渲染上下文
        this.img = null // new Image() 生成的圖片
        this.imageData = null // iamgeData對象,包括width,height,data數組
        this.dotArr = [] // ------------------------------ 用來收集像素點,成員是一個包含x,y, cricle的對象
        this.createCanvas() // 建立函數
        this.createImage() // 建立函數
      }

      createCanvas = () =>  {
        const HTML = document.documentElement
        const clientWidth = HTML.clientWidth
        const clientHeight = HTML.clientHeight
        const canvas = this.canvas = document.createElement('canvas') // ------------- 建立canvas
        canvas.width = this.clientWidth = clientWidth
        canvas.height = this.clientHeight = clientHeight
        canvas.style.border = '1px solid black';
        this.context = canvas.getContext('2d') // ------------------------------------ 獲取context
        document.body.appendChild(canvas)
      }

      createImage = () => {
        const img = this.img = new Image()
        img.src = './5.jpg'
        if (img.complete) { // -------------------------- if else保證了圖片加載完成後再執行 init 方法
          this.init()
        }
        else {
          img.onload = this.init
        }
      }

      init = () =>  {
        const sx = this.sx = this.clientWidth/2 - this.img.width/2; 
        // 橫座標和寬,能夠本身用兩個正方形驗證
        const sy = this.sy = this.clientHeight/2 - this.img.height/2;
        this.context.drawImage(this.img, sx, sy)
        // 畫圖
        const imageData = this.imageData = this.context.getImageData(sx, sy, this.img.width, this.img.height)
        // 獲取 imageData 對象
        this.getDotList()
      }

      // 重點是該函數,獲取dotList數組
      getDotList = () => {
        const dataArr = this.imageData.data
        const imgWidth = this.imageData.width
        for(let x = 0; x < imgWidth; x = x + 6) {
        // x 表示橫軸的點,每次增長6則每一個點之間有間隙
          for(let y = 0; y < this.imageData.height; y = y + 6 ) {
            // y 表示縱軸
            
            const iDotPositionInArray = (y * imgWidth + x) * 4
            // (1) y * imgWidth:表示該點的位置已是第y行了,即有 y * imgWidth個點
            // (2) y * imgWidth + x:表示該點的具體位置,即前面有 y * imgWidth + x 個點
            // (3) (y * imgWidth + x) * 4:表示再data數組中,該點的位置。由於每一個點佔據data數組的4個成員
            
            if(dataArr[iDotPositionInArray + 3] > 256/2 && dataArr[iDotPositionInArray] < 100) {
              // iDotPositionInArray + 3:表示該點的 Alpha 透明度
              // Alpha在0 - 256之間
              // 256/2:表示該點可見,不是透明的
              this.dotArr.push({x, y, radius: 2}) // x,y表示座標,radius半徑,半徑隨便設合適便可
            }
          }
        }
        this.draw()
      }

      draw = () => {
        const context = this.context
        // context.clearRect(0, 0, this.clientWidth, this.clientHeight);
        // clearRect清除矩形的canvas,即清除drawImage的圖片,下面從新畫點圖
        context.fillStyle = 'black'
        this.dotArr.forEach(({x,y,radius}) => {
          context.save()
          context.beginPath()
          context.arc(x, y, radius, 0, 360 * Math.PI/180) // 畫圓
          context.fill()
          context.restore()
        })
      }
    }

    new CanvasPartiular()
  </script>
</body>
</html>
複製代碼

實例3:時鐘動畫

  • 弧度 = 弧長 / 半徑

context.translate()

  • context.translate(x, y), 重新映射畫布上的 (0, 0) 位置

context.font

  • context.font = "40px Arial" 設置文本內容的字體屬性

context.textAlign

  • context.textAlign = 'center' --------- 設置文本 ( 左右對齊 ) 方式

context.textBaseline

  • context.textBaseline = 'middle' ----- 設置文本的 ( 上下對齊 ) 方式

context.fillText()

  • context.fillText(text, x, y, maxWidth) 在畫布上繪製填色的文本,默認黑色

sin函數,cos 函數

  • cos(180° - a) = - cos(a)
  • sin(180° - a) = sin(a)
  • sin(2a) = 2 * sin(a) * cos(a)

context.rotate()

  • context.rotate(angle) 旋轉當前繪圖,參數以弧度計算

context.lineWidth

  • context.lineWidth = 10 設置線條寬度,以像素計算

context.lineCap

  • context.lineCap = 'round' 設置線條末端線帽的樣式

context.save() 和 context.restore()

  • context.save() 保存當前環境狀態
  • context.restore() 返回以前保存過的路徑狀態和屬性
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    html {
      height: 100%;
    }
    body {
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    #canvas {
      border: 1px solid red;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width=600 height=600></canvas>
  <script>
    const canvas = document.getElementById('canvas')
    const cWidth = canvas.width
    const cHeight = canvas.height
    const context = canvas.getContext('2d')

    const center = context.translate(cWidth/2, cHeight/2)
    const arcRadius = cWidth/2;
    const rate = cWidth / 600; // -------- 比例,以當前大小600爲基準,若是cWidth = 1200則rate爲2,放大一倍

    function clockBorder() { // 時鐘外圓邊框
      context.beginPath()
      context.arc(0, 0, (arcRadius - 20/2) * rate, 0, 360 * Math.PI/180) // 時鐘大圓
      context.strokeStyle='blue'
      context.lineWidth = 20 * rate;
      context.stroke()
      context.closePath()
    }

    function clockNumber() { // 數字刻度
      var numberArr = [3,4,5,6,7,8,9,10,11,12,1,2];
      context.font = `${30 * rate}px Arail`;
      context.textAlign = 'center';
      context.textBaseline = 'middle';
      context.fillStyle = 'black';
      numberArr.forEach((item, index) => {
        const rad = 2 * Math.PI / 12 * index; 
        // ----------------------------------------------------- 2 * Math.PI / 12 表示一個小時所佔的弧度
        // ----------------------------------------------------- rad表示當前點數的弧度
        const x = Math.cos(rad) * (arcRadius - 60) * rate;
        const y = Math.sin(rad) * (arcRadius - 60) * rate;
        context.fillText(item, x, y) // ------------------------ 填充數字
      })
    }

    function clockDot() { // 60份的刻度,和上面同樣
      for(let i = 0; i < 60; i++) {
        const rad = 2 * Math.PI / 60 * i;
        const x = Math.cos(rad) * ( arcRadius - 34) * rate;
        const y = Math.sin(rad) * ( arcRadius - 34) * rate;
        context.beginPath()
        context.arc(x, y, 4 * rate, 0, 360 * Math.PI/180)
        if (i % 5 === 0) {
          context.fillStyle = '#000' // 整點的數字對應的刻度顏色高亮
        } else {
          context.fillStyle = '#ccc'
        }
        context.fill() // -------------------------------------- 畫圓填充顏色
      }
    }

    function clockPointer(hour, minute, second) { // ----------- 時針分針秒針
      // 時針
      const radHour = 2 * Math.PI / 12 * hour; // ----------------------------------------- 一小時的弧度
      const radHourMinute = 2 * Math.PI / 12 / 60 * minute; // ---------------------------- 一分鐘的弧度
      const radHourtMinuteSecond = 2 * Math.PI / 12 / 60 / 60 * second; // ---------------- 一秒鐘的弧度
      context.save() // --------------------- 保存當前環境狀態,由於畫別的分針,秒針時不能用如今的旋轉後的環境
      context.rotate(radHour + radHourMinute + radHourtMinuteSecond) // -------------------- 旋轉的總角度
      context.beginPath()
      context.lineWidth = 10 * rate;
      context.lineCap = 'round';
      context.strokeStyle='black'
      context.moveTo(0, 10 * rate)
      context.lineTo(0, (-arcRadius/2 + 20) * rate)
      context.stroke()
      context.restore() // ---------------------------------------------------- 旋轉後,獲取旋轉以前的狀態

      // 分針
      const radMinute =  2 * Math.PI / 60 * minute;
      context.save()
      context.beginPath()
      context.rotate(radMinute)
      context.lineCap = 'round'
      context.strokeStyle='black'
      context.lineWidth = 6 * rate;
      context.moveTo(0, 10 * rate)
      context.lineTo(0, (-arcRadius + 120) * rate)
      context.stroke()
      context.restore()

      // 秒針
      const radSecond = 2 * Math.PI / 60 * second;
      context.save()
      context.beginPath()
      context.rotate(radSecond)
      context.lineCap = 'round'
      context.lineWidth = 5 * rate;
      context.moveTo(-3 * rate, 14 * rate)
      context.lineTo(3 * rate, 14 * rate)
      context.lineTo(1* rate, (-arcRadius + 90) * rate)
      context.lineTo(-1* rate, (-arcRadius + 90) * rate)
      context.fillStyle = 'blue'
      context.fill()
      context.restore()

      // 圓點
      context.beginPath()
      context.arc(0, 0, 4 * rate, 0, 2 * Math.PI)
      context.fillStyle = '#fff'
      context.fill()
    }

    clockDot()
    clockBorder()
    clockNumber()
    clockPointer(4, 15, 60)


    setInterval(() => {
      context.clearRect(-300, -300, 600, 600) // ------------------------------ 清除後,重新繪製
      clockBorder()
      clockDot()
      clockNumber()
      const date = new Date()
      const hour = date.getHours()
      const minute = date.getMinutes()
      const second = date.getSeconds()
      clockPointer(hour, minute, second)
    }, 1000)
  </script>
</body>
</html>

複製代碼

實例4:縮放圖像

HTML5滑動條

  • <input type="range"> 滑動條
  • 注意:input標籤是單標籤,即沒有結束標籤
  • html單標籤有:<input><img><link><br><hr><meta>

<input type="range" min="1" max="10" step="1" value="3" />

  • min:容許的最小值
  • max:容許的最大值
  • step:數字間隔
  • value:默認值

context.drawImage() --- 重點理解這9個參數

  • context.drawImage(img,sx,sy,swidth,sheight,x,y,width,height)
  • sx:要裁剪的原圖像的起始點x座標
  • sy:要裁剪的原圖像的起始點y座標
  • swidth:要裁剪的原圖像的寬度
  • sheight:要裁剪的原圖像的高度
  • x:裁剪完的圖像要放在canvas上的起始點的x座標
  • y:裁剪完的圖像要放在canvas上的起始點的y座標
  • width:裁剪完的圖像要放在canvas上的寬度
  • height:裁剪完的圖像要放在canvas上的高度
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body style="background: #777;">
  <canvas id="canvas" width="400" height="400"
    style="border: 1px solid red; display: block; margin: 0 auto;"
  ></canvas>
  <input
    type="range"
    id="range"
    style="display: block;margin: 20px auto;width: 400px"
    min="0.1"
    max="3.0"
    step="0.01"
    value="1.0"
  >
  <script>
    const canvas = document.getElementById('canvas')
    const slider = document.getElementById('range')
    const context = canvas.getContext('2d')

    const img = new Image()
    img.src = './002.jpg'
    if (img.complete) {
      init()
    } else {
      img.onload = init
    }

    function init() {
      const scale = slider.value
      drawImageByScale(scale)
    }

    function drawImageByScale(scale) {
      const imgWidth = img.width * scale;
      const imgHeight = img.height * scale;
      const canvasWidth = canvas.width;
      const canvasHeight = canvas.height;
      const dx = canvasWidth/2 - imgWidth/2;
      const dy = canvasHeight/2 - imgHeight/2;

      context.clearRect(0, 0, canvasWidth, canvasWidth)
      context.save()
      context.beginPath()
      context.drawImage(img, dx, dy, imgWidth, imgHeight)
      context.restore()
    }

    slider.onmousemove = function() {
      const scale = slider.value
      drawImageByScale(scale)
    }
  </script>
</body>
</html>
複製代碼

實例5:刮刮卡

content.globalCompositeOperation

  • context.globalCompositeOperation = 'source-over' 設置如何將源(新的)圖像設置到目標(已有)圖像上
  • Composite:組合,合成
  • destination:目標,終點 n
  • 紅色色表示源(新的)圖像,藍色表示目標(已有)圖像

context.lineJoin

  • context.lineJoin = 'round' 設置當兩條線相交時,邊角的類型
  • context.linWidth = 40
  • context.linCap = 'round'

canvas中獲取鼠標的座標有很大的偏移,不精確?

  • canvas的寬高必須在canvas標籤中設置,不能用css設置,否則會偏移

代碼

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    #canvas { // 設置canvas的背景,能夠用別的圖片做爲底層,將canvas移動到圖片上重疊
      background-image: url('./4.jpg');
      background-position: center;
      background-repeat: no-repeat;
      background-size: cover;
      border: 1px solid red;
    }
  </style>
</head>
<body>
  <canvas id="canvas" width=400 height=330></canvas>
  <script>
    const canvas = document.getElementById('canvas')
    const canvasWidth = canvas.width
    const canvasHeight = canvas.height
    if (canvas.getContext) { // ----------------------------------- 瀏覽器是否支持canvas
      const context = canvas.getContext('2d')
      const img = new Image()
      img.src = './002.jpg'
      if (img.complete) { // --------------------------------- if...else保證圖標加載完成後執行drawImages()
        drawImages()
      } else {
        img.onload = drawImages
      }

      let isEmit = false // 用於標誌移動時時候能夠畫線條了,由於只有鼠標按下後或者touch後生效,結束後又設爲false
      let drawDots = 0 // 用來記錄如今畫的線段後,canvas源圖像透明的點,>2/3則顯示整個canvas背景


      function drawImages() {
        context.drawImage(img, 0, 0, canvas.width, canvas.height)
        context.globalCompositeOperation = 'destination-out' 
        // ----------------------------------  context.globalCompositeOperation 
        // ---------------------------------- 'destination-out' 目標圖被源圖佔據的部分將透明

        // 監聽鼠標和touch事件
        canvas.addEventListener('mousedown', moveStart, false);
        canvas.addEventListener('touchstart', moveStart, false);
        canvas.addEventListener('mousemove', move, false);
        canvas.addEventListener('touchmove', move, false);
        canvas.addEventListener('mouseup', moveEnd, false);
        canvas.addEventListener('touchend', moveEnd, false);
        context.lineWidth = 30
        context.lineCap = 'round'
        context.lineJoin = 'round'
        context.strokeStyle = 'white'
      }

      function moveStart(e) {
        isEmit = true // 點擊後才准許移動時畫圖
        drawLineFn(e)
      }
      function move(e) {
        if (!isEmit) return; // 不成立,則返回
        drawLineFn(e)
      }
      function moveEnd(e) {
        isEmit = false // 結束後移動不能再畫圖
        drawLineFn(e)
        paintAll() // 判斷是否所有顯示背景圖,當畫到必定程度,直接能夠顯示所有
      }
      function getDot(e) {
        const dotx = e.type.match('mouse') ? e.clientX : e.changedTouches[0].clientX;
        const doty = e.type.match('mouse') ? e.clientY : e.changedTouches[0].clientY;
        return { dotx, doty }
      }
      function drawLineFn(e) {
        const {dotx, doty} = getDot(e)
        context.save()
        context.beginPath()
        context.moveTo(dotx, doty)
        context.lineTo(dotx + 0.11, doty + 0.1) // 畫線
        context.stroke()
        context.closePath()
        context.restore()
      }
      function paintAll() {
        const imageData = context.getImageData(0, 0, canvasWidth, canvasHeight)
        const allDots = imageData.width * imageData.height; // ----------- 圖片全部的點
        for(let i = 0; i < allDots; i++) {
          if(imageData.data[i*4 + 3] === 0) { // ------------------------- 統計透明的點
            drawDots++ // 統計透明的點
          }
        }
        if (drawDots > allDots * 2/3) { // 透明佔總數點的比例
          context.save()
          context.beginPath()
          context.fillRect(0, 0, canvasWidth, canvasHeight) // 用源圖佔滿整個目標圖,globalCompositeOperation的運用
          context.closePath()
          context.restore()
        }
      }
    };
  </script>
</body>
</html>
複製代碼

資料

canvas-api:www.w3school.com.cn/tags/html_r…
MDN:developer.mozilla.org/zh-CN/docs/…
ImageData對象:developer.mozilla.org/zh-CN/docs/…
canvas轉成圖片保存:segmentfault.com/a/119000001…
粒子動畫3:juejin.im/post/57e7a7…
粒子動畫1:juejin.im/post/57cda0…
時鐘動畫:www.imooc.com/video/11261
globalCompositeOperation1:www.w3school.com.cn/tags/canvas…
globalCompositeOperation2:www.w3school.com.cn/tiy/t.asp?f…
刮刮卡1:juejin.im/post/5ca18a…
刮刮卡2:juejin.im/post/5d8a3d…vue

相關文章
相關標籤/搜索