CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-konva

React binding to canvas element via Konva framework providing declarative and reactive canvas graphics.

Pending
Overview
Eval results
Files

interactions.mddocs/

Event Handling and Interactions

Comprehensive event system and interactive components for React Konva. This includes mouse, touch, pointer, and drag events, as well as interactive transformation capabilities for creating engaging canvas applications.

Capabilities

Event System

React Konva provides a comprehensive event system that maps Konva events to React props, supporting all modern interaction patterns including mouse, touch, pointer, and custom events.

/**
 * Comprehensive event interface for all Konva components
 * Supports mouse, touch, pointer, drag, and transform events
 */
interface KonvaNodeEvents {
  // Mouse events
  onMouseOver?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  onMouseMove?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  onMouseOut?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  onMouseEnter?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  onMouseLeave?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  onMouseDown?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  onMouseUp?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  onWheel?(evt: Konva.KonvaEventObject<WheelEvent>): void;
  onClick?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  onDblClick?(evt: Konva.KonvaEventObject<MouseEvent>): void;
  
  // Touch events
  onTouchStart?(evt: Konva.KonvaEventObject<TouchEvent>): void;
  onTouchMove?(evt: Konva.KonvaEventObject<TouchEvent>): void;
  onTouchEnd?(evt: Konva.KonvaEventObject<TouchEvent>): void;
  onTap?(evt: Konva.KonvaEventObject<Event>): void;
  onDblTap?(evt: Konva.KonvaEventObject<Event>): void;
  
  // Drag events
  onDragStart?(evt: Konva.KonvaEventObject<DragEvent>): void;
  onDragMove?(evt: Konva.KonvaEventObject<DragEvent>): void;
  onDragEnd?(evt: Konva.KonvaEventObject<DragEvent>): void;
  
  // Transform events
  onTransform?(evt: Konva.KonvaEventObject<Event>): void;
  onTransformStart?(evt: Konva.KonvaEventObject<Event>): void;
  onTransformEnd?(evt: Konva.KonvaEventObject<Event>): void;
  
  // Pointer events
  onPointerDown?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerMove?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerUp?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerCancel?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerEnter?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerLeave?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerOver?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerOut?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerClick?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onPointerDblClick?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onGotPointerCapture?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  onLostPointerCapture?(evt: Konva.KonvaEventObject<PointerEvent>): void;
  
  // Context menu
  onContextMenu?(evt: Konva.KonvaEventObject<PointerEvent>): void;
}

Mouse Events

Standard mouse interaction events for desktop applications.

Usage Examples:

import React, { useState } from 'react';
import { Stage, Layer, Circle } from 'react-konva';

// Click and hover interactions
const InteractiveCircle = () => {
  const [color, setColor] = useState('blue');
  const [position, setPosition] = useState({ x: 100, y: 100 });

  const handleClick = (e) => {
    setColor(color === 'blue' ? 'red' : 'blue');
  };

  const handleMouseEnter = () => {
    document.body.style.cursor = 'pointer';
  };

  const handleMouseLeave = () => {
    document.body.style.cursor = 'default';
  };

  const handleMouseMove = (e) => {
    const stage = e.target.getStage();
    const pointerPos = stage.getPointerPosition();
    console.log('Mouse at:', pointerPos);
  };

  return (
    <Stage width={800} height={600}>
      <Layer>
        <Circle
          x={position.x}
          y={position.y}
          radius={50}
          fill={color}
          onClick={handleClick}
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}
          onMouseMove={handleMouseMove}
        />
      </Layer>
    </Stage>
  );
};

// Double-click and wheel events
const AdvancedMouseEvents = () => {
  const [scale, setScale] = useState(1);
  const [strokeWidth, setStrokeWidth] = useState(2);

  const handleDoubleClick = () => {
    setScale(scale === 1 ? 2 : 1);
  };

  const handleWheel = (e) => {
    e.evt.preventDefault();
    const newStrokeWidth = strokeWidth + (e.evt.deltaY > 0 ? -1 : 1);
    setStrokeWidth(Math.max(1, Math.min(10, newStrokeWidth)));
  };

  return (
    <Stage width={800} height={600}>
      <Layer>
        <Circle
          x={200}
          y={200}
          radius={50}
          fill="green"
          stroke="darkgreen"
          strokeWidth={strokeWidth}
          scaleX={scale}
          scaleY={scale}
          onDblClick={handleDoubleClick}
          onWheel={handleWheel}
        />
      </Layer>
    </Stage>
  );
};

Touch Events

Touch interaction events for mobile and tablet applications.

Usage Examples:

import React, { useState } from 'react';
import { Stage, Layer, Rect } from 'react-konva';

