Skip to content

Commit

Permalink
feat(painter): eraser
Browse files Browse the repository at this point in the history
  • Loading branch information
surunzi committed Feb 7, 2024
1 parent e91bda4 commit 51e5da6
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 25 deletions.
53 changes: 45 additions & 8 deletions src/painter/tools/Brush.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import Painter, { Layer } from '../'
import defaults from 'licia/defaults'
import Tool from './Tool'
import nextTick from 'licia/nextTick'

export default class Brush extends Tool {
private drawCtx: CanvasRenderingContext2D
private drawCanvas: HTMLCanvasElement
private brushCavnas: HTMLCanvasElement
private brushCtx: CanvasRenderingContext2D
private isDrawing = false
private drawOptions: Required<IDrawOptions> = {
color: 'rgb(0,0,0)',
size: 4,
opacity: 100,
hardness: 100,
}
constructor(painter: Painter) {
super(painter)

Expand All @@ -21,20 +29,33 @@ export default class Brush extends Tool {

this.brushCavnas = document.createElement('canvas')
this.brushCtx = this.brushCavnas.getContext('2d')!
this.generateBrush()
}
onDragStart(e: any) {
onDragStart(e: any, drawOptions: IDrawOptions = {}) {
super.onDragStart(e)

const { canvas, drawCanvas, drawCtx } = this
const { canvas, drawCanvas, drawCtx, options } = this
drawCanvas.width = canvas.width
drawCanvas.height = canvas.height
drawCtx.clearRect(0, 0, canvas.width, canvas.height)

this.isDrawing = true
this.draw(this.x, this.y)
nextTick(() => {
this.isDrawing = true
defaults(drawOptions, {
color: 'rgb(0,0,0)',
size: options.size,
opacity: options.opacity,
hardness: options.hardness,
})
this.drawOptions = drawOptions as Required<IDrawOptions>
this.generateBrush()
this.draw(this.x, this.y)
})
}
onDragMove(e: any) {
if (!this.isDrawing) {
return
}

super.onDragMove(e)
const { x, y, lastX, lastY } = this

Expand All @@ -57,6 +78,10 @@ export default class Brush extends Tool {
this.draw(x, y)
}
onDragEnd(e: any) {
if (!this.isDrawing) {
return
}

super.onDragEnd(e)

const { painter } = this
Expand Down Expand Up @@ -109,18 +134,23 @@ export default class Brush extends Tool {
}
private commitDraw(ctx: CanvasRenderingContext2D) {
const { drawCanvas } = this
ctx.globalAlpha = this.options.opacity / 100
const { color, opacity } = this.drawOptions
ctx.globalAlpha = opacity / 100
if (color === 'transparent') {
ctx.globalCompositeOperation = 'destination-out'
}
ctx.drawImage(drawCanvas, 0, 0)
ctx.globalCompositeOperation = 'source-over'
ctx.globalAlpha = 1
}
private generateBrush() {
const { brushCavnas, brushCtx } = this
const { size, hardness } = this.options
const { size, hardness, color } = this.drawOptions

brushCavnas.width = size
brushCavnas.height = size
brushCtx.clearRect(0, 0, size, size)
brushCtx.fillStyle = 'rgb(0,0,0)'
brushCtx.fillStyle = color === 'transparent' ? 'black' : color

const center = size / 2
let radius = size / 2
Expand All @@ -137,3 +167,10 @@ export default class Brush extends Tool {
brushCtx.globalAlpha = 1
}
}

interface IDrawOptions {
color?: string
size?: number
hardness?: number
opacity?: number
}
70 changes: 69 additions & 1 deletion src/painter/tools/Eraser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,71 @@
import Tool from './Tool'
import Brush from './Brush'
import Pencil from './Pencil'
import Painter, { Layer } from '../'

export default class Eraser extends Tool {}
export default class Eraser extends Tool {
constructor(painter: Painter) {
super(painter)

this.options = {
mode: 'brush',
size: 4,
opacity: 100,
hardness: 100,
}
}
onDragStart(e: any) {
this.getTool().onDragStart(e, this.getOptions())
}
onDragMove(e: any) {
this.getTool().onDragMove(e)
}
onDragEnd(e: any) {
this.getTool().onDragEnd(e)
}
onAfterRenderLayer(layer: Layer) {
this.getTool().onAfterRenderLayer(layer)
}
private getTool(): Brush | Pencil {
return this.painter.getTool(this.options.mode) as any
}
private getOptions() {
const { options } = this

return {
color: 'transparent',
size: options.size,
opacity: options.opacity,
hardness: options.hardness,
}
}
protected renderToolbar() {
super.renderToolbar()

const { toolbar, options } = this

toolbar.appendText('Mode:')
toolbar.appendSelect('mode', options.mode, {
Brush: 'brush',
Pencil: 'pencil',
})
toolbar.appendText('Size:')
toolbar.appendNumber('size', options.size, {
min: 1,
max: 1000,
step: 1,
})
toolbar.appendText('Hardness:')
toolbar.appendNumber('hardness', options.hardness, {
min: 1,
max: 100,
step: 1,
})
toolbar.appendText('Opacity:')
toolbar.appendNumber('opacity', options.opacity, {
min: 1,
max: 100,
step: 1,
})
}
}
49 changes: 41 additions & 8 deletions src/painter/tools/Pencil.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import Painter, { Layer } from '../'
import Tool from './Tool'
import defaults from 'licia/defaults'
import nextTick from 'licia/nextTick'

export default class Pencil extends Tool {
private drawCtx: CanvasRenderingContext2D
private drawCanvas: HTMLCanvasElement
private isDrawing = false
private drawOptions: Required<IDrawOptions> = {
color: 'rgb(0,0,0)',
size: 1,
opacity: 100,
}
constructor(painter: Painter) {
super(painter)

Expand All @@ -16,18 +23,30 @@ export default class Pencil extends Tool {
this.drawCanvas = document.createElement('canvas')
this.drawCtx = this.drawCanvas.getContext('2d')!
}
onDragStart(e: any) {
onDragStart(e: any, drawOptions: IDrawOptions = {}) {
super.onDragStart(e)

const { canvas, drawCanvas, drawCtx } = this
const { canvas, drawCanvas, drawCtx, options } = this
drawCanvas.width = canvas.width
drawCanvas.height = canvas.height
drawCtx.clearRect(0, 0, canvas.width, canvas.height)

this.isDrawing = true
this.draw(this.x, this.y)
nextTick(() => {
this.isDrawing = true
defaults(drawOptions, {
color: 'rgb(0,0,0)',
size: options.size,
opacity: options.opacity,
})
this.drawOptions = drawOptions as Required<IDrawOptions>
this.draw(this.x, this.y)
})
}
onDragMove(e: any) {
if (!this.isDrawing) {
return
}

super.onDragMove(e)
const { x, y, lastX, lastY } = this

Expand All @@ -49,6 +68,10 @@ export default class Pencil extends Tool {
this.draw(x, y)
}
onDragEnd(e: any) {
if (!this.isDrawing) {
return
}

super.onDragEnd(e)
this.isDrawing = false

Expand Down Expand Up @@ -86,18 +109,28 @@ export default class Pencil extends Tool {
return
}

const { size } = this.options
const color = 'rgb(0,0,0)'
drawCtx.fillStyle = color
const { size, color } = this.drawOptions
drawCtx.fillStyle = color === 'transparent' ? 'black' : color
const centerX = size > 1 ? x - Math.floor((size - 1) / 2) : x
const centerY = size > 1 ? y - Math.floor((size - 1) / 2) : y
drawCtx.fillRect(centerX, centerY, size, size)
this.painter.renderCanvas()
}
private commitDraw(ctx: CanvasRenderingContext2D) {
const { drawCanvas } = this
ctx.globalAlpha = this.options.opacity / 100
const { color, opacity } = this.drawOptions
ctx.globalAlpha = opacity / 100
if (color === 'transparent') {
ctx.globalCompositeOperation = 'destination-out'
}
ctx.drawImage(drawCanvas, 0, 0)
ctx.globalCompositeOperation = 'source-over'
ctx.globalAlpha = 1
}
}

interface IDrawOptions {
color?: string
size?: number
opacity?: number
}
16 changes: 8 additions & 8 deletions src/painter/tools/Zoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default class Zoom extends Tool {
super(painter)

this.options = {
zoomIn: true,
mode: 'in',
}

this.bindEvent()
Expand All @@ -35,7 +35,7 @@ export default class Zoom extends Tool {
onClick(e: any) {
const offset = this.$viewport.offset()

let ratio = this.options.zoomIn ? 0.3 : -0.3
let ratio = this.options.mode === 'in' ? 0.3 : -0.3
if (e.altKey) {
ratio = -ratio
}
Expand Down Expand Up @@ -129,20 +129,20 @@ export default class Zoom extends Tool {
toolbar.appendButton(
painter.c('<span class="icon icon-zoom-in"></span>'),
() => {
if (!options.zoomIn) {
this.setOption('zoomIn', true)
if (options.mode !== 'in') {
this.setOption('mode', 'in')
}
},
options.zoomIn ? 'active' : ''
options.mode === 'in' ? 'active' : ''
)
toolbar.appendButton(
painter.c('<span class="icon icon-zoom-out"></span>'),
() => {
if (options.zoomIn) {
this.setOption('zoomIn', false)
if (options.mode !== 'out') {
this.setOption('mode', 'out')
}
},
options.zoomIn ? '' : 'active'
options.mode === 'out' ? 'active' : ''
)
toolbar.appendSeparator()
toolbar.appendButton(
Expand Down

0 comments on commit 51e5da6

Please sign in to comment.