'))
}
private bindEvent() {
- const { painter } = this
+ const { painter, maskBrush } = this
painter.on(
'canvasRender',
debounce(() => {
+ this.renderMask()
this.emit('change')
}, 20)
)
@@ -79,7 +121,7 @@ export default class MaskEditor extends Component
{
const c = new Color(color)
const rgb = Color.parse(c.toRgb()).val
- const ctx = painter.getActiveLayer().getContext()
+ const ctx = this.drawingLayer.getContext()
const canvas = ctx.canvas
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
const { data } = imageData
@@ -91,6 +133,19 @@ export default class MaskEditor extends Component {
ctx.putImageData(imageData, 0, 0)
painter.renderCanvas()
})
+
+ maskBrush.on('optionChange', (name, val) => {
+ if (name === 'layerOpacity') {
+ this.drawingLayer.opacity = val
+ painter.renderCanvas()
+ }
+ })
+
+ this.on('optionChange', (name) => {
+ if (name === 'image') {
+ this.loadImage()
+ }
+ })
}
}
@@ -145,3 +200,10 @@ class MaskEraser extends LunaPainter.Eraser {
})
}
}
+
+class MaskPaintBucket extends LunaPainter.PaintBucket {
+ constructor(painter: LunaPainter) {
+ super(painter)
+ this.options.tolerance = 180
+ }
+}
diff --git a/src/mask-editor/package.json b/src/mask-editor/package.json
index fcf05c7..ff755cd 100644
--- a/src/mask-editor/package.json
+++ b/src/mask-editor/package.json
@@ -7,6 +7,7 @@
"toolbar",
"painter"
],
- "style": false
+ "style": false,
+ "react": true
}
}
diff --git a/src/mask-editor/react.tsx b/src/mask-editor/react.tsx
new file mode 100644
index 0000000..2692c0f
--- /dev/null
+++ b/src/mask-editor/react.tsx
@@ -0,0 +1,33 @@
+import { CSSProperties, FC, useEffect, useRef } from 'react'
+import MaskEditor, { IOptions } from './index'
+import { useNonInitialEffect } from '../share/hooks'
+
+interface IMaskEditorProps extends IOptions {
+ style?: CSSProperties
+ onCreate?: (maskEditor: MaskEditor) => void
+}
+
+const LunaMaskEditor: FC = (props) => {
+ const maskEditorRef = useRef(null)
+ const maskEditor = useRef()
+
+ useEffect(() => {
+ const { image } = props
+ maskEditor.current = new MaskEditor(maskEditorRef.current!, {
+ image,
+ })
+ props.onCreate && props.onCreate(maskEditor.current)
+
+ return () => maskEditor.current?.destroy()
+ }, [])
+
+ useNonInitialEffect(() => {
+ if (maskEditor.current) {
+ maskEditor.current.setOption('image', props.image)
+ }
+ }, [props.image])
+
+ return
+}
+
+export default LunaMaskEditor
diff --git a/src/mask-editor/story.js b/src/mask-editor/story.js
index bb06bc8..db80b46 100644
--- a/src/mask-editor/story.js
+++ b/src/mask-editor/story.js
@@ -1,22 +1,98 @@
import MaskEditor from 'luna-mask-editor.js'
import story from '../share/story'
import $ from 'licia/$'
+import h from 'licia/h'
+import readme from './README.md'
+import LunaMaskEditor from './react'
+import { text } from '@storybook/addon-knobs'
+import { useRef } from 'react'
-const def = story('mask-editor', (container) => {
- $(container).css({
- width: '100%',
- maxWidth: 1200,
- height: 600,
- margin: '0 auto',
- })
+const def = story(
+ 'mask-editor',
+ (wrapper) => {
+ $(wrapper).html('')
- const maskEditor = new MaskEditor(container, {
- image: 'https://res.liriliri.io/luna/pic1.jpg',
- })
+ const container = h('div')
+
+ $(container).css({
+ width: '100%',
+ maxWidth: 1200,
+ height: 600,
+ margin: '0 auto',
+ })
+ wrapper.appendChild(container)
+
+ const maskContainer = h('div')
+ $(maskContainer).css({
+ border: '1px solid #eee',
+ width: '100%',
+ maxWidth: 1200,
+ margin: '0 auto',
+ fontSize: 0,
+ marginTop: '10px',
+ })
+ wrapper.appendChild(maskContainer)
+
+ const { image } = createKnobs()
+ const maskEditor = new MaskEditor(container, {
+ image,
+ })
+
+ onCreate(maskEditor, maskContainer)
- return maskEditor
-})
+ return maskEditor
+ },
+ {
+ readme,
+ source: __STORY__,
+ ReactComponent({ theme }) {
+ const maskContainer = useRef(null)
+ const { image } = createKnobs()
+
+ return (
+ <>
+ {
+ if (maskContainer.current) {
+ onCreate(maskEditor, maskContainer.current)
+ }
+ }}
+ />
+
+ >
+ )
+ },
+ }
+)
+
+function createKnobs() {
+ const image = text('Image', 'https://res.liriliri.io/luna/pic1.jpg')
+
+ return {
+ image,
+ }
+}
+
+function onCreate(maskEditor, maskContainer) {
+ const canvas = maskEditor.getCanvas()
+ maskContainer.appendChild(canvas)
+ $(canvas).css({
+ maxWidth: '100%',
+ })
+}
export default def
-export const { maskEditor } = def
+export const { maskEditor: html, react } = def
diff --git a/src/painter/index.ts b/src/painter/index.ts
index e748344..216799b 100644
--- a/src/painter/index.ts
+++ b/src/painter/index.ts
@@ -16,6 +16,7 @@ import {
Tool,
} from './tools'
import { duplicateCanvas } from './util'
+import isHidden from 'licia/isHidden'
const $document = $(document as any)
@@ -41,6 +42,9 @@ export interface IOptions extends IComponentOptions {
export default class Painter extends Component {
static Brush = Brush
static Eraser = Eraser
+ static PaintBucket = PaintBucket
+ static Zoom = Zoom
+ static Hand = Hand
private $toolBox: $.$
private $canvas: $.$
private $viewport: $.$
@@ -114,6 +118,7 @@ export default class Painter extends Component {
this.bindEvent()
this.resetViewport()
this.useTool(this.options.tool)
+
zoom.fitScreen()
hand.centerCanvas()
}
@@ -214,6 +219,10 @@ export default class Painter extends Component {
layer.getContext().drawImage(tempCanvas, x, y, oldWidth, oldHeight)
})
+ const zoom = this.getTool('zoom') as Zoom
+ const ratio = zoom.getRatio()
+ zoom.zoomTo(ratio, false)
+
this.renderCanvas()
}
private initTpl() {
@@ -261,9 +270,9 @@ export default class Painter extends Component {
$viewport
.on(drag('start'), this.onViewportDragStart)
+ .on(drag('move'), this.onViewportMouseMove)
.on('click', this.onViewportClick)
.on('mouseenter', this.onViewportMouseEnter)
- .on('mousemove', this.onViewportMouseMove)
.on('mouseleave', this.onViewportMouseLeave)
$toolBox
@@ -329,6 +338,10 @@ export default class Painter extends Component {
this.currentTool.onClick(e.origEvent)
}
private onResize = () => {
+ if (isHidden(this.container)) {
+ return
+ }
+
this.resetViewport()
const { $canvas, viewport } = this
@@ -375,6 +388,8 @@ export class Layer {
}
}
+export * from './tools'
+
if (typeof module !== 'undefined') {
exportCjs(module, Painter)
}
diff --git a/src/painter/react.tsx b/src/painter/react.tsx
index d6462cb..1113a60 100644
--- a/src/painter/react.tsx
+++ b/src/painter/react.tsx
@@ -1,5 +1,6 @@
import { CSSProperties, FC, useEffect, useRef } from 'react'
import Painter, { IOptions } from './index'
+import { useNonInitialEffect } from '../share/hooks'
interface IPainterProps extends IOptions {
style?: CSSProperties
@@ -12,7 +13,6 @@ const LunaPainter: FC = (props) => {
useEffect(() => {
const { width, height, tool } = props
- console.log(width)
painter.current = new Painter(painterRef.current!, {
width,
height,
@@ -23,6 +23,18 @@ const LunaPainter: FC = (props) => {
return () => painter.current?.destroy()
}, [])
+ useNonInitialEffect(() => {
+ if (painter.current) {
+ painter.current.setOption('width', props.width)
+ }
+ }, [props.width])
+
+ useNonInitialEffect(() => {
+ if (painter.current) {
+ painter.current.setOption('height', props.height)
+ }
+ }, [props.height])
+
return
}
diff --git a/src/painter/story.js b/src/painter/story.js
index 116f65d..f6fb6b4 100644
--- a/src/painter/story.js
+++ b/src/painter/story.js
@@ -64,17 +64,17 @@ function onCreate(painter) {
}
function createKnobs() {
- const width = number('Width', 512, {
+ const width = number('Width', 3000, {
range: true,
min: 128,
- max: 2048,
+ max: 4000,
step: 2,
})
- const height = number('Height', 512, {
+ const height = number('Height', 2000, {
range: true,
min: 128,
- max: 2048,
+ max: 4000,
step: 2,
})
diff --git a/src/painter/tools/Brush.ts b/src/painter/tools/Brush.ts
index 9fd7341..88f315b 100644
--- a/src/painter/tools/Brush.ts
+++ b/src/painter/tools/Brush.ts
@@ -65,6 +65,7 @@ export default class Brush extends Tool {
this.drawOptions = drawOptions as Required
this.generateBrush()
this.draw(this.x, this.y)
+ this.painter.renderCanvas()
})
}
onDragMove(e: any) {
@@ -92,6 +93,7 @@ export default class Brush extends Tool {
}
this.draw(x, y)
+ this.painter.renderCanvas()
}
onDragEnd(e: any) {
if (!this.isDrawing) {
@@ -136,7 +138,6 @@ export default class Brush extends Tool {
const startX = size > 1 ? x - Math.floor((size - 1) / 2) : x
const startY = size > 1 ? y - Math.round((size - 1) / 2) : y
drawCtx.drawImage(this.brushCavnas, startX, startY)
- this.painter.renderCanvas()
}
protected renderToolbar() {
super.renderToolbar()
diff --git a/src/painter/tools/PaintBucket.ts b/src/painter/tools/PaintBucket.ts
index 666b6e2..598ae1d 100644
--- a/src/painter/tools/PaintBucket.ts
+++ b/src/painter/tools/PaintBucket.ts
@@ -2,6 +2,7 @@ import Tool from './Tool'
import Color from 'licia/Color'
import isEqual from 'licia/isEqual'
import Painter from '../'
+import { colorDistance } from '../util'
export default class PaintBucket extends Tool {
constructor(painter: Painter) {
@@ -108,14 +109,3 @@ export default class PaintBucket extends Tool {
})
}
}
-
-function colorDistance(color1: number[], color2: number[]) {
- const r = Math.abs(color1[0] - color2[0])
- const g = Math.abs(color1[1] - color2[1])
- const b = Math.abs(color1[2] - color2[2])
- if (color1.length === 4 && color2.length === 4) {
- const a = Math.abs(color1[3] - color2[3])
- return Math.round((r + b + g + a) / 4)
- }
- return Math.round((r + b + g) / 3)
-}
diff --git a/src/painter/tools/Pencil.ts b/src/painter/tools/Pencil.ts
index 0e37a9b..4f33309 100644
--- a/src/painter/tools/Pencil.ts
+++ b/src/painter/tools/Pencil.ts
@@ -57,6 +57,7 @@ export default class Pencil extends Tool {
})
this.drawOptions = drawOptions as Required
this.draw(this.x, this.y)
+ this.painter.renderCanvas()
})
}
onDragMove(e: any) {
@@ -83,6 +84,7 @@ export default class Pencil extends Tool {
}
this.draw(x, y)
+ this.painter.renderCanvas()
}
onDragEnd(e: any) {
if (!this.isDrawing) {
@@ -145,7 +147,6 @@ export default class Pencil extends Tool {
const startX = size > 1 ? x - Math.floor((size - 1) / 2) : x
const startY = size > 1 ? y - Math.round((size - 1) / 2) : y
drawCtx.fillRect(startX, startY, size, size)
- this.painter.renderCanvas()
}
private commitDraw(ctx: CanvasRenderingContext2D) {
const { drawCanvas } = this
diff --git a/src/painter/util.ts b/src/painter/util.ts
index f2150f1..ea812f1 100644
--- a/src/painter/util.ts
+++ b/src/painter/util.ts
@@ -10,3 +10,19 @@ export function duplicateCanvas(
}
return result
}
+
+const ratio3 = 255 / Math.sqrt(255 * 255 * 3)
+const ratio4 = 255 / Math.sqrt(255 * 255 * 4)
+
+export function colorDistance(color1: number[], color2: number[]) {
+ const r = Math.abs(color1[0] - color2[0])
+ const g = Math.abs(color1[1] - color2[1])
+ const b = Math.abs(color1[2] - color2[2])
+ if (color1.length === 4 && color2.length === 4) {
+ const a = Math.abs(color1[3] - color2[3])
+ if (a !== 0) {
+ return Math.sqrt(r * r + g * g + b * b + a * a) * ratio4
+ }
+ }
+ return Math.sqrt(r * r + g * g + b * b) * ratio3
+}