溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

HTML5 canvas如何實現畫圖程序

發布時間:2020-07-11 14:53:38 來源:億速云 閱讀:231 作者:Leah 欄目:web開發

這篇文章將為大家詳細講解有關HTML5 canvas如何實現畫圖程序,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

項目簡介

整個項目分為兩大部分

  1. 場景
    場景負責canvas控制,事件監聽,動畫處理

  2. 精靈
    精靈則指的是每一種可以繪制的canvas元素

Demo演示地址

項目特點

可擴展性強

sprite精靈實現

父類

class Element {
  constructor(options = {
    fillStyle: 'rgba(0,0,0,0)',
    lineWidth: 1,
    strokeStyle: 'rgba(0,0,0,255)'
  }) {
    this.options = options
  }
  setStyle(options){
    this.options =  Object.assign(this.options. options)
  }
}
  1. 屬性:

  • options中存儲了所有的繪圖屬性

    • fillStyle:設置或返回用于填充繪畫的顏色、漸變或模式

    • strokeStyle:設置或返回用于筆觸的顏色、漸變或模式

    • lineWidth:設置或返回當前的線條寬度

    • 使用的都是getContext("2d")對象的原生屬性,此處只列出了這三種屬性,需要的話還可以繼續擴充。

  • 有需要可以繼續擴充

  1. 方法:

  • setStyle方法用于重新設置當前精靈的屬性

  • 有需要可以繼續擴充

所有的精靈都繼承Element類。

子類

子類就是每一種精靈元素的具體實現,這里我們介紹一遍Circle元素的實現

class Circle extends Element {
  // 定位點的坐標(這塊就是圓心),半徑,配置對象
  constructor(x, y, r = 0, options) {
    // 調用父類的構造函數
    super(options)
    this.x = x
    this.y = y
    this.r = r
  }
  // 改變元素大小
  resize(x, y) {
    this.r = Math.sqrt((this.x - x) ** 2 + (this.y - y) ** 2)
  }
  // 移動元素到新位置,接收兩個參數,新的元素位置
  moveTo(x, y) {
    this.x = x
    this.y = y
  }
  // 判斷點是否在元素中,接收兩個參數,點的坐標
  choose(x, y) {
    return ((x - this.x) ** 2 + (y - this.y) ** 2) < (this.r ** 2)
  }
  // 偏移,計算點和元素定位點的相對偏移量(ofsetX, offsetY)
  getOffset(x, y) {
    return {
      x: x - this.x,
      y: y - this.y
    }
  }
  // 繪制元素實現,接收一個ctx對象,將當前元素繪制到指定畫布上
  draw(ctx) {
    // 取到繪制所需屬性
    let {
      fillStyle,
      strokeStyle,
      lineWidth
    } = this.options
    // 開始繪制beginPath() 方法開始一條路徑,或重置當前的路徑
    ctx.beginPath()
    // 設置屬性
    ctx.fillStyle = fillStyle
    ctx.strokeStyle = strokeStyle
    ctx.lineWidth = lineWidth
    // 畫圓
    ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI)
    // 填充顏色
    ctx.stroke()
    ctx.fill()
    // 繪制完成
  }
  // 驗證函數,判斷當前元素是否滿足指定條件,此處用來檢驗是否將元素添加到場景中。
  validate() {
    return this.r >= 3
  }
}

arc() 方法創建弧/曲線(用于創建圓或部分圓)

  • x    圓的中心的 x 坐標。

  • y    圓的中心的 y 坐標。

  • r    圓的半徑。

  • sAngle    起始角,以弧度計。(弧的圓形的三點鐘位置是 0 度)。

  • eAngle    結束角,以弧度計。

  • counterclockwise    可選。規定應該逆時針還是順時針繪圖。False = 順時針,true = 逆時針。

注意事項:

  • 構造函數的形參只有兩個是必須的,就是定位點的坐標。

  • 其它的形參都必須有默認值。

所有方法的調用時機

  • 我們在畫布上繪制元素的時候回調用resize方法。

  • 移動元素的時候調用moveTo方法。

  • choose會在鼠標按下時調用,判斷當前元素是否被選中。

  • getOffset選中元素時調用,判斷選中位置。

  • draw繪制函數,繪制元素到場景上時調用。

scene場景的實現

  1. 屬性介紹

class Sence {
  constructor(id, options = {
    width: 600,
    height: 400
  }) {
    // 畫布屬性
    this.canvas = document.querySelector('#' + id)
    this.canvas.width = options.width
    this.canvas.height = options.height
    this.width = options.width
    this.height = options.height
    // 繪圖的對象
    this.ctx = this.canvas.getContext('2d')
    // 離屏canvas
    this.outCanvas = document.createElement('canvas')
    this.outCanvas.width = this.width
    this.outCanvas.height = this.height
    this.outCtx = this.outCanvas.getContext('2d')
    // 畫布狀態
    this.stateList = {
      drawing: 'drawing',
      moving: 'moving'
    }
    this.state = this.stateList.drawing
    // 鼠標狀態
    this.mouseState = {
    // 記錄鼠標按下時的偏移量
      offsetX: 0,
      offsetY: 0,
      down: false, //記錄鼠標當前狀態是否按下
      target: null //當前操作的目標元素
    }
    // 當前選中的精靈構造器
    this.currentSpriteConstructor = null
    // 存儲精靈
    let sprites = []
    this.sprites = sprites
    /* .... */
  }
}
  1. 事件邏輯

