Skip to content

Commit

Permalink
feat(painter): brush
Browse files Browse the repository at this point in the history
  • Loading branch information
surunzi committed Feb 5, 2024
1 parent 6de14e4 commit d41e8e8
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 31 deletions.
9 changes: 3 additions & 6 deletions src/painter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import each from 'licia/each'
import ResizeSensor from 'licia/ResizeSensor'
import { exportCjs, drag } from '../share/util'
import { Brush, Pencil, Hand, Zoom, Tool } from './tools'
import LunaToolbar from 'luna-toolbar'

const $document = $(document as any)

Expand Down Expand Up @@ -127,17 +126,15 @@ export default class Painter extends Component<IOptions> {
getCanvas() {
return this.canvas
}
updateCanvas() {
renderCanvas() {
const { ctx, canvas, layers } = this

ctx.clearRect(0, 0, canvas.width, canvas.height)
each(layers, (layer) => {
ctx.drawImage(layer.getCanvas(), 0, 0)
this.currentTool.onAfterRenderLayer(layer)
})
}
addToolbar(toolbar: LunaToolbar) {
this.addSubComponent(toolbar)
}
private initTpl() {
const { width, height } = this.options

Expand Down Expand Up @@ -227,7 +224,7 @@ export default class Painter extends Component<IOptions> {
}
}

class Layer {
export class Layer {
private canvas: HTMLCanvasElement
private ctx: CanvasRenderingContext2D
constructor(width: number, height: number) {
Expand Down
63 changes: 52 additions & 11 deletions src/painter/tools/Brush.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Painter from '../index'
import Painter, { Layer } from '../'
import Tool from './Tool'
import types from 'licia/types'
import h from 'licia/h'
Expand All @@ -7,8 +7,11 @@ import LunaToolbar from 'luna-toolbar'
export default class Pencil extends Tool {
private drawCtx: CanvasRenderingContext2D
private drawCanvas: HTMLCanvasElement
private brushCavnas: HTMLCanvasElement
private brushCtx: CanvasRenderingContext2D
private isDrawing = false
private options: types.PlainObj<any> = {
size: 1,
size: 4,
opacity: 100,
hardness: 100,
}
Expand All @@ -22,12 +25,17 @@ export default class Pencil extends Tool {

this.toolbar.on('change', (key, val) => {
this.options[key] = val
this.generateBrush()
})

painter.addToolbar(toolbar)
painter.addSubComponent(toolbar)

this.drawCanvas = document.createElement('canvas')
this.drawCtx = this.drawCanvas.getContext('2d')!

this.brushCavnas = document.createElement('canvas')
this.brushCtx = this.brushCavnas.getContext('2d')!
this.generateBrush()
}
onUse() {
this.$toolbar.append(this.toolbar.container)
Expand All @@ -37,6 +45,7 @@ export default class Pencil extends Tool {
}
setOption(name: string, val: any) {
this.options[name] = val
this.generateBrush()

this.renderToolbar()
}
Expand All @@ -47,6 +56,9 @@ export default class Pencil extends Tool {
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)
}
onDragMove(e: any) {
super.onDragMove(e)
Expand All @@ -56,6 +68,7 @@ export default class Pencil extends Tool {
x: x - lastX,
y: y - lastY,
}

if (Math.abs(delta.x) > 1 || Math.abs(delta.y) > 1) {
const steps = Math.max(Math.abs(delta.x), Math.abs(delta.y))
delta.x /= steps
Expand All @@ -67,29 +80,34 @@ export default class Pencil extends Tool {
}
}

this.draw(this.x, this.y)
this.draw(x, y)
}
onDragEnd(e: any) {
super.onDragEnd(e)

const { painter } = this

this.isDrawing = false
this.commitDraw(painter.getActiveLayer().getContext())
painter.updateCanvas()
painter.renderCanvas()
}
draw(x: number, y: number) {
const { canvas, drawCtx } = this
const { size } = this.options

if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
return
}

const { size } = this.options
const color = 'rgb(0,0,0)'
drawCtx.fillStyle = color
drawCtx.fillRect(x, y, size, size)
this.painter.updateCanvas()
this.commitDraw(this.ctx)
const centerX = size > 1 ? x - Math.floor((size - 1) / 2) : x
const centerY = size > 1 ? y - Math.floor((size - 1) / 2) : y
drawCtx.drawImage(this.brushCavnas, centerX, centerY)
this.painter.renderCanvas()
}
onAfterRenderLayer(layer: Layer) {
if (layer === this.painter.getActiveLayer() && this.isDrawing) {
this.commitDraw(this.ctx)
}
}
private commitDraw(ctx: CanvasRenderingContext2D) {
const { drawCanvas } = this
Expand Down Expand Up @@ -120,4 +138,27 @@ export default class Pencil extends Tool {
step: 1,
})
}
private generateBrush() {
const { brushCavnas, brushCtx } = this
const { size, hardness } = this.options

brushCavnas.width = size
brushCavnas.height = size
brushCtx.clearRect(0, 0, size, size)
brushCtx.fillStyle = 'rgb(0,0,0)'

const center = size / 2
let radius = size / 2
const opacityStep = 1 / radius / ((105 - hardness) / 25)
let opacity = opacityStep
for (; radius > 0; radius--) {
brushCtx.globalAlpha = Math.min(opacity, 1)
brushCtx.beginPath()
brushCtx.ellipse(center, center, radius, radius, 0, 0, 2 * Math.PI)
brushCtx.fill()
opacity += opacityStep
}

brushCtx.globalAlpha = 1
}
}
25 changes: 18 additions & 7 deletions src/painter/tools/Pencil.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Painter from '../index'
import Painter, { Layer } from '../'
import Tool from './Tool'
import types from 'licia/types'
import h from 'licia/h'
Expand All @@ -7,6 +7,7 @@ import LunaToolbar from 'luna-toolbar'
export default class Pencil extends Tool {
private drawCtx: CanvasRenderingContext2D
private drawCanvas: HTMLCanvasElement
private isDrawing = false
private options: types.PlainObj<any> = {
size: 1,
opacity: 100,
Expand All @@ -23,7 +24,7 @@ export default class Pencil extends Tool {
this.options[key] = val
})

painter.addToolbar(toolbar)
painter.addSubComponent(toolbar)

this.drawCanvas = document.createElement('canvas')
this.drawCtx = this.drawCanvas.getContext('2d')!
Expand All @@ -46,6 +47,9 @@ export default class Pencil extends Tool {
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)
}
onDragMove(e: any) {
super.onDragMove(e)
Expand All @@ -66,15 +70,16 @@ export default class Pencil extends Tool {
}
}

this.draw(this.x, this.y)
this.draw(x, y)
}
onDragEnd(e: any) {
super.onDragEnd(e)
this.isDrawing = false

const { painter } = this

this.commitDraw(painter.getActiveLayer().getContext())
painter.updateCanvas()
painter.renderCanvas()
}
draw(x: number, y: number) {
const { canvas, drawCtx } = this
Expand All @@ -86,9 +91,15 @@ export default class Pencil extends Tool {
const { size } = this.options
const color = 'rgb(0,0,0)'
drawCtx.fillStyle = color
drawCtx.fillRect(x, y, size, size)
this.painter.updateCanvas()
this.commitDraw(this.ctx)
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()
}
onAfterRenderLayer(layer: Layer) {
if (layer === this.painter.getActiveLayer() && this.isDrawing) {
this.commitDraw(this.ctx)
}
}
private commitDraw(ctx: CanvasRenderingContext2D) {
const { drawCanvas } = this
Expand Down
7 changes: 3 additions & 4 deletions src/painter/tools/Tool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Painter from '../index'
import Painter, { Layer } from '../'
import $ from 'licia/$'
import { eventPage } from '../../share/util'

Expand Down Expand Up @@ -28,6 +28,7 @@ export default class Tool {

this.$toolbar = painter.$container.find(painter.c('.toolbar'))
}
/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars */
onDragStart(e: any) {
this.getXY(e)
}
Expand All @@ -37,11 +38,9 @@ export default class Tool {
onDragEnd(e: any) {
this.getXY(e)
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onUse() {}
// eslint-disable-next-line @typescript-eslint/no-empty-function
onUnuse() {}
// eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars
onAfterRenderLayer(layer: Layer) {}
onClick(e: any) {}
private getXY(e: any) {
const { canvas, $canvas } = this
Expand Down
6 changes: 3 additions & 3 deletions src/share/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,14 @@ export default class Component<
getOption(name: string) {
return (this.options as any)[name]
}
protected addSubComponent(component: Component) {
addSubComponent(component: Component) {
component.setOption('theme', this.options.theme)
this.subComponents.push(component)
}
protected removeSubComponent(component: Component) {
removeSubComponent(component: Component) {
remove(this.subComponents, (com) => com === component)
}
protected destroySubComponents() {
destroySubComponents() {
each(this.subComponents, (component) => component.destroy())
this.subComponents = []
}
Expand Down

0 comments on commit d41e8e8

Please sign in to comment.