141141 # canvas {
142142 background-color : white;
143143 display : none;
144+ touch-action : none;
145+ border : 1px solid # ccc ;
144146 }
145147
146148 .color-picker {
@@ -311,6 +313,12 @@ <h2>Your Notes</h2>
311313 let startX , startY ;
312314 let autoSaveTimeout ;
313315 const maxStackSize = 50 ; // Limit for undo/redo stack
316+ let tempCanvas = document . createElement ( 'canvas' ) ;
317+ let tempCtx = tempCanvas . getContext ( '2d' ) ;
318+ let currentX , currentY ; // Store current mouse position
319+ let animationFrameId = null ;
320+ let points = [ ] ;
321+ let lastPoint = null ;
314322
315323 // DOM elements
316324 const canvas = document . getElementById ( 'canvas' ) ;
@@ -344,6 +352,12 @@ <h2>Your Notes</h2>
344352 canvas . addEventListener ( 'mouseup' , stopDrawing ) ;
345353 canvas . addEventListener ( 'mouseout' , stopDrawing ) ;
346354
355+ // Touch and Pencil events
356+ canvas . addEventListener ( 'touchstart' , handleTouchStart , { passive : false } ) ;
357+ canvas . addEventListener ( 'touchmove' , handleTouchMove , { passive : false } ) ;
358+ canvas . addEventListener ( 'touchend' , handleTouchEnd ) ;
359+ canvas . addEventListener ( 'touchcancel' , handleTouchEnd ) ;
360+
347361 // Window resize handler
348362 window . addEventListener ( 'resize' , function ( ) {
349363 if ( canvas . style . display !== 'none' ) {
@@ -387,28 +401,26 @@ <h2>Your Notes</h2>
387401 saveStatus . style . display = 'block' ;
388402 saveStatus . textContent = 'Changes saved!' ;
389403
390- // Remove any existing timeout
391404 if ( saveStatus . timeout ) {
392405 clearTimeout ( saveStatus . timeout ) ;
393406 }
394407
395- // Set new timeout
396408 saveStatus . timeout = setTimeout ( ( ) => {
397409 saveStatus . style . display = 'none' ;
398410 } , 2000 ) ;
399411 }
400412
401413 function switchTab ( index ) {
402- if ( currentTab === index ) return ; // Don't switch if clicking current tab
414+ if ( currentTab === index ) return ;
403415
404- saveNotes ( true ) ; // Save current tab
416+ saveNotes ( true ) ;
405417 currentTab = index ;
406418 const tabs = document . querySelectorAll ( '.tab' ) ;
407419 tabs . forEach ( ( tab , i ) => {
408420 tab . classList . toggle ( 'active' , i === index ) ;
409421 } ) ;
410422 loadNoteContent ( ) ;
411- saveToUndoStack ( ) ; // Initialize undo stack for new tab
423+ saveToUndoStack ( ) ;
412424 }
413425
414426 function loadNoteContent ( ) {
@@ -440,7 +452,6 @@ <h2>Your Notes</h2>
440452 canvas . style . display = 'none' ;
441453 document . getElementById ( 'textFormatTools' ) . style . display = 'flex' ;
442454 document . querySelector ( 'button[onclick="switchMode(\'text\')"]' ) . classList . add ( 'active' ) ;
443- // Hide drawing tools
444455 document . querySelectorAll ( '.tool-button:not([onclick^="switchMode"])' ) . forEach ( btn =>
445456 btn . style . display = 'none'
446457 ) ;
@@ -449,10 +460,10 @@ <h2>Your Notes</h2>
449460 canvas . style . display = 'block' ;
450461 document . getElementById ( 'textFormatTools' ) . style . display = 'none' ;
451462 document . querySelector ( 'button[onclick="switchMode(\'draw\')"]' ) . classList . add ( 'active' ) ;
452- // Show drawing tools
453463 document . querySelectorAll ( '.tool-button:not([onclick^="switchMode"])' ) . forEach ( btn =>
454464 btn . style . display = 'inline-block'
455465 ) ;
466+ setupCanvas ( ) ;
456467 }
457468 }
458469
@@ -462,7 +473,6 @@ <h2>Your Notes</h2>
462473 buttons . forEach ( btn => btn . classList . remove ( 'active' ) ) ;
463474 document . querySelector ( `button[onclick="setTool('${ tool } ')"]` ) . classList . add ( 'active' ) ;
464475
465- // Reset drawing state
466476 ctx . beginPath ( ) ;
467477 isDrawing = false ;
468478 }
@@ -495,6 +505,294 @@ <h2>Your Notes</h2>
495505 }
496506 saveToUndoStack ( ) ;
497507 }
508+
509+ function startDrawing ( e ) {
510+ isDrawing = true ;
511+ const rect = canvas . getBoundingClientRect ( ) ;
512+ const scaleX = canvas . width / rect . width ;
513+ const scaleY = canvas . height / rect . height ;
514+
515+ startX = ( e . clientX - rect . left ) * scaleX ;
516+ startY = ( e . clientY - rect . top ) * scaleY ;
517+ currentX = startX ;
518+ currentY = startY ;
519+
520+ // Reset points array and last point
521+ points = [ ] ;
522+ lastPoint = null ;
523+
524+ if ( currentTool === 'pen' ) {
525+ ctx . beginPath ( ) ;
526+ ctx . moveTo ( startX , startY ) ;
527+ }
528+
529+ saveToUndoStack ( ) ;
530+ }
531+
532+ function draw ( e ) {
533+ if ( ! isDrawing ) return ;
534+
535+ const rect = canvas . getBoundingClientRect ( ) ;
536+ const scaleX = canvas . width / rect . width ;
537+ const scaleY = canvas . height / rect . height ;
538+
539+ currentX = ( e . clientX - rect . left ) * scaleX ;
540+ currentY = ( e . clientY - rect . top ) * scaleY ;
541+
542+ // Get pressure from Apple Pencil if available
543+ let pressure = e . pressure || e . force || 1 ;
544+
545+ if ( currentTool === 'pen' || currentTool === 'eraser' ) {
546+ points . push ( {
547+ x : currentX ,
548+ y : currentY ,
549+ pressure : pressure
550+ } ) ;
551+
552+ if ( points . length > 4 ) {
553+ points . shift ( ) ;
554+ }
555+
556+ if ( points . length > 1 ) {
557+ ctx . strokeStyle = currentTool === 'eraser' ? '#ffffff' : currentColor ;
558+ ctx . lineCap = 'round' ;
559+ ctx . lineJoin = 'round' ;
560+
561+ // Apply pressure sensitivity to line width
562+ ctx . lineWidth = currentSize * pressure ;
563+
564+ // Start a new path
565+ ctx . beginPath ( ) ;
566+
567+ // Move to the first point
568+ if ( lastPoint ) {
569+ ctx . moveTo ( lastPoint . x , lastPoint . y ) ;
570+ } else {
571+ ctx . moveTo ( points [ 0 ] . x , points [ 0 ] . y ) ;
572+ }
573+
574+ // If we have enough points, draw a smooth curve
575+ if ( points . length > 2 ) {
576+ // Calculate control points
577+ const xc = ( points [ 1 ] . x + points [ 2 ] . x ) / 2 ;
578+ const yc = ( points [ 1 ] . y + points [ 2 ] . y ) / 2 ;
579+
580+ // Draw curve using quadratic bezier
581+ ctx . quadraticCurveTo ( points [ 1 ] . x , points [ 1 ] . y , xc , yc ) ;
582+ } else {
583+ // Draw straight line if only 2 points
584+ ctx . lineTo ( points [ points . length - 1 ] . x , points [ points . length - 1 ] . y ) ;
585+ }
586+
587+ ctx . stroke ( ) ;
588+
589+ // Store last point
590+ lastPoint = points [ points . length - 1 ] ;
591+ }
592+ } else {
593+ // For shapes, use requestAnimationFrame
594+ if ( ! animationFrameId ) {
595+ tempCanvas . width = canvas . width ;
596+ tempCanvas . height = canvas . height ;
597+ tempCtx . clearRect ( 0 , 0 , tempCanvas . width , tempCanvas . height ) ;
598+ tempCtx . drawImage ( canvas , 0 , 0 ) ;
599+ animationFrameId = requestAnimationFrame ( drawShape ) ;
600+ }
601+ }
602+ }
603+
604+ function drawShape ( ) {
605+ // Clear main canvas and restore the initial state
606+ ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
607+ ctx . drawImage ( tempCanvas , 0 , 0 ) ;
608+
609+ // Draw the current shape
610+ ctx . beginPath ( ) ;
611+ ctx . strokeStyle = currentColor ;
612+ ctx . lineWidth = currentSize ;
613+ ctx . lineCap = 'round' ;
614+
615+ if ( currentTool === 'rectangle' ) {
616+ ctx . rect ( startX , startY , currentX - startX , currentY - startY ) ;
617+ } else if ( currentTool === 'circle' ) {
618+ const radius = Math . sqrt ( Math . pow ( currentX - startX , 2 ) + Math . pow ( currentY - startY , 2 ) ) ;
619+ ctx . arc ( startX , startY , radius , 0 , 2 * Math . PI ) ;
620+ } else if ( currentTool === 'line' ) {
621+ ctx . moveTo ( startX , startY ) ;
622+ ctx . lineTo ( currentX , currentY ) ;
623+ }
624+ ctx . stroke ( ) ;
625+
626+ // Continue animation if still drawing
627+ if ( isDrawing ) {
628+ animationFrameId = requestAnimationFrame ( drawShape ) ;
629+ } else {
630+ animationFrameId = null ;
631+ }
632+ }
633+
634+ function stopDrawing ( ) {
635+ if ( ! isDrawing ) return ;
636+ isDrawing = false ;
637+ ctx . closePath ( ) ;
638+
639+ // Reset points array and last point
640+ points = [ ] ;
641+ lastPoint = null ;
642+
643+ // Cancel any pending animation frame
644+ if ( animationFrameId ) {
645+ cancelAnimationFrame ( animationFrameId ) ;
646+ animationFrameId = null ;
647+ }
648+
649+ // Save the current state
650+ notes [ currentTab ] . drawing = canvas . toDataURL ( ) ;
651+ saveNotes ( true ) ;
652+ }
653+
654+ function loadDrawing ( dataUrl , callback ) {
655+ if ( ! dataUrl ) {
656+ ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
657+ if ( callback ) callback ( ) ;
658+ return ;
659+ }
660+
661+ const img = new Image ( ) ;
662+ img . onload = function ( ) {
663+ ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
664+ ctx . drawImage ( img , 0 , 0 ) ;
665+ if ( callback ) callback ( ) ;
666+ } ;
667+ img . src = dataUrl ;
668+ }
669+
670+ function saveToUndoStack ( ) {
671+ undoStack . push ( {
672+ text : textarea . value ,
673+ drawing : canvas . toDataURL ( )
674+ } ) ;
675+
676+ if ( undoStack . length > maxStackSize ) {
677+ undoStack . shift ( ) ;
678+ }
679+ redoStack = [ ] ;
680+ }
681+
682+ function undoAction ( ) {
683+ if ( undoStack . length === 0 ) return ;
684+
685+ redoStack . push ( {
686+ text : textarea . value ,
687+ drawing : canvas . toDataURL ( )
688+ } ) ;
689+
690+ const previousState = undoStack . pop ( ) ;
691+ textarea . value = previousState . text ;
692+ loadDrawing ( previousState . drawing ) ;
693+
694+ notes [ currentTab ] . text = previousState . text ;
695+ notes [ currentTab ] . drawing = previousState . drawing ;
696+ saveNotes ( true ) ;
697+ }
698+
699+ function redoAction ( ) {
700+ if ( redoStack . length === 0 ) return ;
701+
702+ undoStack . push ( {
703+ text : textarea . value ,
704+ drawing : canvas . toDataURL ( )
705+ } ) ;
706+
707+ const nextState = redoStack . pop ( ) ;
708+ textarea . value = nextState . text ;
709+ loadDrawing ( nextState . drawing ) ;
710+
711+ notes [ currentTab ] . text = nextState . text ;
712+ notes [ currentTab ] . drawing = nextState . drawing ;
713+ saveNotes ( true ) ;
714+ }
715+
716+ function saveNotes ( autosave = false ) {
717+ notes [ currentTab ] . text = textarea . value ;
718+ notes [ currentTab ] . drawing = canvas . toDataURL ( ) ;
719+ localStorage . setItem ( "allNotes" , JSON . stringify ( notes ) ) ;
720+
721+ if ( ! autosave ) {
722+ showSaveStatus ( ) ;
723+ }
724+ }
725+
726+ function clearNotes ( ) {
727+ if ( confirm ( 'Are you sure you want to clear the current note?' ) ) {
728+ saveToUndoStack ( ) ;
729+ textarea . value = '' ;
730+ ctx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
731+ notes [ currentTab ] = { text : '' , drawing : null } ;
732+ saveNotes ( ) ;
733+ }
734+ }
735+
736+ function showDownloadOptions ( ) {
737+ const options = document . getElementById ( 'download-options' ) ;
738+ options . classList . toggle ( 'show' ) ;
739+ }
740+
741+ function downloadAsPDF ( ) {
742+ const { jsPDF } = window . jspdf ;
743+ const pdf = new jsPDF ( ) ;
744+
745+ // Add text
746+ pdf . text ( notes [ currentTab ] . text , 10 , 10 ) ;
747+
748+ // Add drawing if exists
749+ if ( notes [ currentTab ] . drawing ) {
750+ pdf . addImage ( notes [ currentTab ] . drawing , 'PNG' , 10 , 100 , 190 , 100 ) ;
751+ }
752+
753+ pdf . save ( `note${ currentTab + 1 } .pdf` ) ;
754+ }
755+
756+ function downloadAsImage ( ) {
757+ const link = document . createElement ( 'a' ) ;
758+ link . download = `note${ currentTab + 1 } .png` ;
759+ link . href = canvas . toDataURL ( ) ;
760+ link . click ( ) ;
761+ }
762+
763+ function downloadAsTXT ( ) {
764+ const link = document . createElement ( 'a' ) ;
765+ const file = new Blob ( [ notes [ currentTab ] . text ] , { type : 'text/plain' } ) ;
766+ link . href = URL . createObjectURL ( file ) ;
767+ link . download = `note${ currentTab + 1 } .txt` ;
768+ link . click ( ) ;
769+ }
770+
771+ // Add these new touch handling functions
772+ function handleTouchStart ( e ) {
773+ e . preventDefault ( ) ; // Prevent scrolling
774+ const touch = e . touches [ 0 ] ;
775+ const mouseEvent = new MouseEvent ( 'mousedown' , {
776+ clientX : touch . clientX ,
777+ clientY : touch . clientY
778+ } ) ;
779+ startDrawing ( mouseEvent ) ;
780+ }
781+
782+ function handleTouchMove ( e ) {
783+ e . preventDefault ( ) ; // Prevent scrolling
784+ const touch = e . touches [ 0 ] ;
785+ const mouseEvent = new MouseEvent ( 'mousemove' , {
786+ clientX : touch . clientX ,
787+ clientY : touch . clientY
788+ } ) ;
789+ draw ( mouseEvent ) ;
790+ }
791+
792+ function handleTouchEnd ( e ) {
793+ e . preventDefault ( ) ;
794+ stopDrawing ( ) ;
795+ }
498796 </ script >
499797</ body >
500798</ html >
0 commit comments