Skip to content

Pad ​

Interactive drawing and sketching component with support for multiple stroke types, shapes, colors, and undo/redo functionality.

Overview ​

VOvPad is a versatile drawing canvas component that enables freehand drawing, shape creation, and sketching capabilities. It supports multiple stroke types (dash, line, square, circle, triangle, half_triangle), customizable colors and line styles, background images, and complete undo/redo history. The component is fully touch-compatible and responds to mouse, touch, and pointer events.

Features:

  • 6 stroke types with shape guides and real-time preview
  • Freehand drawing with customizable brush properties
  • Shape filling and styling options
  • Eraser functionality with background color awareness
  • Complete undo/redo history tracking
  • Touch, mouse, and pointer event support
  • Background image support with scaling
  • Canvas locking to prevent modifications
  • Export to PNG or JPEG with custom dimensions
  • Full TypeScript support

Dependencies ​

No external dependencies required. The component uses native Canvas API.

Usage ​

Examples
vue
<template>
  <v-container fluid>
    <v-ov-pad
      ref="pad"
      class="border"
      :width="'100%'"
      :height="'240px'"
      :lock="locked"
      :eraser="eraser"
      :background-color="backgroundColor"
      :background-image="backgroundImage"
      :lineWidth="line"
      :color="color"
    />
    <v-btn @click="reset">Clear</v-btn>
    <v-btn @click="undo">Undo</v-btn>
    <v-btn @click="redo">Redo</v-btn>
    <v-btn @click="locked = !locked">{{ locked ? 'Unlock' : 'Lock' }}</v-btn>
    <v-menu>
      <template #activator="{ props }">
        <v-btn v-bind="props" :color="color">Color</v-btn>
      </template>
      <v-card class="pa-4">
        <v-color-picker v-model="color" label="Color"></v-color-picker>
      </v-card>
    </v-menu>
    <v-btn @click="backgroundColor = backgroundColor === 'white' ? 'lightgray' : 'white'"
      >Toggle background color</v-btn
    >
    <v-btn @click="eraser = !eraser">{{ eraser ? 'Pen' : 'Eraser' }}</v-btn>

    <v-file-input label="Background image" @change="setImage($event)"></v-file-input>
    <v-slider v-model="line" label="Line width" min="1" max="20"></v-slider>
    <v-btn @click="save">Save</v-btn>
  </v-container>
</template>

<script setup lang="ts">
definePage({
  meta: {
    visibility: 'with-role',
    access: 'with-role',
    roles: ['developer'],
  },
})

import VOvPad from '@/components/VOvPad.vue'

const pad = ref<typeof VOvPad | null>(null)

const locked = ref(false)
const eraser = ref(false)
const backgroundColor = ref('white')
const backgroundImage = ref('')
const line = ref(5)
const color = ref('#000000')

const reset = () => {
  if (!pad.value) return
  pad.value.reset()
}

const undo = () => {
  if (!pad.value) return
  pad.value.undo()
}

const redo = () => {
  if (!pad.value) return
  pad.value.redo()
}

const setImage = (e: Event) => {
  const file = (e.target as HTMLInputElement).files?.[0]
  if (file) {
    backgroundImage.value = window.URL.createObjectURL(file)
  }
}

const save = () => {
  if (!pad.value) return
  const data = pad.value.save()
  console.log(data)
}
</script>

<style scoped>
* {
  margin: 0.25rem;
  padding: 0.25rem;
}
</style>

API ​

Props ​

Dimensions ​

PropTypeDefaultDescription
widthstring | number'100%'Canvas container width (CSS value or pixels)
heightstring | number'100%'Canvas container height (CSS value or pixels)

Drawing Settings ​

PropTypeDefaultDescription
colorstring'#000000'Drawing color (hex format)
lineWidthnumber5Brush stroke width in pixels
lineCap'round' | 'square' | 'butt''round'Line ending style
lineJoin'miter' | 'round' | 'bevel''miter'Line join style

Stroke Type ​

PropTypeDefaultDescription
strokeType'dash' | 'line' | 'square' | 'circle' | 'triangle' | 'half_triangle''dash'Current drawing mode
fillShapebooleanfalseFill shapes instead of stroking outlines

Background & Canvas ​

PropTypeDefaultDescription
backgroundColorstring'#FFFFFF'Canvas background color (hex format)
backgroundImagestring | nullnullBackground image URL or data URI
canvasIdstringrandomUnique identifier for canvas element

Canvas Behavior ​

PropTypeDefaultDescription
eraserbooleanfalseEnable eraser mode (uses backgroundColor)
lockbooleanfalsePrevent drawing, undo, redo, and reset operations

Output Format ​

PropTypeDefaultDescription
saveAs'jpeg' | 'png''png'Export format
outputWidthnumbercanvas widthCustom output image width
outputHeightnumbercanvas heightCustom output image height

Image Data ​

PropTypeDefaultDescription
initialImageStroke[][]Pre-drawn strokes to load on mount
additionalImagesStroke[][]Additional image layers
imagestring''Legacy image prop for compatibility

Events ​

EventPayloadDescription
update:imagestring (data URL)Emitted when canvas is saved or modified

Exposed Methods ​

MethodParametersReturnsDescription
clear()-voidClear all canvas content without resetting state
reset()-voidReset canvas to initial state (requires lock: false)
undo()-voidUndo last stroke (requires lock: false)
redo()-voidRedo last undone stroke (requires lock: false)
save()-stringExport canvas as data URL and emit update:image
startDraw()eventvoidBegin drawing stroke from event coordinates
draw()eventvoidContinue drawing (throttled to ~60fps)
stopDraw()-voidFinalize current stroke and add to history
handleResize()-voidManually trigger canvas resize (called automatically on window resize)

Stroke Types ​

Dash ​

Free-hand drawing with customizable brush properties.

typescript
startDraw()  // Begin stroke
draw(event)  // Draw freehand line as mouse moves
stopDraw()   // Finish and save stroke

Line ​

Click-drag to create straight line with guide preview.

typescript
startDraw()  // Click to set start point
draw(event)  // Drag to show line guide
stopDraw()   // Release to finalize line

Square ​

Click-drag to create rectangular shape.

typescript
startDraw()  // Click to set first corner
draw(event)  // Drag to show preview rectangle
stopDraw()   // Release to finalize square

Circle ​

Click-drag to create circular shape based on radius.

typescript
startDraw()  // Click to set center
draw(event)  // Drag to adjust radius
stopDraw()   // Release to finalize circle

Triangle ​

Click-drag to create triangle shape.

typescript
startDraw()  // Click to set first point
draw(event)  // Drag to show triangle guide
stopDraw()   // Release to finalize triangle

Half Triangle ​

Click-drag to create half-triangle (right triangle).

typescript
startDraw()  // Click to set first point
draw(event)  // Drag to show half-triangle guide
stopDraw()   // Release to finalize shape