欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue + canvas實(shí)現(xiàn)涂鴉面板的示例代碼

 更新時間:2023年08月03日 10:41:29   作者:柏成  
這篇文章主要給大家介紹了vue + canvas實(shí)現(xiàn)涂鴉面板的示例,文章通過代碼示例介紹的非常詳細(xì),感興趣的小伙伴跟著小編一起來看看吧

此篇文章用于記錄柏成從零開發(fā)一個canvas涂鴉面板的歷程,最終效果如下:

canvas涂鴉面板_min.gif

介紹

我們基于 canvas 實(shí)現(xiàn)了一款簡單的涂鴉面板,用于在網(wǎng)頁上進(jìn)行繪圖和創(chuàng)作。其支持以下快捷鍵:

功能快捷鍵
撤銷Ctrl + Z
恢復(fù)Ctrl + Y

我們可以通過 new Board 創(chuàng)建一個空白畫板,其接收一個容器作為參數(shù),下面是個基本例子:

<template>
  <div class="drawing-board">
    <div id="container" ref="container" style="width: 100%; height: 100%"></div>
  </div>
</template>
<script setup>
  import { ref, onMounted } from 'vue'
  import Board from '@/canvas/board.js'
  const container = ref(null)
  onMounted(() => {
    // 創(chuàng)建一個空白畫板
    new Board(container.value)
  })
</script>

初始化

Board 的實(shí)現(xiàn)是一個類,在 src/canvas/board.js中定義。

new Board(container)時做了什么?我們在構(gòu)造函數(shù)中創(chuàng)建一個 canvas 畫布追加到了 container 容器中,并定義了一系列屬性,最后執(zhí)行了 init 初始化方法。

在初始化方法中,我們設(shè)置了畫筆樣式(其實(shí)可以動態(tài)去設(shè)置,讓用戶選擇畫筆顏色、粗細(xì)、線條樣式等,時間有限,未實(shí)現(xiàn)此功能);注冊監(jiān)聽了鼠標(biāo)鍵盤事件,用于繪制畫筆軌跡和實(shí)現(xiàn)撤銷恢復(fù)快捷鍵操作。

export default class BoardCanvas {
  constructor(container) {
    // 容器
    this.container = container
    // canvas畫布
    this.canvas = this.createCanvas(container)
    // 繪制工具
    this.ctx = this.canvas.getContext('2d')
    // 起始點(diǎn)位置
    this.startX = 0
    this.stateY = 0
    // 畫布?xì)v史棧
    this.pathSegmentHistory = []
    this.index = 0
    // 初始化
    this.init()
  }
  // 創(chuàng)建畫布
  createCanvas(container) {
    const canvas = document.createElement('canvas')
    canvas.width = container.clientWidth
    canvas.height = container.clientHeight
    canvas.style.display = 'block'
    canvas.style.backgroundColor = 'antiquewhite'
    container.appendChild(canvas)
    return canvas
  }
  // 初始化
  init() {
    this.addPathSegment()
    this.setContext2DStyle()
    // 阻止默認(rèn)右擊事件
    this.canvas.addEventListener('contextmenu', (e) => e.preventDefault())
    // 自定義鼠標(biāo)按下事件
    this.canvas.addEventListener('mousedown', this.mousedownEvent.bind(this))
    // 自定義鍵盤按下事件
    window.document.addEventListener('keydown', this.keydownEvent.bind(this))
  }
  // 設(shè)置畫筆樣式
  setContext2DStyle() {
    this.ctx.strokeStyle = '#EB7347'
    this.ctx.lineWidth = 3
    this.ctx.lineCap = 'round'
    this.ctx.lineJoin = 'round'
  }
}

自定義鼠標(biāo)事件

我們之前在 init 初始化方法中注冊了 onmousedown 鼠標(biāo)按下事件,需要在此處實(shí)現(xiàn)鼠標(biāo)按下拖拽可以繪制畫筆軌跡的邏輯

mousedownEvent(e) {
  const that = this
  const ctx = this.ctx
  ctx.beginPath()
  ctx.moveTo(e.offsetX, e.offsetY)
  ctx.stroke()
  this.canvas.onmousemove = function (e) {
    ctx.lineTo(e.offsetX, e.offsetY)
    ctx.stroke()
  }
  this.canvas.onmouseup = this.canvas.onmouseout = function () {
    that.addPathSegment()
    this.onmousemove = null
    this.onmouseup = null
    this.onmouseout = null
  }
}

自定義鍵盤事件

我們之前在 init 初始化方法中注冊了 onkeydown 鍵盤按下事件,需要在此處實(shí)現(xiàn)撤銷恢復(fù)的邏輯

