摘要:方法創(chuàng)建弧曲線用于創(chuàng)建圓或部分圓圓的中心的坐標(biāo)。弧的圓形的三點鐘位置是度。規(guī)定應(yīng)該逆時針還是順時針繪圖。注意事項構(gòu)造函數(shù)的形參只有兩個是必須的,就是定位點的坐標(biāo)。選中元素時調(diào)用,判斷選中位置。
面向?qū)ο蟮腸anvas畫圖程序 項目簡介
整個項目分為兩大部分
場景
場景負(fù)責(zé)canvas控制,事件監(jiān)聽,動畫處理
精靈
精靈則指的是每一種可以繪制的canvas元素
Demo演示地址
Demo為最新代碼
可擴展性強
sprite精靈實現(xiàn) 父類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) } }
屬性:
options中存儲了所有的繪圖屬性
fillStyle:設(shè)置或返回用于填充繪畫的顏色、漸變或模式
strokeStyle:設(shè)置或返回用于筆觸的顏色、漸變或模式
lineWidth:設(shè)置或返回當(dāng)前的線條寬度
使用的都是getContext("2d")對象的原生屬性,此處只列出了這三種屬性,需要的話還可以繼續(xù)擴充。
有需要可以繼續(xù)擴充
方法:
setStyle方法用于重新設(shè)置當(dāng)前精靈的屬性
有需要可以繼續(xù)擴充
所有的精靈都繼承Element類。
子類子類就是每一種精靈元素的具體實現(xiàn),這里我們介紹一遍Circle元素的實現(xiàn)
class Circle extends Element { // 定位點的坐標(biāo)(這塊就是圓心),半徑,配置對象 constructor(x, y, r = 0, options) { // 調(diào)用父類的構(gòu)造函數(shù) 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) } // 移動元素到新位置,接收兩個參數(shù),新的元素位置 moveTo(x, y) { this.x = x this.y = y } // 判斷點是否在元素中,接收兩個參數(shù),點的坐標(biāo) 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 } } // 繪制元素實現(xiàn),接收一個ctx對象,將當(dāng)前元素繪制到指定畫布上 draw(ctx) { // 取到繪制所需屬性 let { fillStyle, strokeStyle, lineWidth } = this.options // 開始繪制beginPath() 方法開始一條路徑,或重置當(dāng)前的路徑 ctx.beginPath() // 設(shè)置屬性 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() // 繪制完成 } // 驗證函數(shù),判斷當(dāng)前元素是否滿足指定條件,此處用來檢驗是否將元素添加到場景中。 validate() { return this.r >= 3 } }
arc() 方法創(chuàng)建弧/曲線(用于創(chuàng)建圓或部分圓)
x 圓的中心的 x 坐標(biāo)。
y 圓的中心的 y 坐標(biāo)。
r 圓的半徑。
sAngle 起始角,以弧度計。(弧的圓形的三點鐘位置是 0 度)。
eAngle 結(jié)束角,以弧度計。
counterclockwise 可選。規(guī)定應(yīng)該逆時針還是順時針繪圖。False = 順時針,true = 逆時針。
注意事項:
構(gòu)造函數(shù)的形參只有兩個是必須的,就是定位點的坐標(biāo)。
其它的形參都必須有默認(rèn)值。
所有方法的調(diào)用時機
我們在畫布上繪制元素的時候回調(diào)用resize方法。
移動元素的時候調(diào)用moveTo方法。
choose會在鼠標(biāo)按下時調(diào)用,判斷當(dāng)前元素是否被選中。
getOffset選中元素時調(diào)用,判斷選中位置。
draw繪制函數(shù),繪制元素到場景上時調(diào)用。
scene場景的實現(xiàn)屬性介紹
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") // 畫布狀態(tài) this.stateList = { drawing: "drawing", moving: "moving" } this.state = this.stateList.drawing // 鼠標(biāo)狀態(tài) this.mouseState = { // 記錄鼠標(biāo)按下時的偏移量 offsetX: 0, offsetY: 0, down: false, //記錄鼠標(biāo)當(dāng)前狀態(tài)是否按下 target: null //當(dāng)前操作的目標(biāo)元素 } // 當(dāng)前選中的精靈構(gòu)造器 this.currentSpriteConstructor = null // 存儲精靈 let sprites = [] this.sprites = sprites /* .... */ } }
事件邏輯
class Sence { constructor(id, options = { width: 600, height: 400 }) { /* ... */ // 監(jiān)聽事件 this.canvas.addEventListener("contextmenu", (e) => { console.log(e) }) // 鼠標(biāo)按下時的處理邏輯 this.canvas.addEventListener("mousedown", (e) => { // 只有左鍵按下時才會處理鼠標(biāo)事件 if (e.button === 0) { // 鼠標(biāo)的位置 let x = e.offsetX let y = e.offsetY // 記錄鼠標(biāo)是否按下 this.mouseState.down = true // 創(chuàng)建一個臨時target // 記錄目標(biāo)元素 let target = null if (this.state === this.stateList.drawing) { // 判斷當(dāng)前有沒有精靈構(gòu)造器,有的話就構(gòu)造一個對應(yīng)的精靈元素 if (this.currentSpriteConstructor) { target = new this.currentSpriteConstructor(x, y) } } else if (this.state === this.stateList.moving) { let sprites = this.sprites // 遍歷所有的精靈,調(diào)用他們的choose方法,判斷有沒有被選中 for (let i = sprites.length - 1; i >= 0; i--) { if (sprites[i].choose(x, y)) { target = sprites[i] break; } } // 如果選中的話就調(diào)用target的getOffset方法,獲取偏移量 if (target) { let offset = target.getOffset(x, y) this.mouseState.offsetX = offset.x this.mouseState.offsetY = offset.y } } // 存儲當(dāng)前目標(biāo)元素 this.mouseState.target = target // 在離屏canvas保存除目標(biāo)元素外的所有元素 let ctx = this.outCtx // 清空離屏canvas ctx.clearRect(0, 0, this.width, this.height) // 將目標(biāo)元素外的所有的元素繪制到離屏canvas中 this.sprites.forEach(item => { if (item !== target) { item.draw(ctx) } }) if(target){ // 開始動畫 this.anmite() } } }) this.canvas.addEventListener("mousemove", (e) => { // 如果鼠標(biāo)按下且有目標(biāo)元素,才執(zhí)行下面的代碼 if (this.mouseState.down && this.mouseState.target) { let x = e.offsetX let y = e.offsetY if (this.state === this.stateList.drawing) { // 調(diào)用當(dāng)前target的resize方法,改變大小 this.mouseState.target.resize(x, y) } else if (this.state === this.stateList.moving) { // 取到存儲的偏移量 let { offsetX, offsetY } = this.mouseState // 調(diào)用moveTo方法將target移動到新的位置 this.mouseState.target.moveTo(x - offsetX, y - offsetY) } } }) document.body.addEventListener("mouseup", (e) => { if (this.mouseState.down) { // 將鼠標(biāo)按下狀態(tài)記錄為false this.mouseState.down = false if (this.state === this.stateList.drawing) { // 調(diào)用target的validate方法。判斷他要不要被加到場景去呢 if (this.mouseState.target.validate()) { this.sprites.push(this.mouseState.target) } } else if (this.state === this.stateList.moving) { // 什么都不做 } } }) } }
方法介紹
class Sence { // 動畫 anmite() { requestAnimationFrame(() => { // 清除畫布 this.clear() // 將離屏canvas繪制到當(dāng)前canvas上 this.paint(this.outCanvas) // 繪制target this.mouseState.target.draw(this.ctx) // 鼠標(biāo)是按下狀態(tài)就繼續(xù)執(zhí)行下一幀動畫 if (this.mouseState.down) { this.anmite() } }) } // 可以將手動的創(chuàng)建的精靈添加到畫布中 append(sprite) { this.sprites.push(sprite) sprite.draw(this.ctx) } // 根據(jù)ID值,從場景中刪除對應(yīng)元素 remove(id) { this.sprites.splice(id, 1) } // clearRect清除指定區(qū)域的畫布內(nèi)容 clear() { this.ctx.clearRect(0, 0, this.width, this.height) } // 重繪整個畫布的內(nèi)容 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) } // 設(shè)置當(dāng)前選中的精靈構(gòu)造器 setCurrentSprite(Element) { this.currentSpriteConstructor = Element } }
Demo演示地址
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/96460.html
摘要:主要實現(xiàn)功能在畫布上跟隨鼠標(biāo)的按鍵移動畫出拖拉范圍內(nèi)的矩形彈出選擇項,選對勾則將這部分矩形填上背景色,選叉號則取消本次拖拉的矩形。附業(yè)務(wù)目的視頻遮罩是一種將視頻某部分區(qū)域遮蓋的效果,可用于遮蓋電視臺圖標(biāo),廣告,鏡頭內(nèi)敏感部分等。 作者:云荒杯傾 序 本意是用這個做視頻遮罩效果,但是還是從更通用的角度來解釋事情本身吧。少摻雜一點業(yè)務(wù)目的。 主要實現(xiàn)功能 在canvas畫布上跟隨鼠標(biāo)的按鍵...
摘要:小程序和的頁面展示特殊字體有一個網(wǎng)站,叫有字庫。這就是直接再頁面上顯示文字的辦法這個在和小程序上面都可以使用的,非常方便。接下來就是畫圖了。引入就是用小程序的引入字體方法啦。 請看清楚我虛線下面所有的話。橫線上的廢話隨便你看不看。說實話這個字體已經(jīng)把我折騰的死去活來了一段時間,而且我們項目還經(jīng)常要畫分享圖去刷朋友圈,默認(rèn)字體沒辦法達到設(shè)計的那種效果,查了不少資料,也自己摸索了半天,最后...
摘要:小程序和的頁面展示特殊字體有一個網(wǎng)站,叫有字庫。這就是直接再頁面上顯示文字的辦法這個在和小程序上面都可以使用的,非常方便。接下來就是畫圖了。引入就是用小程序的引入字體方法啦。 請看清楚我虛線下面所有的話。橫線上的廢話隨便你看不看。說實話這個字體已經(jīng)把我折騰的死去活來了一段時間,而且我們項目還經(jīng)常要畫分享圖去刷朋友圈,默認(rèn)字體沒辦法達到設(shè)計的那種效果,查了不少資料,也自己摸索了半天,最后...
閱讀 1226·2023-04-26 02:20
閱讀 3349·2021-11-22 14:45
閱讀 4166·2021-11-17 09:33
閱讀 1020·2021-09-06 15:00
閱讀 1492·2021-09-03 10:30
閱讀 3900·2021-07-26 22:01
閱讀 1004·2019-08-30 15:54
閱讀 544·2019-08-30 15:43