UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

503 lines (469 loc) 17.6 kB
import { FabricObject } from '../shapes/Object/FabricObject'; import { Point } from '../Point'; import { Canvas } from './Canvas'; import { Group } from '../shapes/Group'; import { ActiveSelection } from '../shapes/ActiveSelection'; describe('Selectable Canvas', () => { describe('_pointIsInObjectSelectionArea', () => { it('points and selection area', () => { const object = new FabricObject({ left: 40, top: 40, width: 40, height: 50, angle: 0, padding: 0, }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(object); // point1 is outside object because object starts at 40,40 const point1 = new Point(30, 30); expect(canvas._pointIsInObjectSelectionArea(object, point1)).toBe(false); // point2 is contained in object const point2 = new Point(40, 40); expect(canvas._pointIsInObjectSelectionArea(object, point2)).toBe(true); // point3 is contained of object const point3 = new Point(65, 50); expect(canvas._pointIsInObjectSelectionArea(object, point3)).toBe(true); // point4 is outside of object (bottom) const point4 = new Point(45, 95); expect(canvas._pointIsInObjectSelectionArea(object, point4)).toBe(false); // point5 is outside of object (top) const point5 = new Point(50, 30); expect(canvas._pointIsInObjectSelectionArea(object, point5)).toBe(false); // point6 is outside of object (right) const point6 = new Point(90, 45); expect(canvas._pointIsInObjectSelectionArea(object, point6)).toBe(false); }); it('points and selection area, with rotation', () => { const object = new FabricObject({ left: 25, top: 20, width: 40, height: 50, angle: 90, padding: 0, originX: 'center', originY: 'center', }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(object); // point1 is contained in object top left const point1 = new Point(0, 0); expect(canvas._pointIsInObjectSelectionArea(object, point1)).toBe(true); // point2 is contained in object bottom right const point2 = new Point(50, 40); expect(canvas._pointIsInObjectSelectionArea(object, point2)).toBe(true); // point3 is outside of object (bottom) because object is rotate const point3 = new Point(20, 41); expect(canvas._pointIsInObjectSelectionArea(object, point3)).toBe(false); // point4 is outside of object (right) const point4 = new Point(51, 25); expect(canvas._pointIsInObjectSelectionArea(object, point4)).toBe(false); // point5 is outside of object (top left) const point5 = new Point(-1, -1); expect(canvas._pointIsInObjectSelectionArea(object, point5)).toBe(false); }); it('points and selection area, with rotation and scaling', () => { const object = new FabricObject({ left: 50, top: 40, width: 40, height: 50, angle: 90, padding: 0, scaleX: 2, scaleY: 2, originX: 'center', originY: 'center', }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(object); // point1 is contained in object top left const point1 = new Point(0, 0); expect(canvas._pointIsInObjectSelectionArea(object, point1)).toBe(true); // point2 is contained in object bottom right const point2 = new Point(100, 80); expect(canvas._pointIsInObjectSelectionArea(object, point2)).toBe(true); // point3 is outside of object (bottom) because object is rotate const point3 = new Point(40, 82); expect(canvas._pointIsInObjectSelectionArea(object, point3)).toBe(false); // point4 is outside of object (right) const point4 = new Point(102, 50); expect(canvas._pointIsInObjectSelectionArea(object, point4)).toBe(false); // point5 is outside of object (top left) const point5 = new Point(-2, -2); expect(canvas._pointIsInObjectSelectionArea(object, point5)).toBe(false); }); it('points and selection area, with rotation and scaling and the strokeWidth', () => { const object = new FabricObject({ left: 50, top: 40, width: 40, height: 50, angle: 90, padding: 0, scaleX: 2, scaleY: 2, strokeWidth: 2, originX: 'center', originY: 'center', }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(object); // point1 is contained in object top left const point1 = new Point(-2, -2); expect(canvas._pointIsInObjectSelectionArea(object, point1)).toBe(true); // point2 is contained in object bottom right const point2 = new Point(102, 82); expect(canvas._pointIsInObjectSelectionArea(object, point2)).toBe(true); // point3 is outside of object (bottom) because object is rotate const point3 = new Point(40, 83); expect(canvas._pointIsInObjectSelectionArea(object, point3)).toBe(false); // point4 is outside of object (right) const point4 = new Point(103, 50); expect(canvas._pointIsInObjectSelectionArea(object, point4)).toBe(false); // point5 is outside of object (top left) const point5 = new Point(-3, -3); expect(canvas._pointIsInObjectSelectionArea(object, point5)).toBe(false); }); it('points and selection area, with rotation and scaling and the strokeWidth and strokeUniform', () => { const object = new FabricObject({ left: 50, top: 40, width: 40, height: 50, angle: 90, padding: 0, scaleX: 2, scaleY: 2, strokeWidth: 2, strokeUniform: true, originX: 'center', originY: 'center', }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(object); // point1 is contained in object top left const point1 = new Point(-1, -1); expect(canvas._pointIsInObjectSelectionArea(object, point1)).toBe(true); // point2 is contained in object bottom right const point2 = new Point(101, 81); expect(canvas._pointIsInObjectSelectionArea(object, point2)).toBe(true); // point3 is outside of object (bottom) because object is rotate const point3 = new Point(40, 82); expect(canvas._pointIsInObjectSelectionArea(object, point3)).toBe(false); // point4 is outside of object (right) const point4 = new Point(102, 50); expect(canvas._pointIsInObjectSelectionArea(object, point4)).toBe(false); // point5 is outside of object (top left) const point5 = new Point(-2, -2); expect(canvas._pointIsInObjectSelectionArea(object, point5)).toBe(false); }); it('points and selection area, with rotation and scaling and the strokeWidth and strokeUniform and padding', () => { const object = new FabricObject({ left: 50, top: 40, width: 40, height: 50, angle: 90, padding: 5, scaleX: 2, scaleY: 2, strokeWidth: 2, strokeUniform: true, originX: 'center', originY: 'center', }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(object); // point1 is contained in object top left const point1 = new Point(-6, -6); expect(canvas._pointIsInObjectSelectionArea(object, point1)).toBe(true); // point2 is contained in object bottom right padding area const point2 = new Point(106, 86); expect(canvas._pointIsInObjectSelectionArea(object, point2)).toBe(true); // point3 is outside of object (bottom) because object is rotate const point3 = new Point(40, 88); expect(canvas._pointIsInObjectSelectionArea(object, point3)).toBe(false); // point4 is outside of object (right) const point4 = new Point(108, 50); expect(canvas._pointIsInObjectSelectionArea(object, point4)).toBe(false); // point5 is outside of object (top left) const point5 = new Point(-7, -7); expect(canvas._pointIsInObjectSelectionArea(object, point5)).toBe(false); }); it('points and selection area, with rotation and scaling and the strokeWidth and padding', () => { const object = new FabricObject({ left: 50, top: 40, width: 40, height: 50, angle: 90, padding: 5, scaleX: 2, scaleY: 2, strokeWidth: 2, originX: 'center', originY: 'center', }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(object); // point1 is contained in object top left const point1 = new Point(-7, -7); expect(canvas._pointIsInObjectSelectionArea(object, point1)).toBe(true); // point2 is contained in object bottom right padding area const point2 = new Point(107, 87); expect(canvas._pointIsInObjectSelectionArea(object, point2)).toBe(true); // point3 is outside of object (bottom) because object is rotate const point3 = new Point(40, 88); expect(canvas._pointIsInObjectSelectionArea(object, point3)).toBe(false); // point4 is outside of object (right) const point4 = new Point(107, 50); expect(canvas._pointIsInObjectSelectionArea(object, point4)).toBe(false); // point5 is outside of object (top left) const point5 = new Point(-8, -8); expect(canvas._pointIsInObjectSelectionArea(object, point5)).toBe(false); }); it('points and selection area, with the strokeWidth and padding', () => { const object = new FabricObject({ left: 20, top: 25, width: 40, height: 50, padding: 5, strokeWidth: 2, originX: 'center', originY: 'center', }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(object); // point1 is contained in object top left const point1 = new Point(-6, -6); expect(canvas._pointIsInObjectSelectionArea(object, point1)).toBe(true); // point2 is contained in object bottom right padding area const point2 = new Point(46, 56); expect(canvas._pointIsInObjectSelectionArea(object, point2)).toBe(true); // point3 is outside of object (bottom) because object is rotate const point3 = new Point(20, 57); expect(canvas._pointIsInObjectSelectionArea(object, point3)).toBe(false); // point4 is outside of object (right) const point4 = new Point(47, 20); expect(canvas._pointIsInObjectSelectionArea(object, point4)).toBe(false); // point5 is outside of object (top left) const point5 = new Point(-7, -7); expect(canvas._pointIsInObjectSelectionArea(object, point5)).toBe(false); }); it('points and selection area, group transformation and padding', () => { const object = new FabricObject({ left: 5, top: 5, width: 10, height: 10, padding: 5, strokeWidth: 0, }); const object2 = new FabricObject({ left: 35, top: 35, width: 10, height: 10, padding: 5, strokeWidth: 0, }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); const group = new Group([object, object2], { scaleX: 2, scaleY: 2 }); canvas.add(group); for (let y = -1; y <= 31; y++) { for (let x = -1; x <= 31; x++) { expect( canvas['_pointIsInObjectSelectionArea'](object, new Point(x, y)), ).toBe(x >= 0 && x <= 30 && y >= 0 && y <= 30); } } }); }); describe('searchPossibleTargets', () => { test('the target returned will stop at the first non interactive container', () => { const object = new FabricObject({ id: 'a', left: 0, top: 0, width: 10, height: 10, padding: 0, strokeWidth: 0, }); const groupB = new Group([object], { id: 'b', interactive: true, subTargetCheck: true, }); const groupC = new Group([groupB], { id: 'c', interactive: false, subTargetCheck: true, }); const groupD = new Group([groupC], { id: 'd', interactive: true, subTargetCheck: true, }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(groupD); const target = canvas.searchPossibleTargets( canvas.getObjects(), groupD.getCenterPoint(), ); expect(target).toBe(groupC); expect(canvas.targets.map((obj) => obj.id)).toEqual(['a', 'b', 'c']); }); test('a interactive group covered by a non interactive group wont be selected', () => { const object = new FabricObject({ id: 'a', left: 0, top: 0, width: 10, height: 10, padding: 0, strokeWidth: 0, }); const groupB = new Group([object], { id: 'b', interactive: true, subTargetCheck: true, }); const groupC = new Group([groupB], { id: 'c', interactive: false, subTargetCheck: true, }); const groupD = new Group([groupC], { id: 'd', interactive: true, subTargetCheck: true, }); const groupE = new Group([groupD], { id: 'e', interactive: true, subTargetCheck: true, }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); canvas.add(groupE); const target = canvas.searchPossibleTargets( canvas.getObjects(), groupD.getCenterPoint(), ); expect(target).toBe(groupC); expect(canvas.targets.map((obj) => obj.id)).toEqual(['a', 'b', 'c', 'd']); }); test('nested non interactive groups with subTargetCheck', () => { const object = new FabricObject({ left: 0, top: 0, width: 10, height: 10, padding: 0, strokeWidth: 0, }); const object2 = new FabricObject({ left: 20, top: 0, width: 10, height: 10, padding: 0, strokeWidth: 0, }); const object3 = new FabricObject({ left: 40, top: 0, width: 10, height: 10, padding: 0, strokeWidth: 0, }); const nestedGroup = new Group([object2, object3], { interactive: false, subTargetCheck: true, }); const canvas = new Canvas(undefined, { renderOnAddRemove: false }); const group = new Group([object, nestedGroup], { interactive: true, subTargetCheck: true, }); canvas.add(group); const object2Position = object2.getCenterPoint(); const target = canvas.searchPossibleTargets( canvas.getObjects(), object2Position, ); expect(target).toBe(nestedGroup); nestedGroup.set({ interactive: true }); const nestedTarget = canvas.searchPossibleTargets( canvas.getObjects(), object2Position, ); expect(nestedTarget).toBe(object2); }); }); describe('setupCurrentTransform', () => { test.each( ['tl', 'mt', 'tr', 'mr', 'br', 'mb', 'bl', 'ml', 'mtr'] .map((controlKey) => [ { controlKey, zoom: false }, { controlKey, zoom: true }, ]) .flat(), )('should fire before:transform event %p', ({ controlKey, zoom }) => { const canvas = new Canvas(); const canvasOffset = canvas.calcOffset(); const object = new FabricObject({ left: 50, top: 50, width: 50, height: 50, }); canvas.add(object); canvas.setActiveObject(object); zoom && canvas.zoomToPoint(new Point(25, 25), 2); expect(canvas._currentTransform).toBeFalsy(); const spy = jest.fn(); canvas.on('before:transform', spy); const setupCurrentTransformSpy = jest.spyOn( canvas, '_setupCurrentTransform', ); const { corner: { tl, tr, bl }, } = object.oCoords[controlKey]; canvas.getSelectionElement().dispatchEvent( new MouseEvent('mousedown', { clientX: canvasOffset.left + (tl.x + tr.x) / 2, clientY: canvasOffset.top + (tl.y + bl.y) / 2, which: 1, }), ); expect(setupCurrentTransformSpy).toHaveBeenCalledTimes(1); expect(canvas._currentTransform).toBeDefined(); expect(canvas._currentTransform).toHaveProperty('target', object); expect(canvas._currentTransform).toHaveProperty('corner', controlKey); expect(spy).toHaveBeenCalledTimes(1); }); }); describe('_discardActiveObject', () => { test('will clear hovered target if the target is the active selection', () => { const objects = [new FabricObject(), new FabricObject()]; const canvas = new Canvas(); canvas.add(...objects); const as = new ActiveSelection(objects); as.canvas = canvas; canvas._hoveredTarget = as; canvas.setActiveObject(as); expect(canvas._activeObject).toBe(as); expect(canvas._hoveredTarget).toBe(as); canvas.discardActiveObject(); expect(canvas._activeObject).toBe(undefined); expect(canvas._hoveredTarget).toBe(undefined); }); }); });