fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
348 lines (312 loc) • 9.77 kB
text/typescript
import { Polyline } from './Polyline';
import { Point } from '../Point';
import { describe, expect, it } from 'vitest';
import { Polygon } from './Polygon';
import { FabricObject } from './Object/FabricObject';
import { createReferenceObject, createSVGElement } from '../../test/utils';
const points = [
{ x: 2, y: 2 },
{ x: 12, y: 2 },
{ x: 12, y: 7 },
];
const REFERENCE_OBJECT = createReferenceObject('Polyline', {
left: 15,
top: 17,
width: 10,
height: 10,
points: getPoints(),
});
describe('Polyline', () => {
describe('_calcDimensions and pathOffset', () => {
it('returns dimensions of the polyline regardless of transform or strokeWidth', () => {
const polyline = new Polyline(points, {
scaleX: 2,
scaleY: 2,
angle: 5,
strokeWidth: 4,
});
const dim = polyline._calcDimensions();
expect(dim).toEqual({
left: 2,
top: 2,
width: 10,
height: 5,
pathOffset: new Point(7, 4.5),
strokeDiff: new Point(0, 0),
strokeOffset: new Point(0, 0),
});
});
it('returns dimensions of the polyline regardless of transform or strokeWidth and skew', () => {
const polyline = new Polyline(points, {
scaleX: 2,
scaleY: 2,
angle: 5,
strokeWidth: 4,
skewX: 10,
skewY: 5,
});
const dim = polyline._calcDimensions();
expect(dim).toEqual({
left: 2,
top: 2,
width: 10,
height: 5,
pathOffset: new Point(7, 4.5),
strokeDiff: expect.any(Point),
strokeOffset: expect.any(Point),
});
});
it('returns dimensions of the polyline exactBounds and no strokeWidth', () => {
const polyline = new Polyline(points, {
scaleX: 2,
scaleY: 2,
angle: 5,
strokeWidth: 0,
exactBoundingBox: true,
});
const dim = polyline._calcDimensions();
expect(dim).toEqual({
left: 2,
top: 2,
width: 10,
height: 5,
pathOffset: new Point(7, 4.5),
strokeDiff: new Point(0, 0),
strokeOffset: new Point(0, 0),
});
});
it('returns dimensions of the polyline exactBounds and strokeWidth', () => {
const polyline = new Polyline(points, {
scaleX: 2,
scaleY: 2,
angle: 5,
strokeWidth: 4,
exactBoundingBox: true,
});
const dim = polyline._calcDimensions();
expect(dim).toEqual({
left: 2,
top: 0,
width: 12,
height: 7,
pathOffset: new Point(8, 3.5),
strokeDiff: new Point(4, 4),
strokeOffset: new Point(0, 4),
});
});
it('returns dimensions of the polyline exactBounds and strokeWidth with skew', () => {
const polyline = new Polyline(points, {
scaleX: 2,
scaleY: 2,
angle: 5,
strokeWidth: 4,
skewX: 10,
exactBoundingBox: true,
});
const dim = polyline._calcDimensions();
expect(dim).toEqual({
left: 2,
top: 0,
width: 13.234288864959254,
height: 7,
pathOffset: new Point(8, 3.5),
strokeDiff: new Point(4.70530792283386, 4),
strokeOffset: new Point(0.7053079228338603, 4),
});
});
});
it('should safeguard passing points in options', () => {
expect(new Polyline(points, { points: [{ x: 1, y: 1 }] })).toEqual(
expect.objectContaining({
points: points,
}),
);
});
const REFERENCE_EMPTY_OBJECT = {
points: [],
width: 0,
height: 0,
top: 0,
left: 0,
};
it('initializes constructor correctly', () => {
expect(Polyline).toBeTruthy();
const polyline = new Polyline(getPoints());
expect(polyline).toBeInstanceOf(Polyline);
expect(polyline).toBeInstanceOf(FabricObject);
expect(polyline.constructor).toHaveProperty('type', 'Polyline');
expect(polyline.get('points')).toEqual([
{ x: 10, y: 12 },
{ x: 20, y: 22 },
]);
});
it('positions with strokeWidth top-left and origins top-left', () => {
const polyline = new Polyline(getPoints(), {
strokeWidth: 2,
originX: 'left',
originY: 'top',
});
expect(polyline.left).toBe(9);
expect(polyline.top).toBe(11);
});
it('positions with strokeWidth top-left and origins center-center', () => {
const polyline = new Polyline(getPoints(), {
strokeWidth: 2,
originX: 'center',
originY: 'center',
});
expect(polyline.left).toBe(15);
expect(polyline.top).toBe(17);
});
it('positions with strokeWidth top-left and origins bottom-right', () => {
const polyline = new Polyline(getPoints(), {
strokeWidth: 2,
originX: 'right',
originY: 'bottom',
});
expect(polyline.left).toBe(21);
expect(polyline.top).toBe(23);
});
it('has complexity function', () => {
const polyline = new Polyline(getPoints());
expect(polyline.complexity).toBeTypeOf('function');
});
it('converts to object correctly', () => {
const polyline = new Polyline(getPoints());
expect(polyline.toObject).toBeTypeOf('function');
expect({
...polyline.toObject(),
}).toEqual(REFERENCE_OBJECT);
});
it('generates SVG correctly', () => {
const polyline = new Polygon(getPoints(), { fill: 'red', stroke: 'blue' });
expect(polyline.toSVG).toBeTypeOf('function');
const EXPECTED_SVG =
'<g transform="matrix(1 0 0 1 15 17)" >\n<polygon style="stroke: rgb(0,0,255); stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,0,0); fill-rule: nonzero; opacity: 1;" points="-5,-5 5,5" />\n</g>\n';
expect(polyline.toSVG()).toEqual(EXPECTED_SVG);
});
it('creates from object correctly', async () => {
expect(Polyline.fromObject).toBeTypeOf('function');
const polyline = await Polyline.fromObject(REFERENCE_OBJECT);
expect(polyline).toBeInstanceOf(Polyline);
expect(polyline.toObject()).toEqual(REFERENCE_OBJECT);
});
it('creates from element without points nor strokeWidth', async () => {
expect(Polyline.fromElement).toBeTypeOf('function');
const elPolylineWithoutPoints = createSVGElement('polyline', {
'stroke-width': 0,
});
const polyline = await Polyline.fromElement(elPolylineWithoutPoints);
expect(polyline.toObject()).toEqual({
...REFERENCE_OBJECT,
...REFERENCE_EMPTY_OBJECT,
strokeWidth: 0,
});
});
it('creates from element without points but takes strokeWidth into account', async () => {
const elPolylineWithoutPoints = createSVGElement('polyline', {
'stroke-width': 8,
});
const polyline = await Polyline.fromElement(elPolylineWithoutPoints);
expect(polyline.toObject()).toEqual({
...REFERENCE_OBJECT,
...REFERENCE_EMPTY_OBJECT,
left: 0,
top: 0,
strokeWidth: 8,
});
});
it('creates from element with empty points', async () => {
const elPolylineWithEmptyPoints = createSVGElement('polyline', {
points: '',
});
const polyline = await Polyline.fromElement(elPolylineWithEmptyPoints);
expect(polyline.toObject()).toEqual({
...REFERENCE_OBJECT,
...REFERENCE_EMPTY_OBJECT,
left: 0,
top: 0,
});
});
it('creates from element with points and strokeWidth', async () => {
const elPolyline = createSVGElement('polyline', {
points: '10,12 20,22',
'stroke-width': 1,
});
const polyline = await Polyline.fromElement(elPolyline);
expect(polyline.toObject()).toEqual({
...REFERENCE_OBJECT,
left: 15,
top: 17,
});
});
it('creates from element without strokeWidth with top-left on top-left point', async () => {
const elPolyline = createSVGElement('polyline', {
points: '10,12 20,22',
'stroke-width': 0,
});
const polyline = await Polyline.fromElement(elPolyline);
expect(polyline).toBeInstanceOf(Polyline);
expect(polyline.toObject()).toEqual({
...REFERENCE_OBJECT,
strokeWidth: 0,
left: 15,
top: 17,
});
});
it('creates from element with strokeWidth and adjusts space for stroke', async () => {
const elPolyline = createSVGElement('polyline', {
points: '10,12 20,22',
});
const polyline = await Polyline.fromElement(elPolyline);
expect(polyline).toBeInstanceOf(Polyline);
expect(polyline.toObject()).toEqual({
...REFERENCE_OBJECT,
left: 15,
top: 17,
});
});
it('creates from element with custom attributes', async () => {
const elPolylineWithAttrs = createSVGElement('polyline', {
points: '10,10 20,20 30,30 10,10',
fill: 'rgb(255,255,255)',
opacity: 0.34,
'stroke-width': 3,
stroke: 'blue',
transform: 'translate(-10,-20) scale(2)',
'stroke-dasharray': '5, 2',
'stroke-linecap': 'round',
'stroke-linejoin': 'bevel',
'stroke-miterlimit': 5,
});
const polylineWithAttrs = await Polyline.fromElement(elPolylineWithAttrs);
const expectedPoints = [
{ x: 10, y: 10 },
{ x: 20, y: 20 },
{ x: 30, y: 30 },
{ x: 10, y: 10 },
];
expect(polylineWithAttrs.toObject()).toEqual({
...REFERENCE_OBJECT,
width: 20,
height: 20,
fill: 'rgb(255,255,255)',
stroke: 'blue',
strokeWidth: 3,
strokeDashArray: [5, 2],
strokeLineCap: 'round',
strokeLineJoin: 'bevel',
strokeMiterLimit: 5,
opacity: 0.34,
points: expectedPoints,
left: 20,
top: 20,
});
});
});
function getPoints() {
return [
{ x: 10, y: 12 },
{ x: 20, y: 22 },
];
}