效果展示
Canvas API簡介
調用方法
-
傳回getImageData()
對象,該對象為畫布上指定的矩形複制像素資料ImageData
-
把圖像資料(從指定的putImageData()
對象)放回畫布上ImageData
-
在給定的矩形内清除指定的像素clearRect()
-
傳回canvas圖像的URLtoDataURL()
-
添加一個新點,建立從該點到最後指定點的線條lineTo()
-
繪制已定義的路徑stroke()
-
起始一條路徑,或重置目前路徑beginPath()
-
把路徑移動到畫布中的指定點,不建立線條moveTo()
調用屬性
-
設定或傳回用于筆觸的顔色、漸變或模式strokeStyle
-
設定或傳回用于陰影的模糊級别shadowBlur
-
設定或傳回用于陰影的顔色shadowColor
-
設定或傳回目前的線條寬度lineWidth
更多API請參考 canvas基本使用
功能需求說明
- 基礎線條繪制功能
- 筆觸顔色修改
- 筆刷粗細調整
- 撤回、前進、情況功能
- 生成圖檔
初始化資料
-
: 筆觸顔色清單colors
-
: 筆刷對應的粗細brushs
-
: canvas contextcontext
-
: 用于存放儲存圖檔的位址imgUrl
-
: 是否允許執行move時候繪制線條canvasMoveUse
-
: 存儲目前表面狀态數組-上一步preDrawAry
-
: 存儲目前表面狀态數組-下一步nextDrawAry
-
: 中間數組middleAry
-
: 線條寬度lineWidth
-
: 線條顔色lineColor
-
: 陰影shadowBlur
data() {
return {
colors: ['#fef4ac','#0018ba','#ffc200','#f32f15','#cccccc','#5ab639'],
brushs: [{
className: 'small fa fa-paint-brush',
lineWidth: 3
},{
className: 'middle fa fa-paint-brush',
lineWidth: 6
},{
className: 'big fa fa-paint-brush',
lineWidth: 12
}],
context: {},
imgUrl: [],
canvasMoveUse: true,
preDrawAry: [],
nextDrawAry: [],
middleAry: [],
config: {
lineWidth: 1,
lineColor: "#f2849e",
shadowBlur: 2
}
}
}
設定繪畫配置
setCanvasStyle() {
this.context.lineWidth = this.config.lineWidth
this.context.shadowBlur = this.config.shadowBlur
this.context.shadowColor = this.config.lineColor
this.context.strokeStyle = this.config.lineColor
}
筆觸顔色及粗細相關設定(點選修改config資料):
<!-- 畫筆顔色 -->
<li
v-for="item in colors"
:class="{'active':config.lineColor === item}"
:style="{ background: item }"
@click="setColor(item)"
></li>
<!-- 畫筆粗細 -->
<span
v-for="pen in brushs"
:class="[pen.className,{'active': config.lineWidth === pen.lineWidth}]"
@click="setBrush(pen.lineWidth)"
></span>
畫筆的移動操作
// 當在螢幕中移動時即開始繪制準備
beginPath(e){
const canvas = document.querySelector('#canvas')
if (e.target !== canvas) {
this.context.beginPath()
}
}
// 在canvas中滑鼠按下
canvasDown(e) {
// 讓move方法可用
this.canvasMoveUse = true
// client是基于整個頁面的坐标
// offset是cavas距離頂部以及左邊的距離
const canvasX = e.clientX - e.target.parentNode.offsetLeft
const canvasY = e.clientY - e.target.parentNode.offsetTop
// 設定canvas的配置
this.setCanvasStyle()
//清除子路徑
this.context.beginPath()
// 移動的起點
this.context.moveTo(canvasX, canvasY)
//目前繪圖表面狀态
const preData = this.context.getImageData(0, 0, 600, 400)
//目前繪圖表面進棧
// 按下相當于新的操作的開始,是以把目前記錄資料放到prev中
this.preDrawAry.push(preData)
},
// canvas中滑鼠移動
canvasMove(e) {
if(this.canvasMoveUse) {
// 隻有允許移動時調用
const t = e.target
let canvasX
let canvasY
// 由于手機端和pc端擷取頁面坐标方式不同,是以需要做出判斷
if(this.isPc()){
canvasX = e.clientX - t.parentNode.offsetLeft
canvasY = e.clientY - t.parentNode.offsetTop
}else {
canvasX = e.changedTouches[0].clientX - t.parentNode.offsetLeft
canvasY = e.changedTouches[0].clientY - t.parentNode.offsetTop
}
// 連接配接到移動的位置并上色
this.context.lineTo(canvasX, canvasY)
this.context.stroke()
}
},
// canvas中滑鼠放開
canvasUp(e){
const preData = this.context.getImageData(0, 0, 600, 400)
if (!this.nextDrawAry.length) {
// 在沒有撤銷過的情況下,将目前資料放入prev
//目前繪圖表面進棧
this.middleAry.push(preData)
} else {
// 在撤銷的情況下,将在後面步驟的資料情況記錄
this.middleAry = []
this.middleAry = this.middleAry.concat(this.preDrawAry)
this.middleAry.push(preData)
this.nextDrawAry = []
}
// 設定move時不可繪制
this.canvasMoveUse = false
}
為了保證移動端的可用性,加入touchstart等。
<canvas
id="canvas"
class="fl"
width="600"
height="400"
@mousedown="canvasDown($event)"
@mouseup="canvasUp($event)"
@mousemove="canvasMove($event)"
@touchstart="canvasDown($event)"
@touchend="canvasUp($event)"
@touchmove="canvasMove($event)"
>
撤銷清空等操作
// 撤銷
if (this.preDrawAry.length) {
const popData = this.preDrawAry.pop()
const midData = this.middleAry[this.preDrawAry.length + 1]
this.nextDrawAry.push(midData)
this.context.putImageData(popData, 0, 0)
}
// 前進
if (this.nextDrawAry.length) {
const popData = this.nextDrawAry.pop()
const midData = this.middleAry[this.middleAry.length - this.nextDrawAry.length - 2]
this.preDrawAry.push(midData)
this.context.putImageData(popData, 0, 0)
}
// 清空
this.context.clearRect(0, 0, this.context.canvas.width, this.context.canvas.height)
// 清空前後資料
this.preDrawAry = []
this.nextDrawAry = []
// middleAry恢複到預設資料
this.middleAry = [this.middleAry[0]]
demo位址
檢視代碼