最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

canvas涂鴉面板_min.gif

介紹

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

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

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

<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)建一個(gè)空白畫板
    new Board(container.value)
  })
</script>

初始化

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

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

在初始化方法中,我們?cè)O(shè)置了畫筆樣式(其實(shí)可以動(dòng)態(tài)去設(shè)置,讓用戶選擇畫筆顏色、粗細(xì)、線條樣式等,時(shí)間有限,未實(shí)現(xiàn)此功能);注冊(cè)監(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 初始化方法中注冊(cè)了 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 初始化方法中注冊(cè)了 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ù)操作,我們需要一個(gè)存儲(chǔ)畫布快照的棧!這又涉及到兩個(gè)問(wèn)題,我們?nèi)绾潍@取到當(dāng)前畫布快照?如何根據(jù)快照數(shù)據(jù)恢復(fù)畫布?

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

/*
 * @name 返回一個(gè) ImageData 對(duì)象,其中包含 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 } 返回一個(gè) ImageData 對(duì)象,包含 Canvas 給定的矩形圖像像素點(diǎn)信息
 */
context.getImageData(sx, sy, sWidth, sHeight);
 /*
 * @name 將給定 ImageData 對(duì)象的數(shù)據(jù)繪制到位圖上
 * @param { Object } ImageData 對(duì)象,包含 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);

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

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

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

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

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

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

撤銷(undo)

當(dāng)執(zhí)行 undo 操作時(shí),我們先將索引前移, 然后取出當(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 操作時(shí),我們先將索引后移, 然后取出當(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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Vue.js實(shí)現(xiàn)可編輯的表格

    Vue.js實(shí)現(xiàn)可編輯的表格

    這篇文章主要為大家詳細(xì)介紹了Vue.js實(shí)現(xiàn)可編輯的表格,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • 詳解Vite的新體驗(yàn)

    詳解Vite的新體驗(yàn)

    這篇文章主要介紹了詳解Vite的新體驗(yàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • vue實(shí)現(xiàn)壓縮圖片預(yù)覽并上傳功能(promise封裝)

    vue實(shí)現(xiàn)壓縮圖片預(yù)覽并上傳功能(promise封裝)

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)壓縮圖片預(yù)覽并上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • 關(guān)于vue-cli-service:command?not?found報(bào)錯(cuò)引發(fā)的實(shí)戰(zhàn)案例

    關(guān)于vue-cli-service:command?not?found報(bào)錯(cuò)引發(fā)的實(shí)戰(zhàn)案例

    這篇文章主要給大家介紹了關(guān)于vue-cli-service:command?not?found報(bào)錯(cuò)引發(fā)的相關(guān)資料,文中通過(guò)實(shí)例代碼將解決的過(guò)程介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-02-02
  • vue點(diǎn)擊右鍵出現(xiàn)自定義操作菜單實(shí)現(xiàn)代碼

    vue點(diǎn)擊右鍵出現(xiàn)自定義操作菜單實(shí)現(xiàn)代碼

    這篇文章主要給大家介紹了關(guān)于vue點(diǎn)擊右鍵出現(xiàn)自定義操作菜單實(shí)現(xiàn)的相關(guān)資料,在網(wǎng)頁(yè)中我們也希望可以像桌面軟件一樣,點(diǎn)擊右鍵后出現(xiàn)操作菜單,對(duì)選中的數(shù)據(jù)項(xiàng)進(jìn)行相應(yīng)的操作,需要的朋友可以參考下
    2023-08-08
  • Vue路由回退頁(yè)面不刷新的原因及解決方案匯總

    Vue路由回退頁(yè)面不刷新的原因及解決方案匯總

    在Vue開發(fā)過(guò)程中,常常會(huì)碰到這樣一種情形:從頁(yè)面A跳轉(zhuǎn)到頁(yè)面B后,點(diǎn)擊瀏覽器回退按鈕返回頁(yè)面A時(shí),頁(yè)面數(shù)據(jù)卻未刷新,依舊保持之前的狀態(tài),這一情況可能會(huì)給用戶帶來(lái)困擾,對(duì)用戶體驗(yàn)產(chǎn)生不良影響,本文將深入探討如何妥善處理此類問(wèn)題,以保證回退頁(yè)面時(shí)數(shù)據(jù)能夠準(zhǔn)確更新
    2024-11-11
  • 利用vuex-persistedstate將vuex本地存儲(chǔ)實(shí)現(xiàn)

    利用vuex-persistedstate將vuex本地存儲(chǔ)實(shí)現(xiàn)

    這篇文章主要介紹了利用vuex-persistedstate將vuex本地存儲(chǔ)的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • 五種Vue實(shí)現(xiàn)加減乘除運(yùn)算的方法總結(jié)

    五種Vue實(shí)現(xiàn)加減乘除運(yùn)算的方法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了五種Vue實(shí)現(xiàn)加減乘除運(yùn)算的方法,文中的示例代碼簡(jiǎn)潔易懂,對(duì)我們深入了解vue有一定的幫助,需要的可以了解下
    2023-08-08
  • vue使用formData時(shí)候傳遞參數(shù)是個(gè)空值的情況處理

    vue使用formData時(shí)候傳遞參數(shù)是個(gè)空值的情況處理

    這篇文章主要介紹了vue使用formData時(shí)候傳遞參數(shù)是個(gè)空值的情況處理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • element可編輯表格驗(yàn)證問(wèn)題解決

    element可編輯表格驗(yàn)證問(wèn)題解決

    本文主要介紹了element可編輯表格驗(yàn)證問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05

最新評(píng)論

旬邑县| 涟源市| 邯郸市| 阳东县| 大庆市| 五原县| 凤翔县| 五常市| 保德县| 小金县| 仙桃市| 墨江| 龙江县| 石城县| 永德县| 防城港市| 孝感市| 天等县| 双鸭山市| 民县| 措勤县| 连平县| 淮阳县| 鸡泽县| 洛浦县| 淮北市| 丁青县| 灵山县| 富锦市| 肃南| 安龙县| 广河县| 高台县| 深州市| 远安县| 长垣县| 新平| 苍山县| 扬州市| 皋兰县| 周口市|