// Touch interactions
const TouchInteractive = () => {
  const [touches, setTouches] = useState([]);

  const handleTouchStart = (e) => {
    const touch = e.evt.touches[0];
    setTouches([{ id: Date.now(), x: touch.clientX, y: touch.clientY }]);
  };

  const handleTouchMove = (e) => {
    e.evt.preventDefault();
    const touch = e.evt.touches[0];
    setTouches(prev => prev.map(t => ({ ...t, x: touch.clientX, y: touch.clientY })));
  };

  const handleTouchEnd = () => {
    setTouches([]);
  };

  const handleTap = () => {
    console.log('Tapped!');
  };

  const handleDoubleTap = () => {
    console.log('Double tapped!');  
  };

  return (
    <Stage width={800} height={600}>
      <Layer>
        <Rect
          x={100}
          y={100}
          width={200}
          height={150}
          fill="orange"
          onTouchStart={handleTouchStart}
          onTouchMove={handleTouchMove}
          onTouchEnd={handleTouchEnd}
          onTap={handleTap}
          onDblTap={handleDoubleTap}
        />
      </Layer>
    </Stage>
  );
};

Drag and Drop

Drag and drop functionality for moving and repositioning canvas elements.

Usage Examples:

import React, { useState } from 'react';
import { Stage, Layer, Circle, Rect } from 'react-konva';

// Basic dragging
const DraggableShapes = () => {
  const [draggedItem, setDraggedItem] = useState(null);

  const handleDragStart = (e) => {
    setDraggedItem(e.target.name());
    e.target.moveToTop();
  };

  const handleDragEnd = (e) => {
    setDraggedItem(null);
    console.log('Dropped at:', e.target.position());
  };

  const handleDragMove = (e) => {
    // Constrain to bounds
    const stage = e.target.getStage();
    const box = e.target.getClientRect();
    
    const minX = 0;
    const maxX = stage.width() - box.width;
    const minY = 0;
    const maxY = stage.height() - box.height;
    
    e.target.x(Math.max(minX, Math.min(e.target.x(), maxX)));
    e.target.y(Math.max(minY, Math.min(e.target.y(), maxY)));
  };

  return (
    <Stage width={800} height={600}>
      <Layer>
        <Circle
          x={150}
          y={150}
          radius={50}
          fill="red"
          name="circle"
          draggable
          onDragStart={handleDragStart}
          onDragEnd={handleDragEnd}
          onDragMove={handleDragMove}
        />
        <Rect
          x={300}
          y={100}
          width={100}
          height={100}
          fill="blue"
          name="rect"
          draggable
          onDragStart={handleDragStart}
          onDragEnd={handleDragEnd}
          onDragMove={handleDragMove}
        />
      </Layer>
    </Stage>
  );
};

Pointer Events

Modern pointer events for unified handling of mouse, touch, and pen inputs.

Usage Examples:

import React, { useState } from 'react';
import { Stage, Layer, Line } from 'react-konva';

// Drawing with pointer events
const DrawingCanvas = () => {
  const [lines, setLines] = useState([]);
  const [isDrawing, setIsDrawing] = useState(false);

  const handlePointerDown = (e) => {
    setIsDrawing(true);
    const pos = e.target.getStage().getPointerPosition();
    setLines([...lines, { points: [pos.x, pos.y] }]);
  };

  const handlePointerMove = (e) => {
    if (!isDrawing) return;
    
    const stage = e.target.getStage();
    const point = stage.getPointerPosition();
    const lastLine = lines[lines.length - 1];
    
    setLines([
      ...lines.slice(0, -1),
      { ...lastLine, points: [...lastLine.points, point.x, point.y] }
    ]);
  };

  const handlePointerUp = () => {
    setIsDrawing(false);
  };

  return (
    <Stage 
      width={800} 
      height={600}
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
    >
      <Layer>
        {lines.map((line, i) => (
          <Line
            key={i}
            points={line.points}
            stroke="black"
            strokeWidth={2}
            tension={0.5}
            lineCap="round"
            lineJoin="round"
          />
        ))}
      </Layer>
    </Stage>
  );
};

Transformer Component

Interactive transformation component for resizing, rotating, and moving shapes with visual handles.

/**
 * Interactive transformation component
 * Provides visual handles for resizing, rotating, and moving shapes
 */
var Transformer: KonvaNodeComponent<Konva.Transformer, Konva.TransformerConfig>;

interface TransformerConfig extends Konva.NodeConfig {
  // Target nodes to transform
  nodes?: Konva.Node[];
  
  // Resize options
  enabledAnchors?: string[];
  rotateEnabled?: boolean;
  resizeEnabled?: boolean;
  
  // Styling
  borderEnabled?: boolean;
  borderStroke?: string;
  borderStrokeWidth?: number;
  borderDash?: number[];
  
  // Anchors
  anchorSize?: number;
  anchorStroke?: string;
  anchorStrokeWidth?: number;
  anchorFill?: string;
  anchorCornerRadius?: number;
  
  // Behavior
  keepRatio?: boolean;
  centeredScaling?: boolean;
  flipEnabled?: boolean;
  
  // Boundaries
  boundBoxFunc?: (oldBox: any, newBox: any) => any;
}

Usage Examples:

import React, { useState, useRef } from 'react';
import { Stage, Layer, Rect, Circle, Transformer } from 'react-konva';

