diff --git a/packages/app/src/AppNav.tsx b/packages/app/src/AppNav.tsx index 25a299e..73650f8 100644 --- a/packages/app/src/AppNav.tsx +++ b/packages/app/src/AppNav.tsx @@ -21,6 +21,8 @@ export default function AppNav() { setRemoveFullyErased, activeObject, erasable, + erasableBackground, + toggleErasableBackground, toggleErasable, } = useStore(); @@ -60,7 +62,6 @@ export default function AppNav() { label={Action} labelPlacement="bottom" /> - - + toggleErasableBackground(e.target.checked)} + /> + } + label={Erasable background} + labelPlacement="bottom" + /> {activeObject && ( toggleErasable( - value === 'deep' ? value : Boolean(Number(value)) + value === 'deep' ? value : Boolean(Number(value)), ) } exclusive diff --git a/packages/app/src/Fabric.tsx b/packages/app/src/Fabric.tsx index bf6ddc0..8f11b2a 100644 --- a/packages/app/src/Fabric.tsx +++ b/packages/app/src/Fabric.tsx @@ -1,13 +1,12 @@ import { EraserBrush } from '@erase2d/fabric'; import * as fabric from 'fabric'; -import { useCallback, useEffect, useRef } from 'react'; +import { useCallback, useEffect } from 'react'; import { Canvas } from '../src/Canvas'; import { useIsTransparentWorker } from '../src/useIsTransparentWorker'; import { useStore } from './Store'; export default function FabricPage() { - const { tool, removeFullyErased, setActiveObject } = useStore(); - const ref = useRef(null); + const { canvas: ref, tool, removeFullyErased, setActiveObject } = useStore(); const isTransparent = useIsTransparentWorker(); const onLoad = useCallback( @@ -109,10 +108,14 @@ export default function FabricPage() { originX: 'center', originY: 'center', }), - } + }, ), ]; + const img = document.createElement('img'); + img.src = 'https://cdn.viva.org.uk/wp-content/uploads/2020/08/Horses.jpg'; + canvas.backgroundImage = new fabric.FabricImage(img, { erasable: false }); + canvas.add( rect, ...(!!group @@ -124,7 +127,7 @@ export default function FabricPage() { }), ] : objects), - circle + circle, ); const animate = (toState: number) => { @@ -137,12 +140,12 @@ export default function FabricPage() { easing: toState ? fabric.util.ease.easeInOutQuad : fabric.util.ease.easeInOutSine, - } + }, ); }; animate(1); }, - [ref] + [ref], ); useEffect(() => { @@ -159,7 +162,7 @@ export default function FabricPage() { canvas.isDrawingMode = tool !== 'select'; return canvas.on( 'path:created', - ({ path }) => tool === 'draw' && (path.erasable = true) + ({ path }) => tool === 'draw' && (path.erasable = true), ); }, [ref, tool]); @@ -180,8 +183,8 @@ export default function FabricPage() { console.log(res); const transparent = await Promise.all( e.detail.targets.map( - async (target) => [target, await isTransparent(target)] as const - ) + async (target) => [target, await isTransparent(target)] as const, + ), ); const fullyErased = transparent .filter(([, transparent]) => transparent) @@ -201,7 +204,7 @@ export default function FabricPage() { const disposers = ( ['selection:created', 'selection:updated', 'selection:cleared'] as const ).map((ev) => - canvas.on(ev, () => setActiveObject(canvas.getActiveObject())) + canvas.on(ev, () => setActiveObject(canvas.getActiveObject())), ); return () => disposers.forEach((d) => d()); }, [ref, tool]); diff --git a/packages/app/src/Store.tsx b/packages/app/src/Store.tsx index 801e5bc..7bebbd5 100644 --- a/packages/app/src/Store.tsx +++ b/packages/app/src/Store.tsx @@ -1,14 +1,16 @@ -import type { FabricObject } from 'fabric'; +import { Canvas, type FabricObject } from 'fabric'; import { createContext, useCallback, useContext, useEffect, + useRef, useState, } from 'react'; import type { Tool } from '../src/tool'; export function useStoreData() { + const canvas = useRef(null); const [tool, setTool] = useState('erase'); const [removeFullyErased, setRemoveFullyErased] = useState(true); const [activeObject, setActiveObject] = useState(); @@ -25,10 +27,25 @@ export function useStoreData() { setErasable(erasable); tool !== 'erase' && tool !== 'undo' && setTool('erase'); }, - [activeObject, setErasable, tool] + [activeObject, setErasable, tool], + ); + const [erasableBackground, setErasableBackground] = useState< + boolean | 'deep' | undefined + >(); + const toggleErasableBackground = useCallback( + (erasable: boolean | 'deep') => { + const bg = canvas.current?.backgroundImage; + if (!bg) { + return; + } + bg.erasable = erasable; + setErasableBackground(erasable); + }, + [setErasable, tool], ); return { + canvas, tool, setTool, removeFullyErased, @@ -36,6 +53,8 @@ export function useStoreData() { activeObject, setActiveObject, erasable, + erasableBackground, + toggleErasableBackground, toggleErasable, }; } diff --git a/packages/fabric/index.ts b/packages/fabric/index.ts index a9c2027..6a5e6dc 100644 --- a/packages/fabric/index.ts +++ b/packages/fabric/index.ts @@ -2,9 +2,9 @@ export { ClippingGroup } from './src/ClippingGroup'; export { - EraserBrush, eraseCanvasDrawable, eraseObject, + EraserBrush, type ErasingEvent, type ErasingEventType, } from './src/EraserBrush';