// 鍵盤事件
keydownEvent(e) {
  if (!e.ctrlKey) return
  switch (e.keyCode) {
    case 90:
      this.undo()
      break
    case 89:
      this.redo()
      break
  }
}

要實(shí)現(xiàn)撤銷恢復(fù)操作,我們需要一個存儲畫布快照的棧!這又涉及到兩個問題,我們?nèi)绾潍@取到當(dāng)前畫布快照?如何根據(jù)快照數(shù)據(jù)恢復(fù)畫布?

查閱 canvas官方API文檔 得知,獲取快照 API 為 getImageData;通過快照恢復(fù)畫布的 API 為 putImageData

/*
 * @name 返回一個 ImageData 對象,其中包含 Canvas 畫布部分或完整的像素點(diǎn)信息
 * @param { Number } sx 將要被提取的圖像數(shù)據(jù)矩形區(qū)域的左上角 x 坐標(biāo)
 * @param { Number } sy 將要被提取的圖像數(shù)據(jù)矩形區(qū)域的左上角 y 坐標(biāo)
 * @param { Number } sWidth  將要被提取的圖像數(shù)據(jù)矩形區(qū)域的寬度
 * @param { Number } sHeight 將要被提取的圖像數(shù)據(jù)矩形區(qū)域的高度
 * @return { Object } 返回一個 ImageData 對象,包含 Canvas 給定的矩形圖像像素點(diǎn)信息
 */
context.getImageData(sx, sy, sWidth, sHeight);
 /*
 * @name 將給定 ImageData 對象的數(shù)據(jù)繪制到位圖上
 * @param { Object } ImageData 對象,包含 Canvas 給定的矩形圖像像素點(diǎn)信息
 * @param { Number } dx 目標(biāo) Canvas 中被圖像數(shù)據(jù)替換的起點(diǎn)橫坐標(biāo)
 * @param { Number } dy 目標(biāo) Canvas 中被圖像數(shù)據(jù)替換的起點(diǎn)縱坐標(biāo)
 */
 context.putImageData(ImageData, dx, dy);

我們對保存畫布快照的邏輯進(jìn)行了一次封裝,如下:

// 添加路徑片段
addPathSegment() {
  const data = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height)
  // 刪除當(dāng)前索引后的路徑片段,然后追加一個新的路徑片段,更新索引
  this.pathSegmentHistory.splice(this.index + 1)
  this.pathSegmentHistory.push(data)
  this.index = this.pathSegmentHistory.length - 1
}

我們在構(gòu)造函數(shù)中定義了一個存儲畫布快照的棧 - pathSegmentHistory;一個指向棧中當(dāng)前快照的索引 - index

在初始化和繪制一個路徑片段結(jié)束時都會調(diào)用 addPathSegment 方法,用于保存當(dāng)前畫布快照到棧中,并將索引指向棧中的最后一個成員

Tip:在保存快照數(shù)據(jù)之前,我們會先刪除棧中位于索引之后的全部快照數(shù)據(jù),目的是執(zhí)行撤銷操作后再繪制軌跡,要清空棧中的多余數(shù)據(jù)。舉個栗子,如果我們先執(zhí)行3次undo,再執(zhí)行一次redo,最后繪制一條新的軌跡,則需要先清除棧中的最后兩條快照數(shù)據(jù),再添加一條新的當(dāng)前畫布快照數(shù)據(jù),示意圖如下

空白視圖-undo-redo-add.png

撤銷(undo)

當(dāng)執(zhí)行 undo 操作時,我們先將索引前移, 然后取出當(dāng)前索引指向的快照數(shù)據(jù),重新繪制畫布

// 撤銷
undo() {
  if (this.index <= 0) return
  this.index--
  this.ctx.putImageData(this.pathSegmentHistory[this.index], 0, 0)
}

空白視圖-undo.png

恢復(fù)(redo)

當(dāng)執(zhí)行 redo 操作時,我們先將索引后移, 然后取出當(dāng)前索引指向的快照數(shù)據(jù),重新繪制畫布

// 恢復(fù)
redo() {
  if (this.index >= this.pathSegmentHistory.length - 1) return
  this.index++
  this.ctx.putImageData(this.pathSegmentHistory[this.index], 0, 0)
}

空白視圖-redo.png

源碼

涂鴉面板demo代碼:vue-canvas

到此這篇關(guān)于vue + canvas實(shí)現(xiàn)涂鴉面板的示例代碼的文章就介紹到這了,更多相關(guān)vue + canvas涂鴉面板內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論