@vi5hnuu/ngx-scribbly
v1.0.5
Published
A flexible Angular directive to draw any custom elements on screen via mouse interaction, with aspect-ratio locking and full shape support.
Maintainers
Readme
🖋️ ngx-scribbly
ngx-scribbly is an Angular directive that allows users to draw resizable, absolutely positioned components or templates inside any container, with full support for aspect ratio locking, dynamic component rendering, and bounding detection.
Perfect for apps like image annotation tools, design systems, or content creation overlays.
📦 Installation
npm i @vi5hnuu/ngx-scribbly🚀 Features
- Draw Angular components or templates dynamically
- Emits
ScribbleConfigwith exact placement and size - Maintains aspect ratio (optional)
- Blocks drawing outside host element bounds
- Supports both
ComponentTypeandTemplateRef - Reactive
isDrawingoutput for external tools
🔧 Usage
1. Import ScribbleDirective
import {ScribbleDirective,ScribbleConfig} from '@vi5hnuu/ngx-scribbly'
@Component({
selector: "app-root",
standalone: true,
imports: [ScribbleDirective],
templateUrl: "./app.component.html",
styleUrl: "./app.component.css",
})
export class AppComponent {
title = "scribble-app"
}
2. Apply scribble directive to any container
<div scribble
[active]="allowShapeDraw"
[miniSize]="{width:50,height:50}"
(rendered)="onRender($event)"
(isDrawing)="isDrawing($event)"
[selectedMapping]="'star'"
[maintainAspect]="returnNumberOrBooleanFn"
[typeMapping]="{'star':StarTemplate}">
</div>
<ng-template #StarTemplate>
<div class="shape">
<img class="image" src="star.png" alt="shape"/>
</div>
</ng-template>3. Inputs and Outputs
🏷️ Inputs
| Input | Type | Description |
| ----------------- | ------------------------------------------------------------------- | -------------------------------------------------------- |
| typeMapping | TypeMappings | Map of available drawing elements |
| selectedMapping | string \| null | Key to use from typeMapping |
| maintainAspect | (event: MouseEvent) => { maintainAspectRatio: boolean \| number } | Lock aspect ratio. Use a number for fixed (e.g., 16/9) |
export interface TypeMappings { [key:string]:TypeMapping}
export interface TypeMapping{type:ComponentType<any>|TemplateRef<any>,args?:{[key:string]:any}}📤 Outputs
| Output | Type | Description |
| ----------- | ------------------------------ | --------------------------------- |
| rendered | EventEmitter<ScribbleConfig> | Emits when an element is rendered |
| isDrawing | EventEmitter<boolean> | Emits drawing state |
4. ScribbleConfig
export interface ScribbleConfig {
info: {
left: number;
top: number;
width: number;
height: number;
}; // Relative to host
createdEl: HTMLElement; // Rendered element
mapping: string; // Mapping key used
event: Readonly<MouseEvent>; // Final mouseup event
}🧠 Example
@Component({...})
export class EditorComponent {
selectedMapping = 'textBox';
typeMapping = {
textBox: TextComponent,
shapeBox: this.tpl,
};
@ViewChild('shapeTpl', { read: TemplateRef }) tpl!: TemplateRef<any>;
aspectFn = (event: MouseEvent) => ({ maintainAspectRatio: true });
onRendered(config: ScribbleConfig) {
console.log('Drawn:', config);
}
onIsDrawing(is: boolean) {
console.log('Drawing mode:', is);
}
}<ng-template #shapeTpl>
<div style="border: 2px dashed red; background: rgba(0,0,0,0.1);"></div>
</ng-template>📏 Behavior Notes
- Drawing is allowed only within host boundaries
- Aspect ratio locks width and adjust height to maintain aspect
- Resets rendering if mapping changes mid-draw [abort drawing]
- Multiple root nodes in
TemplateRefare not allowed
🧹 Cleanup
Directive automatically unsubscribes from observables on destroy.
✅ License
MIT License. Created by Vishnu kumar.
🔗 Connect with Me
Feel free to connect with me on social media:
