100 lines
2.3 KiB
JavaScript
100 lines
2.3 KiB
JavaScript
|
import {Shape} from './Shape.js';
|
||
|
import {Rect, Text, ClipPath, Defs, G} from '@svgdotjs/svg.js';
|
||
|
import {ColorTypes} from '../shapes/Shape.js';
|
||
|
import {overlayAnnotation} from '../workers/process.js';
|
||
|
|
||
|
/**
|
||
|
* Creates an SVG frame from Tldraw v2 pencil data.
|
||
|
*
|
||
|
* @class Frane
|
||
|
* @extends {Shape}
|
||
|
*/
|
||
|
export class Frame extends Shape {
|
||
|
/**
|
||
|
* @param {Object} frame - The Frame shape JSON.
|
||
|
*/
|
||
|
constructor(frame) {
|
||
|
super(frame);
|
||
|
this.name = this.props?.name;
|
||
|
this.w = this.props?.w;
|
||
|
this.h = this.props?.h;
|
||
|
this.children = frame.children;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Renders the frame object as an SVG group element.
|
||
|
*
|
||
|
* @return {G} - An SVG group element.
|
||
|
*/
|
||
|
draw() {
|
||
|
// Parent group
|
||
|
const frameGroup = this.shapeGroup;
|
||
|
|
||
|
// Group for clipped elements
|
||
|
const clipGroup = new G();
|
||
|
|
||
|
const fillColor = Shape.colorToHex(ColorTypes.SemiFillColor,
|
||
|
ColorTypes.SemiFillColor);
|
||
|
|
||
|
const frameLabel = this.name || 'Frame';
|
||
|
|
||
|
// The text element is not clipped
|
||
|
const textElement = new Text()
|
||
|
.text(frameLabel)
|
||
|
.move(0, -20)
|
||
|
.font({
|
||
|
'family': 'Arial',
|
||
|
'size': 12,
|
||
|
})
|
||
|
.fill('black');
|
||
|
|
||
|
// The frame rectangle that is not clipped
|
||
|
const frame = new Rect({
|
||
|
'x': 0,
|
||
|
'y': 0,
|
||
|
'width': this.w,
|
||
|
'height': this.h,
|
||
|
'stroke': 'black',
|
||
|
'stroke-width': 1,
|
||
|
'fill': fillColor,
|
||
|
});
|
||
|
|
||
|
// Create the clip path with the same properties as the frame
|
||
|
const clipPath = new ClipPath().id(`clipFrame-${this.id}`);
|
||
|
const clipFrame = new Rect({
|
||
|
'x': 0,
|
||
|
'y': 0,
|
||
|
'width': this.w,
|
||
|
'height': this.h,
|
||
|
});
|
||
|
|
||
|
clipPath.add(clipFrame);
|
||
|
|
||
|
// Definitions for clip paths
|
||
|
const defs = new Defs();
|
||
|
defs.add(clipPath);
|
||
|
|
||
|
// Add defs to the parent group
|
||
|
frameGroup.add(defs);
|
||
|
|
||
|
const children = this.children || [];
|
||
|
|
||
|
// Add the children to the clipGroup so they will be clipped
|
||
|
children.forEach((child) => {
|
||
|
overlayAnnotation(clipGroup, child);
|
||
|
});
|
||
|
|
||
|
// Apply clipping to the clipGroup only
|
||
|
clipGroup.clipWith(clipPath);
|
||
|
|
||
|
// Add non-clipped...
|
||
|
frameGroup.add(frame);
|
||
|
frameGroup.add(textElement);
|
||
|
|
||
|
// ...and clipped elements to the frame group
|
||
|
frameGroup.add(clipGroup);
|
||
|
|
||
|
return frameGroup;
|
||
|
}
|
||
|
}
|