@@ -15,7 +15,6 @@ const SHAPE_TYPE = {
1515 PEN : "pen" ,
1616 CIRCLE : "circle" ,
1717 ERASER : "eraser" ,
18- IMAGE : 'image' ,
1918} ;
2019
2120export const Canvas = ( ) => {
@@ -83,30 +82,6 @@ export const Canvas = () => {
8382 }
8483 } ;
8584
86- const handleImageUpload = ( file ) => {
87- if ( ! file ) return ;
88- const reader = new FileReader ( ) ;
89- reader . onload = ( event ) => {
90- const img = new Image ( ) ;
91- img . onload = ( ) => {
92- const newShape = {
93- id : Date . now ( ) . toString ( ) ,
94- type : SHAPE_TYPE . IMAGE ,
95- image : img ,
96- start : { x : 100 , y : 100 } ,
97- end : { x : 100 + img . width , y : 100 + img . height } ,
98- width : img . width ,
99- height : img . height ,
100- } ;
101- setShapes ( ( prev ) => [ ...prev , newShape ] ) ;
102- } ;
103- img . src = event . target . result ;
104- } ;
105- reader . readAsDataURL ( file ) ;
106- } ;
107-
108-
109-
11085 // --- Helpers ---
11186 const getWorldPoint = ( e ) => {
11287 const canvas = canvasRef . current ;
@@ -161,14 +136,6 @@ const handleImageUpload = (file) => {
161136 const maxY = Math . max ( ...shape . path . map ( p => p . y ) ) ;
162137 return { minX, minY, maxX, maxY, width : maxX - minX , height : maxY - minY } ;
163138 }
164- if ( shape . type === SHAPE_TYPE . IMAGE ) {
165- const minX = Math . min ( shape . start . x , shape . end . x ) ;
166- const maxX = Math . max ( shape . start . x , shape . end . x ) ;
167- const minY = Math . min ( shape . start . y , shape . end . y ) ;
168- const maxY = Math . max ( shape . start . y , shape . end . y ) ;
169- return { minX, minY, maxX, maxY, width : maxX - minX , height : maxY - minY } ;
170- }
171-
172139
173140 return null ;
174141} , [ ] ) ;
@@ -225,7 +192,7 @@ const handleImageUpload = (file) => {
225192 if ( isSelected ) {
226193 // draw purple glow under the stroke for visibility
227194 ctx . save ( ) ;
228- ctx . lineWidth = shape . type === SHAPE_TYPE . IMAGE ? 4 : shape . width + 4 ;
195+ ctx . lineWidth = shape . width + 4 ;
229196 ctx . strokeStyle = "rgba(76,29,149,1)" ;
230197 switch ( shape . type ) {
231198 case SHAPE_TYPE . LINE :
@@ -278,7 +245,7 @@ const handleImageUpload = (file) => {
278245
279246 // then draw the actual shape on top
280247 ctx . strokeStyle = shape . color ;
281- ctx . lineWidth = shape . type === SHAPE_TYPE . IMAGE ? 1 : shape . width ;
248+ ctx . lineWidth = shape . width ;
282249 }
283250
284251 switch ( shape . type ) {
@@ -550,15 +517,6 @@ const handleImageUpload = (file) => {
550517 ctx . globalAlpha = 1 ;
551518 }
552519 break ;
553- case SHAPE_TYPE . IMAGE : {
554- const { image, start, end } = shape ;
555- if ( image ) {
556- const width = end . x - start . x ;
557- const height = end . y - start . y ;
558- ctx . drawImage ( image , start . x , start . y , width , height ) ;
559- }
560- break ;
561- }
562520 default :
563521 break ;
564522 }
@@ -589,15 +547,12 @@ const handleImageUpload = (file) => {
589547 const ctx = canvas ?. getContext ( "2d" ) ;
590548 if ( ! ctx ) return ;
591549
592- // clear and reset transform
593- ctx . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ;
594- ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
595- ctx . globalCompositeOperation = "source-over" ;
596- ctx . globalAlpha = 1 ;
597- ctx . shadowBlur = 0 ;
550+ // clear and reset transform
551+ ctx . setTransform ( 1 , 0 , 0 , 1 , 0 , 0 ) ;
552+ ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
598553
599- // apply pan & zoom
600- ctx . setTransform ( 1 , 0 , 0 , 1 , offset . x , offset . y ) ;
554+ // apply pan & zoom
555+ ctx . setTransform ( 1 , 0 , 0 , 1 , offset . x , offset . y ) ;
601556 ctx . scale ( scale , scale ) ;
602557
603558 // draw non-selected shapes first
@@ -667,29 +622,18 @@ const handleImageUpload = (file) => {
667622 if ( handle && hitShape && hitShape . id === selectedShapeId ) {
668623 // begin resize
669624 const origShape = JSON . parse ( JSON . stringify ( hitShape ) ) ;
670- if ( hitShape . type === SHAPE_TYPE . IMAGE ) origShape . image = hitShape . image ;
671625 const origBBox = getShapeBBox ( origShape ) ;
672626 manipulationMode . current = { mode : 'resize' , dir : handle . dir , origShape, origBBox } ;
673627 setIsDrawing ( true ) ;
674628 return ;
675629 }
676630
677631 if ( hitShape ) {
678- try {
679- const _ctx = canvasRef . current ?. getContext ( '2d' ) ;
680- if ( _ctx ) {
681- _ctx . globalCompositeOperation = 'source-over' ;
682- _ctx . globalAlpha = 1 ;
683- _ctx . shadowBlur = 0 ;
684- }
685- } catch ( err ) {
686- console . debug ( '[canvas] composite reset failed' , err ) ;
687- }
688632 setSelectedShapeId ( hitShape . id ) ;
689633 setIsDrawing ( true ) ;
690- manipulationMode . current = { mode : "pending- move" } ;
634+ manipulationMode . current = { mode : "move" } ;
691635 setActiveColor ( hitShape . color ) ;
692- if ( hitShape . type === SHAPE_TYPE . PEN ) setStrokeWidth ( hitShape . width ) ;
636+ setStrokeWidth ( hitShape . width ) ;
693637 } else {
694638 setSelectedShapeId ( null ) ;
695639 setIsDrawing ( false ) ;
@@ -721,7 +665,7 @@ const handleImageUpload = (file) => {
721665
722666
723667 // creation tools
724- if ( ( Object . values ( SHAPE_TYPE ) . includes ( activeTool ) || activeTool . startsWith ( 'brush-' ) ) && activeTool !== SHAPE_TYPE . IMAGE ) {
668+ if ( Object . values ( SHAPE_TYPE ) . includes ( activeTool ) || activeTool . startsWith ( 'brush-' ) ) {
725669 setSelectedShapeId ( null ) ;
726670 setIsDrawing ( true ) ;
727671 manipulationMode . current = { mode : "create" } ;
@@ -742,15 +686,6 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
742686 newShape . brush = brushType || "solid" ;
743687 newShape . _seed = Math . floor ( Math . random ( ) * 0xffffffff ) ;
744688 }
745- if ( activeTool === SHAPE_TYPE . IMAGE ) {
746- const hitShape = shapes . slice ( ) . reverse ( ) . find ( ( shape ) => isPointInShape ( worldPoint , shape ) ) ;
747- if ( hitShape && hitShape . type === SHAPE_TYPE . IMAGE ) {
748- // Just select, don't draw a new one
749- setSelectedShapeId ( hitShape . id ) ;
750- setIsDrawing ( false ) ;
751- return ;
752- }
753- }
754689 if ( activeTool === SHAPE_TYPE . CIRCLE ) {
755690 newShape . radius = 0 ;
756691 }
@@ -767,13 +702,8 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
767702 if ( ! shape ) return false ;
768703 const bbox = getShapeBBox ( shape ) ;
769704 if ( ! bbox ) return false ;
770- const tol = shape . type === SHAPE_TYPE . IMAGE ? 8 : ( shape . width || 0 ) + 6 ;
771- return (
772- point . x >= bbox . minX - tol &&
773- point . x <= bbox . maxX + tol &&
774- point . y >= bbox . minY - tol &&
775- point . y <= bbox . maxY + tol
776- ) ;
705+ const tol = shape . width + 6 ;
706+ return ( point . x >= bbox . minX - tol && point . x <= bbox . maxX + tol && point . y >= bbox . minY - tol && point . y <= bbox . maxY + tol ) ;
777707 } ;
778708
779709 const draw = ( e ) => {
@@ -792,24 +722,6 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
792722 }
793723
794724 if ( ! isDrawing ) return ;
795- if (
796- activeTool === 'select' &&
797- selectedShapeId &&
798- manipulationMode . current &&
799- manipulationMode . current . mode === 'pending-move'
800- ) {
801- const dx0 = worldPoint . x - pointerStart . current . x ;
802- const dy0 = worldPoint . y - pointerStart . current . y ;
803- const distSq0 = dx0 * dx0 + dy0 * dy0 ;
804- const threshold = 4 * 4 ; // squared threshold in world coords
805- if ( distSq0 > threshold ) {
806- // begin move: set pointerStart so subsequent deltas work from here
807- manipulationMode . current . mode = 'move' ;
808- pointerStart . current = worldPoint ;
809- } else {
810- return ;
811- }
812- }
813725
814726 // MOVE
815727 if ( activeTool === 'select' && selectedShapeId && manipulationMode . current && manipulationMode . current . mode === 'move' ) {
@@ -841,9 +753,8 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
841753 const shapeIndex = shapes . findIndex ( ( s ) => s . id === selectedShapeId ) ;
842754 if ( shapeIndex === - 1 ) return ;
843755
844- const newShapes = [ ...shapes ] ;
845- const sh = JSON . parse ( JSON . stringify ( origShape ) ) ;
846- if ( origShape && origShape . type === SHAPE_TYPE . IMAGE ) sh . image = origShape . image ;
756+ const newShapes = [ ...shapes ] ;
757+ const sh = JSON . parse ( JSON . stringify ( origShape ) ) ;
847758
848759 // We'll compute a new bounding box keeping the opposite corner fixed depending on dir
849760 let { minX, minY, maxX, maxY } = origBBox ;
@@ -962,15 +873,14 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
962873 setIsPointerDown ( false ) ;
963874 if ( ! isDrawing ) return ;
964875 setIsDrawing ( false ) ;
965- const prevMode = manipulationMode . current ?. mode ;
966876 newShapeId . current = null ;
967877 manipulationMode . current = null ;
968- if ( prevMode === "erase" ) {
969- const ctx = canvasRef . current ? .getContext ( "2d" ) ;
970- if ( ctx ) ctx . globalCompositeOperation = "source-over" ;
971- }
878+ if ( manipulationMode . current ?. mode === "erase" ) {
879+ const ctx = canvasRef . current . getContext ( "2d" ) ;
880+ ctx . globalCompositeOperation = "source-over" ;
881+ }
972882
973- } ;
883+ } ;
974884
975885 // delete
976886 const handleDeleteSelectedShape = useCallback ( ( ) => {
@@ -1143,7 +1053,6 @@ if ((Object.values(SHAPE_TYPE).includes(activeTool) || activeTool.startsWith('br
11431053 onToolChange = { handleToolChange }
11441054 onClear = { handleClear }
11451055 onExport = { handleExport }
1146- onImageUpload = { handleImageUpload }
11471056 />
11481057
11491058 { joined ? (
0 commit comments