class Sence {
  constructor(id, options = {
    width: 600,
    height: 400
  }) {
  /* ... */
  // 監聽事件
    this.canvas.addEventListener('contextmenu', (e) => {
      console.log(e)
    })
    // 鼠標按下時的處理邏輯
    this.canvas.addEventListener('mousedown', (e) => {
    // 只有左鍵按下時才會處理鼠標事件
      if (e.button === 0) {
      // 鼠標的位置
        let x = e.offsetX
        let y = e.offsetY
        // 記錄鼠標是否按下
        this.mouseState.down = true
        // 創建一個臨時target
        // 記錄目標元素
        let target = null
        if (this.state === this.stateList.drawing) {
        // 判斷當前有沒有精靈構造器,有的話就構造一個對應的精靈元素
          if (this.currentSpriteConstructor) {
            target = new this.currentSpriteConstructor(x, y)
          }
        } else if (this.state === this.stateList.moving) {
          let sprites = this.sprites
          // 遍歷所有的精靈,調用他們的choose方法,判斷有沒有被選中
          for (let i = sprites.length - 1; i >= 0; i--) {
            if (sprites[i].choose(x, y)) {
              target = sprites[i]
              break;
            }
          }
          
          // 如果選中的話就調用target的getOffset方法,獲取偏移量
          if (target) {
            let offset = target.getOffset(x, y)
            this.mouseState.offsetX = offset.x
            this.mouseState.offsetY = offset.y
          }
        }
        // 存儲當前目標元素
        this.mouseState.target = target
        // 在離屏canvas保存除目標元素外的所有元素
        let ctx = this.outCtx
        // 清空離屏canvas
        ctx.clearRect(0, 0, this.width, this.height)
        // 將目標元素外的所有的元素繪制到離屏canvas中
        this.sprites.forEach(item => {
          if (item !== target) {
            item.draw(ctx)
          }
        })
        if(target){
            // 開始動畫
            this.anmite()
        }
      }
    })
    this.canvas.addEventListener('mousemove', (e) => {
    //  如果鼠標按下且有目標元素,才執行下面的代碼
      if (this.mouseState.down && this.mouseState.target) {
        let x = e.offsetX
        let y = e.offsetY
        if (this.state === this.stateList.drawing) {
        // 調用當前target的resize方法,改變大小
          this.mouseState.target.resize(x, y)
        } else if (this.state === this.stateList.moving) {
        // 取到存儲的偏移量
          let {
            offsetX, offsetY
          } = this.mouseState
          // 調用moveTo方法將target移動到新的位置
          this.mouseState.target.moveTo(x - offsetX, y - offsetY)
        }
      }
    })
    document.body.addEventListener('mouseup', (e) => {
      if (this.mouseState.down) {
      // 將鼠標按下狀態記錄為false
        this.mouseState.down = false
        if (this.state === this.stateList.drawing) {
        // 調用target的validate方法。判斷他要不要被加到場景去呢
          if (this.mouseState.target.validate()) {
            this.sprites.push(this.mouseState.target)
          }
        } else if (this.state === this.stateList.moving) {
          // 什么都不做
        }
      }
    })
  }
}
  1. 方法介紹

class Sence {
// 動畫
  anmite() {
    requestAnimationFrame(() => {
      // 清除畫布
      this.clear()
      // 將離屏canvas繪制到當前canvas上
      this.paint(this.outCanvas)
      // 繪制target
      this.mouseState.target.draw(this.ctx)
      // 鼠標是按下狀態就繼續執行下一幀動畫
      if (this.mouseState.down) {
        this.anmite()
      }
    })
  }
  // 可以將手動的創建的精靈添加到畫布中
  append(sprite) {
    this.sprites.push(sprite)
    sprite.draw(this.ctx)
  }
  // 根據ID值,從場景中刪除對應元素
  remove(id) {
    this.sprites.splice(id, 1)
  }
  // clearRect清除指定區域的畫布內容
  clear() {
    this.ctx.clearRect(0, 0, this.width, this.height)
  }
  // 重繪整個畫布的內容
  reset() {
    this.clear()
    this.sprites.forEach(element => {
      element.draw(this.ctx)
    })
  }
  // 將離屏canvas繪制到頁面的canvas畫布上
  paint(canvas, x = 0, y = 0) {
    this.ctx.drawImage(canvas, x, y, this.width, this.height)
  }
  // 設置當前選中的精靈構造器
  setCurrentSprite(Element) {
    this.currentSpriteConstructor = Element
  }

關于HTML5 canvas如何實現畫圖程序就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女