// Basic transformer usage
const TransformableShapes = () => {
  const [selectedId, setSelectedId] = useState(null);
  const trRef = useRef();

  const shapes = [
    { id: '1', type: 'rect', x: 100, y: 100, width: 100, height: 100, fill: 'red' },
    { id: '2', type: 'circle', x: 300, y: 100, radius: 50, fill: 'blue' }
  ];

  const handleSelect = (id) => {
    setSelectedId(id);
  };

  const handleDeselect = (e) => {
    // Deselect when clicking on empty area
    if (e.target === e.target.getStage()) {
      setSelectedId(null);
    }
  };

  React.useEffect(() => {
    if (selectedId) {
      const selectedNode = stage.findOne(`#${selectedId}`);
      if (selectedNode && trRef.current) {
        trRef.current.nodes([selectedNode]);
        trRef.current.getLayer().batchDraw();
      }
    }
  }, [selectedId]);

  return (
    <Stage width={800} height={600} onMouseDown={handleDeselect}>
      <Layer>
        {shapes.map((shape) => (
          shape.type === 'rect' ? (
            <Rect
              key={shape.id}
              id={shape.id}
              x={shape.x}
              y={shape.y}
              width={shape.width}
              height={shape.height}
              fill={shape.fill}
              onClick={() => handleSelect(shape.id)}
              draggable
            />
          ) : (
            <Circle
              key={shape.id}
              id={shape.id}
              x={shape.x}
              y={shape.y}
              radius={shape.radius}
              fill={shape.fill}
              onClick={() => handleSelect(shape.id)}
              draggable
            />
          )
        ))}
        
        {selectedId && (
          <Transformer
            ref={trRef}
            rotateEnabled={true}
            resizeEnabled={true}
            enabledAnchors={[
              'top-left', 'top-center', 'top-right',
              'middle-right', 'middle-left',
              'bottom-left', 'bottom-center', 'bottom-right'
            ]}
            borderEnabled={true}
            borderStroke="blue"
            borderStrokeWidth={1}
            borderDash={[4, 4]}
            anchorSize={8}
            anchorStroke="blue"
            anchorFill="white"
            anchorStrokeWidth={1}
            keepRatio={false}
            onTransformEnd={(e) => {
              console.log('Transform complete:', e.target.attrs);
            }}
          />
        )}
      </Layer>
    </Stage>
  );
};

// Advanced transformer with constraints
const ConstrainedTransformer = () => {
  const [selectedId, setSelectedId] = useState(null);
  const trRef = useRef();

  const boundBoxFunc = (oldBox, newBox) => {
    // Limit minimum size
    if (newBox.width < 20 || newBox.height < 20) {
      return oldBox;
    }
    
    // Limit maximum size
    if (newBox.width > 200 || newBox.height > 200) {
      return oldBox;
    }
    
    return newBox;
  };

  return (
    <Stage width={800} height={600}>
      <Layer>
        <Rect
          id="constrained-rect"
          x={200}
          y={200}
          width={100}
          height={100}
          fill="green"
          onClick={() => setSelectedId('constrained-rect')}
          draggable
        />
        
        {selectedId && (
          <Transformer
            ref={trRef}
            nodes={[]}
            keepRatio={true}
            centeredScaling={false}
            enabledAnchors={['top-left', 'top-right', 'bottom-left', 'bottom-right']}
            boundBoxFunc={boundBoxFunc}
            onTransform={(e) => {
              // Real-time transform feedback
              console.log('Transforming:', e.target.attrs);
            }}
          />
        )}
      </Layer>
    </Stage>
  );
};

Event Object Properties

The Konva event object provides access to both the original browser event and Konva-specific information.

/**
 * Konva event object structure
 * Provides access to original event and Konva-specific data
 */
interface KonvaEventObject<T> {
  evt: T; // Original browser event
  target: Konva.Node; // Konva node that triggered the event
  currentTarget: Konva.Node; // Current event target in bubbling phase
  type: string; // Event type
  cancelBubble: boolean; // Controls event bubbling
}

Usage Examples:

import React from 'react';
import { Stage, Layer, Circle } from 'react-konva';

// Accessing event properties
const EventExample = () => {
  const handleClick = (e) => {
    // Original browser event
    console.log('Browser event:', e.evt);
    
    // Konva node that was clicked
    console.log('Target node:', e.target);
    
    // Event type
    console.log('Event type:', e.type);
    
    // Stage pointer position
    const stage = e.target.getStage();
    const pointerPos = stage.getPointerPosition();
    console.log('Pointer position:', pointerPos);
    
    // Node position
    console.log('Node position:', e.target.position());
    
    // Prevent event bubbling
    e.cancelBubble = true;
  };

  return (
    <Stage width={800} height={600}>
      <Layer onClick={() => console.log('Layer clicked')}>
        <Circle
          x={200}
          y={200}
          radius={50}
          fill="purple"
          onClick={handleClick}
        />
      </Layer>
    </Stage>
  );
};

Install with Tessl CLI

npx tessl i tessl/npm-react-konva

docs

core-components.md

index.md

interactions.md

shapes.md

utilities.md

tile.json