uni-app實戰教程 從0到1開發 畫圖軟件 (橡皮擦)

一、本期內容簡述

1. 開發內容

上一期,我們一起學習了如何進行繪畫,本期我們將學習如何擦除我們所繪畫的內容,也就是“橡皮擦”功能。

首先,我們應該明確需求,橡皮擦可以擦除掉我們繪畫的內容。

2. 開發需求

所以開發需求:

(1)??擦除繪畫內容:

  • 單指觸摸屏幕并緩慢移動即可擦除

??(2)修改橡皮擦的形狀和大小??:

  • 可以選擇橡皮擦的形狀
  • 可以調整橡皮擦的大小

二、核心實現代碼

1. html

添加橡皮擦的預覽效果顯示

 <!-- 橡皮擦預覽 --><view class="eraser-preview":class="`shape-${eraserShape}`":style="{display: eraserPreviewVisible ? 'block' : 'none',left: `${eraserPreviewPos.x}px`,top: `${eraserPreviewPos.y}px`,width: `${eraserSize}px`,height: `${eraserSize}px`}"></view>

2. 常量定義

const currentMode = ref('draw') // 'draw' 或 'erase'
const eraserShapes = ref(['圓形', '方形'])
const eraserShapeIndex = ref(0) // 0: 圓形, 1: 方形

首先定義currentMode作為判斷當前是繪畫,還是使用橡皮擦的模式

定義橡皮擦的形狀,以及當前所選橡皮擦的索引值

3. 觸摸狀態

還是之前的核心三個方法的 內容,觸摸、觸摸中、觸摸結束

(1)handleTouchStart

你會發現,這次得如果是繪畫就是將單簽的位置添加到currentPaht中,如果是橡皮擦則記錄橡皮擦的位置,顯示橡皮擦,并

