Skip to content

Commit

Permalink
feat(painter): custom cursor
Browse files Browse the repository at this point in the history
  • Loading branch information
surunzi committed Feb 15, 2024
1 parent 2ec2950 commit de5fe6f
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 15 deletions.
36 changes: 28 additions & 8 deletions src/painter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import stripIndent from 'licia/stripIndent'
import $ from 'licia/$'
import each from 'licia/each'
import ResizeSensor from 'licia/ResizeSensor'
import { exportCjs, drag } from '../share/util'
import { exportCjs, drag, measuredScrollbarWidth } from '../share/util'
import { Brush, Pencil, Hand, Zoom, PaintBucket, Eraser, Tool } from './tools'

const $document = $(document as any)
Expand Down Expand Up @@ -61,6 +61,10 @@ export default class Painter extends Component<IOptions> {
this.$canvas = this.find('.main-canvas')
this.$viewport = this.find('.viewport')
this.viewport = this.$viewport.get(0) as HTMLDivElement
this.find('.viewport-overlay').css({
right: measuredScrollbarWidth(),
bottom: measuredScrollbarWidth(),
})
this.$body = this.find('.body')
this.$foregroundColor = this.find('.palette-foreground').find('input')
this.$backgroundColor = this.find('.palette-background').find('input')
Expand Down Expand Up @@ -193,23 +197,30 @@ export default class Painter extends Component<IOptions> {
</div>
</div>
</div>
<div class="viewport">
<div class="body">
<div class="canvas-wrapper">
<div class="canvas-container">
<canvas class="main-canvas" width="${width}" height="${height}"></canvas>
<div class="viewport-wrapper">
<div class="viewport">
<div class="body">
<div class="canvas-wrapper">
<div class="canvas-container">
<canvas class="main-canvas" width="${width}" height="${height}"></canvas>
</div>
</div>
</div>
</div>
<div class="viewport-overlay"></div>
</div>
`)
)
}
private bindEvent() {
const { $viewport, $tools, $foregroundColor, $backgroundColor, c } = this

$viewport.on(drag('start'), this.onViewportDragStart)
$viewport.on('click', this.onViewportClick)
$viewport
.on(drag('start'), this.onViewportDragStart)
.on('click', this.onViewportClick)
.on('mouseenter', this.onViewportMouseEnter)
.on('mousemove', this.onViewportMouseMove)
.on('mouseleave', this.onViewportMouseLeave)

const self = this
$tools
Expand All @@ -230,6 +241,15 @@ export default class Painter extends Component<IOptions> {
this.resizeSensor.addListener(this.onResize)
this.canvasResizeSenor.addListener(this.resetViewport)
}
private onViewportMouseEnter = (e: any) => {
this.currentTool.onMouseEnter(e.origEvent)
}
private onViewportMouseMove = (e: any) => {
this.currentTool.onMouseMove(e.origEvent)
}
private onViewportMouseLeave = (e: any) => {
this.currentTool.onMouseLeave(e.origEvent)
}
private onViewportDragStart = (e: any) => {
this.currentTool.onDragStart(e.origEvent)
$document.on(drag('move'), this.onViewportDragMove)
Expand Down
22 changes: 22 additions & 0 deletions src/painter/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,34 @@
top: 10px;
}

.viewport-wrapper {
width: 100%;
height: 100%;
position: relative;
cursor: none;
}

.viewport {
width: 100%;
height: 100%;
overflow: auto;
}

.viewport-overlay {
position: absolute;
left: 0;
top: 0;
pointer-events: none;
overflow: hidden;
}

.cursor {
position: absolute;
opacity: 0;
left: 0;
top: 0;
}

.body {
display: flex;
width: 100%;
Expand Down
5 changes: 5 additions & 0 deletions src/painter/tools/Hand.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import Tool from './Tool'
import Zoom from './Zoom'
import Painter from '../'
import { eventClient } from '../../share/util'

export default class Hand extends Tool {
private startX = 0
private startY = 0
private startScrollLeft = 0
private startScrollTop = 0
constructor(painter: Painter) {
super(painter)
this.$cursor.html(painter.c(`<span class="icon icon-hand"></span>`))
}
onDragStart(e: any) {
const { viewport } = this

Expand Down
19 changes: 15 additions & 4 deletions src/painter/tools/PaintBucket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,32 @@ export default class PaintBucket extends Tool {
this.options = {
tolerance: 32,
}

this.$cursor.html(painter.c(`<span class="icon icon-paint-bucket"></span>`))
this.$cursor.find(painter.c('.icon-paint-bucket')).css({
position: 'relative',
left: 0,
top: 0,
})
}
onClick(e: any) {
super.onClick(e)
const { x, y, painter } = this
const point = { x, y }

const layer = painter.getActiveLayer()
const canvas = layer.getCanvas()
const ctx = layer.getContext()
const w = canvas.width
const h = canvas.height
const point = { x, y }

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

const foregroundColor = new Color(this.painter.getForegroundColor())
const fillColor = Color.parse(foregroundColor.toRgb()).val
fillColor[3] = 255

const w = canvas.width
const h = canvas.height
const imageData = ctx.getImageData(0, 0, w, h)
const { tolerance } = this.options

Expand Down
47 changes: 44 additions & 3 deletions src/painter/tools/Tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ export default class Tool {
protected lastY = -1
protected $viewport: $.$
protected viewport: HTMLDivElement
protected $viewportOverlay: $.$
protected $canvas: $.$
protected ctx: CanvasRenderingContext2D
protected canvas: HTMLCanvasElement
protected $toolbar: $.$
protected $cursor: $.$
protected cursor: HTMLDivElement
protected toolbar: LunaToolbar
protected options: types.PlainObj<any> = {}
constructor(painter: Painter) {
Expand All @@ -27,25 +30,30 @@ export default class Tool {
.get(0) as HTMLDivElement
this.$viewport = $(this.viewport)

this.$viewportOverlay = painter.$container.find(
painter.c('.viewport-overlay')
)

this.canvas = painter.getCanvas()
this.ctx = this.canvas.getContext('2d')!
this.$canvas = $(this.canvas)

this.$toolbar = painter.$container.find(painter.c('.toolbar'))

const toolbar = new LunaToolbar(h('div'))
this.toolbar = toolbar
toolbar.on('change', (key, val) => {
this.options[key] = val
})
painter.addSubComponent(toolbar)

this.cursor = h(`div.${painter.c('cursor')}`) as HTMLDivElement
this.$cursor = $(this.cursor)
}
setOption(name: string, val: any) {
this.options[name] = val

this.renderToolbar()
}
/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars */
onDragStart(e: any) {
this.getXY(e)
}
Expand All @@ -59,14 +67,47 @@ export default class Tool {
this.renderToolbar()

this.$toolbar.append(this.toolbar.container)
this.$viewportOverlay.append(this.cursor)
}
onUnuse() {
this.toolbar.$container.remove()
this.$cursor.remove()
}
onAfterRenderLayer(layer: Layer) {}
onClick(e: any) {
this.getXY(e)
}
onMouseMove(e: any) {
const { $cursor, $viewportOverlay } = this
const overlayOffset = $viewportOverlay.offset()
const offset = $cursor.offset()

const x = eventPage('x', e) - overlayOffset.left
const y = eventPage('y', e) - overlayOffset.top

if (x >= overlayOffset.width || y >= overlayOffset.height) {
$cursor.css({
opacity: 0,
})
} else {
$cursor.css({
left: x - offset.width / 2,
top: y - offset.height / 2,
opacity: 1,
})
}
}
/* eslint-disable @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars */
onMouseEnter(e: any) {
this.$cursor.css({
opacity: 1,
})
}
onMouseLeave(e: any) {
this.$cursor.css({
opacity: 0,
})
}
onAfterRenderLayer(layer: Layer) {}
protected renderToolbar() {
this.toolbar.clear()
}
Expand Down

0 comments on commit de5fe6f

Please sign in to comment.