const handleTouchStart = async (e) => {if (!ensureContext()) returnisDrawing.value = trueconst point = {x: e.touches[0].x,y: e.touches[0].y}if (currentMode.value === 'draw') {// 開始新的繪圖路徑currentPath.value = [point]} else {// 橡皮擦模式eraserPreviewPos.value = { x: point.x, y: point.y }eraserPreviewVisible.value = trueeraseAtPoint(point)}
}

其中eraseAtPoint

// 跟蹤最后一個橡皮擦操作
let lastEraserOperation = null
let eraserTimeout = null// 在指定點進行擦除
const eraseAtPoint = (point) => {const size = eraserSize.valueconst halfSize = size / 2// 直接在畫布上繪制背景色來覆蓋原有內容ctx.value.setFillStyle('#ffffff') // 使用畫布背景色ctx.value.beginPath()if (eraserShape.value === 'circle') {// 圓形橡皮擦ctx.value.arc(point.x, point.y, halfSize, 0, 2 * Math.PI)} else {// 方形橡皮擦ctx.value.rect(point.x - halfSize,point.y - halfSize,size,size)}ctx.value.fill()ctx.value.draw(true)// 優化:批量處理橡皮擦操作const currentTime = Date.now()// 如果有最近的橡皮擦操作,且時間間隔短、參數相同,則合并if (lastEraserOperation && currentTime - lastEraserOperation.time < 100 && lastEraserOperation.size === size && lastEraserOperation.shape === eraserShape.value) {// 添加當前點到最后一個橡皮擦操作lastEraserOperation.points.push({ x: point.x, y: point.y })} else {// 創建新的橡皮擦操作lastEraserOperation = {type: 'eraser',points: [{ x: point.x, y: point.y }],size: size,shape: eraserShape.value,time: currentTime}drawingHistory.value.push(lastEraserOperation)}// 清除之前的定時器if (eraserTimeout) {clearTimeout(eraserTimeout)}// 設置定時器,在一段時間不操作后重置最后一個橡皮擦操作eraserTimeout = setTimeout(() => {lastEraserOperation = null}, 200)
}

  • 執行擦除:在畫布上指定的 point 點,用橡皮擦的形狀和大小,覆蓋上背景色(白色),從而實現視覺上的擦除效果。
  • 記錄歷史:將這次擦除操作作為一個對象,高效地添加到 drawingHistory 數組中。這里的“高效”體現在它會合并短時間內連續發生的、參數相同的擦除操作,以避免歷史記錄數組變得過于龐大,影響后續的重繪和撤銷操作。
  • eraserTimeout 是一個計時器,它的核心作用是界定一次連續的、完整的橡皮擦操作。它通過一個“延遲重置”的機制,告訴程序:“如果用戶在短時間內(比如200毫秒)沒有再擦了,我們就認為他這次擦的動作已經結束了,下一次擦就是一次全新的動作了。”

(2)handleTouchMove

const handleTouchMove = async (e) => {if (!isDrawing.value || !ensureContext()) returnconst point = {x: e.touches[0].x,y: e.touches[0].y}if (currentMode.value === 'draw') {// 繪圖模式 - 添加點到當前路徑currentPath.value.push(point)// 優化:只繪制當前路徑的最后一段,而不是重繪整個畫布if (currentPath.value.length > 1) {const lastPoint = currentPath.value[currentPath.value.length - 2]const currentPoint = currentPath.value[currentPath.value.length - 1]ctx.value.setStrokeStyle(currentColor.value)ctx.value.setLineWidth(lineSize.value)ctx.value.setLineCap('round')ctx.value.setLineJoin('round')ctx.value.beginPath()ctx.value.moveTo(lastPoint.x, lastPoint.y)ctx.value.lineTo(currentPoint.x, currentPoint.y)ctx.value.stroke()ctx.value.draw(true)}} else {// 橡皮擦模式eraserPreviewPos.value = { x: point.x, y: point.y }eraseAtPoint(point)}
}

(3)handleTouchEnd

觸摸結束

const handleTouchEnd = () => {if (!isDrawing.value) returnif (currentMode.value === 'draw' && currentPath.value.length > 0) {// 保存完成的繪圖路徑drawingHistory.value.push({type: 'draw',points: [...currentPath.value],color: currentColor.value,size: lineSize.value})}isDrawing.value = falsecurrentPath.value = []eraserPreviewVisible.value = false
}

drawingHistory 是一個“記憶庫”或“操作日志”。它記錄了用戶在畫布上執行的每一個繪圖和擦除動作。這使得應用能夠實現重繪、撤銷/重做(如果需要添加的話)以及最終保存等高級功能。

4. css

/* 橡皮擦預覽樣式 */
.eraser-preview {position: absolute;pointer-events: none;z-index: 9999;background-color: rgba(200, 200, 200, 0.3);border: 1px dashed #666;transform: translate(-50%, -50%);&.shape-circle {border-radius: 50%;}&.shape-square {border-radius: 0;}
}

      本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
      如若轉載,請注明出處:http://www.pswp.cn/news/919057.shtml
      繁體地址,請注明出處:http://hk.pswp.cn/news/919057.shtml
      英文地址,請注明出處:http://en.pswp.cn/news/919057.shtml

      如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

      相關文章

      《A Practical Guide to Building Agents》文檔學習

      《A Practical Guide to Building Agents》文檔總結 該文檔是一份面向產品和工程團隊的實用指南&#xff0c;旨在幫助團隊探索并構建首個基于大語言模型&#xff08;LLM&#xff09;的智能體&#xff08;Agent&#xff09;&#xff0c;提煉了大量客戶部署經驗&#xff0c;提供了…

      OpenCV圖像注冊模塊

      操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 注冊模塊實現了參數化圖像配準。所實現的方法是直接對齊&#xff08;direct alignment&#xff09;&#xff0c;即&#xff0c;它直接使用像素值來…

      模型驅動與分布式建模:技術深度與實戰落地指南

      摘要 在AI、云原生與全球化協作的大潮中&#xff0c;模型驅動架構&#xff08;MDA&#xff09;與分布式建模不再是概念&#xff0c;而是支撐復雜系統設計與持續演化的核心引擎。本文從元模型、模型轉換引擎&#xff0c;到協同協議、沖突解決算法&#xff0c;再到AI輔助建模與自…

      計算機基礎速通--數據結構·圖的基礎應用二(基礎圖算法)

      如有問題大概率是我的理解比較片面&#xff0c;歡迎評論區或者私信指正。 最近了解到了一個新的改變和提高自己的方法時刻記錄不論多小的事情都記下&#xff0c;我目前用了4天&#xff0c;之前感覺一天天忙死但沒啥收獲&#xff0c;但是記錄了之后知道自己的時間花在了哪里&…

      設計模式-策略模式 Java

      模式概述 策略模式是一種行為型設計模式&#xff0c;它通過定義一系列可互換的算法&#xff0c;并將每個算法封裝成獨立類&#xff0c;使客戶端能夠根據需要動態切換算法 簡單代碼示例 // 1. 抽象策略接口 interface PaymentStrategy {void pay(int amount); }// 2. 具體策略實…

      【機器學習深度學習】客觀評估訓練程度

      目錄 前言 一、什么是客觀評估&#xff1f; 二、客觀評估的兩大核心方法 1. 判別式評測&#xff08;Discriminative Evaluation&#xff09; 2. 生成式評測&#xff08;Generative Evaluation&#xff09; 三、為什么客觀評估成本更高&#xff1f; 1.訓練目標收緊 2.訓…

      Linux軟件編程:線程間通信

      目錄 一、線程間通信基礎 1. 概念 2. 通信基礎&#xff1a;共享空間 二、互斥鎖&#xff08;Mutex&#xff09; 1. 概念 2. 使用流程 3. 函數接口 三、死鎖 1. 概念 2. 死鎖產生的 4 個必要條件 3. 避免死鎖的方法 四、信號量&#xff08;Semaphore&#xff09; 1…

      【學習筆記】JVM GC回收機制

      1.三種基本的垃圾回收算法 1>標記-清除法 ①先將從樹根開始&#xff0c;可以到達的對象標記為可達&#xff08;JVM中的對象們存儲為一顆樹&#xff09; ②將沒有標記的對象清除掉 缺點&#xff1a;會產生大量內存碎片 2>復制算法&#xff08;新生代&#xff09; ①先將a區…

      軟件的終極:為70億人編寫70億個不同的軟件

      這是個腦洞大開的想法。昨天晚上&#xff0c;我在用Claude code幫我寫一個小工具&#xff0c;用來管理我本地那些亂七八糟的文檔。寫著寫著&#xff0c;突然意識到一個問題&#xff1a;這個工具完全是按照我的工作習慣定制的——我喜歡用Markdown&#xff0c;習慣把TODO放在文件…

      LakeHouse--湖倉一體架構

      大家可能發現了,近些年湖倉一體數據架構被提及的頻率越來越高。各家大廠也有湖倉一體架構的實踐,也有很多公開分享。 那什么是湖倉一體?為什么出現了湖倉一體架構,換言之,它解決了以前數據倉庫、數據湖+數倉兩層架構所不能解決的什么問題? 本文會從數倉、數據湖依次介紹…

      基于FPGA的實時圖像處理系統(1)——SDRAM回環測試

      SDRAM回環設計 文章目錄SDRAM回環設計一、SDRAM簡介1、引腳2、內部結構框圖3、操作指令二、系統設計三、實現流程1、SDRAM接口2、FIFO設置3、內部SDRAM的控制模塊4、其他四、實現效果五、總結六、代碼1、top2、sdram_top3、sdram_ctrl一、SDRAM簡介 SDRAM英文全稱“Synchronou…

      一鍵檢測接口是否存活:用 Python/Shell 寫個輕量級監控腳本

      網羅開發&#xff08;小紅書、快手、視頻號同名&#xff09;大家好&#xff0c;我是 展菲&#xff0c;目前在上市企業從事人工智能項目研發管理工作&#xff0c;平時熱衷于分享各種編程領域的軟硬技能知識以及前沿技術&#xff0c;包括iOS、前端、Harmony OS、Java、Python等方…

      優秀工具包-Hutool工具詳解

      優秀工具包-Hutool工具詳解 課程概述 Hutool簡介 定位&#xff1a; 小而全的Java工具庫&#xff0c;簡化開發流程。對文件、流、加密解密、轉碼、正則、線程、XML等JDK方法進行封裝。 核心優勢&#xff1a;零依賴、高性能、中文網頁完善。 應用場景&#xff1a;Web開發、數…

      《深度解構:構建瀏覽器端Redis控制臺的WebSocket協議核心技術》

      Redis作為高性能的內存數據庫,其原生客戶端多依賴命令行或桌面應用,而瀏覽器端控制臺的缺失,成為制約Web化管理的關鍵瓶頸,WebSocket協議的出現,打破了HTTP協議單向通信的局限,為瀏覽器與Redis服務之間建立持久、雙向的實時連接提供了可能。本文將從協議本質、交互邏輯、…

      Pushgateway安裝和部署,以及對應Prometheus調整

      目錄Pushgateway簡介安裝驗證Prometheus的配置&#xff1a;其它命令Pushgateway簡介 Pushgateway 是 Prometheus 生態系統中的一個組件。主要特點是推送而非拉取&#xff1a;Prometheus 默認采用拉取&#xff08;pull&#xff09;模式收集指標&#xff0c;但 Pushgateway 允許…

      JAVA面試匯總(四)JVM(一)

      久違的重新寫了一篇面試匯總的&#xff0c;關于JVM的一篇&#xff0c;一共三篇&#xff0c;今天寫了第一篇&#xff0c;繼續重新學習&#xff0c;重新卷起來&#xff0c;come on baby 1.什么情況下會觸發類的初始化&#xff1f; &#xff08;1&#xff09;首先是類未被初始化時…

      Agent中的memory

      rag系列文章目錄 文章目錄rag系列文章目錄前言一、Memory機制作用二、memory分類三、langgraph實踐總結前言 眾所周知&#xff0c;大模型是無狀態的。但是基于大模型的agent一般是有狀態的&#xff0c;也就是它有記憶功能。在AI Agent框架中&#xff0c;Memory機制是核心組件之…

      AI與IT從業者的未來:替代焦慮還是協作革命?

      ??引言&#xff1a;技術滲透與核心命題??2025年&#xff0c;人工智能技術已從實驗室走向產業核心。國務院《關于深入實施“人工智能”行動的意見》推動AI在醫療、制造、金融等領域的規模化落地&#xff0c;全球AI應用用戶規模突破2.3億&#xff0c;生成式AI工具滲透率達16.…

      手機版碰一碰發視頻系統批量剪輯功能開發,支持OEM貼牌

      引言在當今短視頻盛行的時代&#xff0c;視頻內容的快速生產與分享變得愈發重要。手機版碰一碰發視頻系統&#xff0c;借助 NFC 等近場通信技術&#xff0c;實現了便捷的數據交互與視頻分享&#xff0c;而在此基礎上集成的批量剪輯功能&#xff0c;更是為內容創作者和商家帶來了…

      Spring AMQP如何通過配置文件避免硬編碼實現解耦

      在使用Spring AMQP基于注解聲明監聽者時&#xff0c;可通過抽取常量來避免硬編碼&#xff1a;RabbitListener(bindings QueueBinding(exchange Exchange(MQConstant.USER_EXCHANGE),value Queue(MQConstant.USER_QUEUE),key MQConstant.USER_REDIS_BINDING))public void de…