fabric 7.0.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +1 -0
- package/CHANGELOG.md +21 -0
- package/dist/extensions/cropping_controls/croppingControls.d.ts +16 -0
- package/dist/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
- package/dist/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
- package/dist/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
- package/dist/extensions/cropping_controls/enterCropMode.d.ts +7 -0
- package/dist/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
- package/dist/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
- package/dist/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
- package/dist/extensions/index.d.ts +3 -0
- package/dist/extensions/index.d.ts.map +1 -1
- package/dist/fabric.d.ts +1 -0
- package/dist/fabric.d.ts.map +1 -1
- package/dist/index.js +628 -537
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/index.min.mjs +1 -1
- package/dist/index.min.mjs.map +1 -1
- package/dist/index.mjs +628 -537
- package/dist/index.mjs.map +1 -1
- package/dist/index.node.cjs +628 -537
- package/dist/index.node.cjs.map +1 -1
- package/dist/index.node.mjs +628 -537
- package/dist/index.node.mjs.map +1 -1
- package/dist/package.json.min.mjs +1 -1
- package/dist/package.json.mjs +1 -1
- package/dist/src/EventTypeDefs.d.ts +5 -0
- package/dist/src/EventTypeDefs.d.ts.map +1 -1
- package/dist/src/Pattern/Pattern.d.ts.map +1 -1
- package/dist/src/Pattern/Pattern.min.mjs +1 -1
- package/dist/src/Pattern/Pattern.min.mjs.map +1 -1
- package/dist/src/Pattern/Pattern.mjs +2 -1
- package/dist/src/Pattern/Pattern.mjs.map +1 -1
- package/dist/src/Shadow.d.ts +1 -1
- package/dist/src/Shadow.d.ts.map +1 -1
- package/dist/src/Shadow.min.mjs +1 -1
- package/dist/src/Shadow.min.mjs.map +1 -1
- package/dist/src/Shadow.mjs +5 -4
- package/dist/src/Shadow.mjs.map +1 -1
- package/dist/src/canvas/CanvasOptions.d.ts.map +1 -1
- package/dist/src/canvas/CanvasOptions.min.mjs.map +1 -1
- package/dist/src/canvas/CanvasOptions.mjs.map +1 -1
- package/dist/src/canvas/SelectableCanvas.d.ts +2 -0
- package/dist/src/canvas/SelectableCanvas.d.ts.map +1 -1
- package/dist/src/canvas/SelectableCanvas.min.mjs +1 -1
- package/dist/src/canvas/SelectableCanvas.min.mjs.map +1 -1
- package/dist/src/canvas/SelectableCanvas.mjs +33 -13
- package/dist/src/canvas/SelectableCanvas.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvas.d.ts.map +1 -1
- package/dist/src/canvas/StaticCanvas.min.mjs +1 -1
- package/dist/src/canvas/StaticCanvas.min.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvas.mjs +3 -1
- package/dist/src/canvas/StaticCanvas.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
- package/dist/src/canvas/StaticCanvasOptions.min.mjs.map +1 -1
- package/dist/src/canvas/StaticCanvasOptions.mjs.map +1 -1
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.min.mjs.map +1 -1
- package/dist/src/constants.mjs.map +1 -1
- package/dist/src/controls/Control.d.ts +22 -1
- package/dist/src/controls/Control.d.ts.map +1 -1
- package/dist/src/controls/Control.min.mjs +1 -1
- package/dist/src/controls/Control.min.mjs.map +1 -1
- package/dist/src/controls/Control.mjs +45 -1
- package/dist/src/controls/Control.mjs.map +1 -1
- package/dist/src/controls/changeWidth.d.ts +22 -0
- package/dist/src/controls/changeWidth.d.ts.map +1 -1
- package/dist/src/controls/changeWidth.min.mjs +1 -1
- package/dist/src/controls/changeWidth.min.mjs.map +1 -1
- package/dist/src/controls/changeWidth.mjs +46 -18
- package/dist/src/controls/changeWidth.mjs.map +1 -1
- package/dist/src/controls/controlRendering.d.ts.map +1 -1
- package/dist/src/controls/controlRendering.min.mjs +1 -1
- package/dist/src/controls/controlRendering.min.mjs.map +1 -1
- package/dist/src/controls/controlRendering.mjs +18 -34
- package/dist/src/controls/controlRendering.mjs.map +1 -1
- package/dist/src/controls/index.d.ts +2 -1
- package/dist/src/controls/index.d.ts.map +1 -1
- package/dist/src/controls/index.min.mjs +1 -1
- package/dist/src/controls/index.mjs +1 -1
- package/dist/src/gradient/Gradient.d.ts.map +1 -1
- package/dist/src/gradient/Gradient.min.mjs +1 -1
- package/dist/src/gradient/Gradient.min.mjs.map +1 -1
- package/dist/src/gradient/Gradient.mjs +19 -6
- package/dist/src/gradient/Gradient.mjs.map +1 -1
- package/dist/src/shapes/Circle.d.ts.map +1 -1
- package/dist/src/shapes/Circle.min.mjs +1 -1
- package/dist/src/shapes/Circle.min.mjs.map +1 -1
- package/dist/src/shapes/Circle.mjs +10 -7
- package/dist/src/shapes/Circle.mjs.map +1 -1
- package/dist/src/shapes/Ellipse.d.ts.map +1 -1
- package/dist/src/shapes/Ellipse.min.mjs +1 -1
- package/dist/src/shapes/Ellipse.min.mjs.map +1 -1
- package/dist/src/shapes/Ellipse.mjs +2 -1
- package/dist/src/shapes/Ellipse.mjs.map +1 -1
- package/dist/src/shapes/Group.d.ts.map +1 -1
- package/dist/src/shapes/Group.min.mjs +1 -1
- package/dist/src/shapes/Group.min.mjs.map +1 -1
- package/dist/src/shapes/Group.mjs +2 -1
- package/dist/src/shapes/Group.mjs.map +1 -1
- package/dist/src/shapes/IText/IText.d.ts.map +1 -1
- package/dist/src/shapes/IText/IText.min.mjs.map +1 -1
- package/dist/src/shapes/IText/IText.mjs.map +1 -1
- package/dist/src/shapes/Image.d.ts +1 -1
- package/dist/src/shapes/Image.d.ts.map +1 -1
- package/dist/src/shapes/Image.min.mjs +1 -1
- package/dist/src/shapes/Image.min.mjs.map +1 -1
- package/dist/src/shapes/Image.mjs +4 -3
- package/dist/src/shapes/Image.mjs.map +1 -1
- package/dist/src/shapes/Line.d.ts.map +1 -1
- package/dist/src/shapes/Line.min.mjs +1 -1
- package/dist/src/shapes/Line.min.mjs.map +1 -1
- package/dist/src/shapes/Line.mjs +6 -10
- package/dist/src/shapes/Line.mjs.map +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.min.mjs.map +1 -1
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs +5 -4
- package/dist/src/shapes/Object/FabricObjectSVGExportMixin.mjs.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.min.mjs.map +1 -1
- package/dist/src/shapes/Object/InteractiveObject.mjs.map +1 -1
- package/dist/src/shapes/Object/Object.d.ts.map +1 -1
- package/dist/src/shapes/Object/Object.min.mjs +1 -1
- package/dist/src/shapes/Object/Object.min.mjs.map +1 -1
- package/dist/src/shapes/Object/Object.mjs +3 -0
- package/dist/src/shapes/Object/Object.mjs.map +1 -1
- package/dist/src/shapes/Object/ObjectGeometry.min.mjs +1 -1
- package/dist/src/shapes/Object/ObjectGeometry.min.mjs.map +1 -1
- package/dist/src/shapes/Object/ObjectGeometry.mjs +1 -1
- package/dist/src/shapes/Object/ObjectGeometry.mjs.map +1 -1
- package/dist/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
- package/dist/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
- package/dist/src/shapes/Path.d.ts.map +1 -1
- package/dist/src/shapes/Path.min.mjs.map +1 -1
- package/dist/src/shapes/Path.mjs +1 -2
- package/dist/src/shapes/Path.mjs.map +1 -1
- package/dist/src/shapes/Polyline.d.ts.map +1 -1
- package/dist/src/shapes/Polyline.min.mjs +1 -1
- package/dist/src/shapes/Polyline.min.mjs.map +1 -1
- package/dist/src/shapes/Polyline.mjs +10 -6
- package/dist/src/shapes/Polyline.mjs.map +1 -1
- package/dist/src/shapes/Rect.d.ts.map +1 -1
- package/dist/src/shapes/Rect.min.mjs +1 -1
- package/dist/src/shapes/Rect.min.mjs.map +1 -1
- package/dist/src/shapes/Rect.mjs +2 -1
- package/dist/src/shapes/Rect.mjs.map +1 -1
- package/dist/src/shapes/Text/StyledText.d.ts.map +1 -1
- package/dist/src/shapes/Text/StyledText.min.mjs.map +1 -1
- package/dist/src/shapes/Text/StyledText.mjs +0 -3
- package/dist/src/shapes/Text/StyledText.mjs.map +1 -1
- package/dist/src/shapes/Text/Text.d.ts.map +1 -1
- package/dist/src/shapes/Text/Text.min.mjs.map +1 -1
- package/dist/src/shapes/Text/Text.mjs.map +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.min.mjs.map +1 -1
- package/dist/src/shapes/Text/TextSVGExportMixin.mjs +5 -6
- package/dist/src/shapes/Text/TextSVGExportMixin.mjs.map +1 -1
- package/dist/src/shapes/Textbox.d.ts.map +1 -1
- package/dist/src/shapes/Textbox.min.mjs.map +1 -1
- package/dist/src/shapes/Textbox.mjs.map +1 -1
- package/dist/src/shapes/Triangle.d.ts.map +1 -1
- package/dist/src/shapes/Triangle.min.mjs.map +1 -1
- package/dist/src/shapes/Triangle.mjs.map +1 -1
- package/dist/src/util/lang_string.d.ts +1 -1
- package/dist/src/util/lang_string.d.ts.map +1 -1
- package/dist/src/util/lang_string.min.mjs +1 -1
- package/dist/src/util/lang_string.min.mjs.map +1 -1
- package/dist/src/util/lang_string.mjs +1 -1
- package/dist/src/util/lang_string.mjs.map +1 -1
- package/dist/src/util/misc/svgParsing.d.ts.map +1 -1
- package/dist/src/util/misc/svgParsing.min.mjs +1 -1
- package/dist/src/util/misc/svgParsing.min.mjs.map +1 -1
- package/dist/src/util/misc/svgParsing.mjs +2 -1
- package/dist/src/util/misc/svgParsing.mjs.map +1 -1
- package/dist-extensions/cropping_controls/croppingControls.mjs +140 -0
- package/dist-extensions/cropping_controls/croppingControls.mjs.map +1 -0
- package/dist-extensions/cropping_controls/croppingHandlers.mjs +228 -0
- package/dist-extensions/cropping_controls/croppingHandlers.mjs.map +1 -0
- package/dist-extensions/cropping_controls/enterCropMode.mjs +38 -0
- package/dist-extensions/cropping_controls/enterCropMode.mjs.map +1 -0
- package/dist-extensions/cropping_controls/renderCornerControl.mjs +45 -0
- package/dist-extensions/cropping_controls/renderCornerControl.mjs.map +1 -0
- package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts +16 -0
- package/dist-extensions/extensions/cropping_controls/croppingControls.d.ts.map +1 -0
- package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts +39 -0
- package/dist-extensions/extensions/cropping_controls/croppingHandlers.d.ts.map +1 -0
- package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts +7 -0
- package/dist-extensions/extensions/cropping_controls/enterCropMode.d.ts.map +1 -0
- package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts +14 -0
- package/dist-extensions/extensions/cropping_controls/renderCornerControl.d.ts.map +1 -0
- package/dist-extensions/extensions/index.d.ts +3 -0
- package/dist-extensions/extensions/index.d.ts.map +1 -1
- package/dist-extensions/fabric-extensions.min.js +1 -1
- package/dist-extensions/fabric-extensions.min.js.map +1 -1
- package/dist-extensions/fabric.d.ts +1 -0
- package/dist-extensions/fabric.d.ts.map +1 -1
- package/dist-extensions/index.mjs +3 -0
- package/dist-extensions/index.mjs.map +1 -1
- package/dist-extensions/src/EventTypeDefs.d.ts +5 -0
- package/dist-extensions/src/EventTypeDefs.d.ts.map +1 -1
- package/dist-extensions/src/Pattern/Pattern.d.ts.map +1 -1
- package/dist-extensions/src/Shadow.d.ts +1 -1
- package/dist-extensions/src/Shadow.d.ts.map +1 -1
- package/dist-extensions/src/canvas/CanvasOptions.d.ts.map +1 -1
- package/dist-extensions/src/canvas/SelectableCanvas.d.ts +2 -0
- package/dist-extensions/src/canvas/SelectableCanvas.d.ts.map +1 -1
- package/dist-extensions/src/canvas/StaticCanvas.d.ts.map +1 -1
- package/dist-extensions/src/canvas/StaticCanvasOptions.d.ts.map +1 -1
- package/dist-extensions/src/constants.d.ts +1 -0
- package/dist-extensions/src/constants.d.ts.map +1 -1
- package/dist-extensions/src/controls/Control.d.ts +22 -1
- package/dist-extensions/src/controls/Control.d.ts.map +1 -1
- package/dist-extensions/src/controls/changeWidth.d.ts +22 -0
- package/dist-extensions/src/controls/changeWidth.d.ts.map +1 -1
- package/dist-extensions/src/controls/controlRendering.d.ts.map +1 -1
- package/dist-extensions/src/controls/index.d.ts +2 -1
- package/dist-extensions/src/controls/index.d.ts.map +1 -1
- package/dist-extensions/src/gradient/Gradient.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Circle.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Ellipse.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Group.d.ts.map +1 -1
- package/dist-extensions/src/shapes/IText/IText.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Image.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Line.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/FabricObjectSVGExportMixin.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/InteractiveObject.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/Object.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/types/FabricObjectProps.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Object/types/ObjectProps.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Path.d.ts +1 -1
- package/dist-extensions/src/shapes/Path.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Polyline.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Rect.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Text/StyledText.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Text/Text.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Text/TextSVGExportMixin.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Textbox.d.ts.map +1 -1
- package/dist-extensions/src/shapes/Triangle.d.ts.map +1 -1
- package/dist-extensions/src/util/lang_string.d.ts +1 -1
- package/dist-extensions/src/util/lang_string.d.ts.map +1 -1
- package/dist-extensions/src/util/misc/svgParsing.d.ts.map +1 -1
- package/eslint.config.mjs +2 -0
- package/extensions/cropping_controls/croppingControls.spec.ts +115 -0
- package/extensions/cropping_controls/croppingControls.ts +150 -0
- package/extensions/cropping_controls/croppingHandlers.spec.ts +579 -0
- package/extensions/cropping_controls/croppingHandlers.ts +285 -0
- package/extensions/cropping_controls/enterCropMode.ts +30 -0
- package/extensions/cropping_controls/renderCornerControl.ts +53 -0
- package/extensions/index.ts +9 -0
- package/fabric.ts +1 -0
- package/package.json +17 -8
- package/src/ClassRegistry.spec.ts +18 -19
- package/src/EventTypeDefs.ts +15 -11
- package/src/Pattern/Pattern.spec.ts +12 -0
- package/src/Pattern/Pattern.ts +3 -2
- package/src/Shadow.ts +9 -8
- package/src/brushes/PencilBrush.spec.ts +11 -11
- package/src/canvas/Canvas-dispose.spec.ts +8 -7
- package/src/canvas/Canvas.spec.ts +27 -29
- package/src/canvas/CanvasOptions.ts +2 -1
- package/src/canvas/SelectableCanvas.ts +38 -15
- package/src/canvas/StaticCanvas.spec.ts +20 -0
- package/src/canvas/StaticCanvas.ts +7 -4
- package/src/canvas/StaticCanvasOptions.ts +1 -3
- package/src/constants.ts +1 -0
- package/src/controls/Control.spec.ts +102 -0
- package/src/controls/Control.ts +71 -2
- package/src/controls/changeHeight.spec.ts +147 -0
- package/src/controls/changeWidth.ts +68 -35
- package/src/controls/controlRendering.ts +20 -48
- package/src/controls/index.ts +7 -1
- package/src/gradient/Gradient.spec.ts +101 -46
- package/src/gradient/Gradient.ts +27 -14
- package/src/shapes/Circle.spec.ts +10 -39
- package/src/shapes/Circle.ts +11 -11
- package/src/shapes/Ellipse.spec.ts +8 -37
- package/src/shapes/Ellipse.ts +7 -7
- package/src/shapes/Group.ts +3 -3
- package/src/shapes/IText/IText-click-behavior.spec.ts +36 -49
- package/src/shapes/IText/IText.ts +5 -6
- package/src/shapes/IText/ITextKeyBehavior.test.ts +0 -1
- package/src/shapes/IText/__snapshots__/ITextBehavior.test.ts.snap +6 -6
- package/src/shapes/Image.spec.ts +17 -33
- package/src/shapes/Image.ts +15 -11
- package/src/shapes/Line.spec.ts +4 -30
- package/src/shapes/Line.ts +11 -16
- package/src/shapes/Object/FabricObjectSVGExportMixin.ts +11 -4
- package/src/shapes/Object/InteractiveObject.ts +4 -4
- package/src/shapes/Object/Object.ts +6 -5
- package/src/shapes/Object/ObjectGeometry.spec.ts +15 -0
- package/src/shapes/Object/ObjectGeometry.ts +1 -1
- package/src/shapes/Object/objectSvgExport.spec.ts +112 -0
- package/src/shapes/Object/types/FabricObjectProps.ts +1 -4
- package/src/shapes/Object/types/ObjectProps.ts +1 -3
- package/src/shapes/Path.spec.ts +4 -27
- package/src/shapes/Path.ts +2 -4
- package/src/shapes/Polygon.spec.ts +4 -31
- package/src/shapes/Polyline.spec.ts +4 -31
- package/src/shapes/Polyline.ts +11 -12
- package/src/shapes/Rect.spec.ts +25 -33
- package/src/shapes/Rect.ts +7 -7
- package/src/shapes/Text/StyledText.ts +0 -3
- package/src/shapes/Text/Text.spec.ts +3 -32
- package/src/shapes/Text/Text.ts +5 -6
- package/src/shapes/Text/TextSVGExportMixin.spec.ts +9 -0
- package/src/shapes/Text/TextSVGExportMixin.ts +14 -16
- package/src/shapes/Text/__snapshots__/Text.spec.ts.snap +1 -1
- package/src/shapes/Text/__snapshots__/TextSVGExportMixin.spec.ts.snap +1 -1
- package/src/shapes/Textbox.spec.ts +5 -5
- package/src/shapes/Textbox.ts +6 -5
- package/src/shapes/Triangle.ts +4 -4
- package/src/shapes/__snapshots__/Image.spec.ts.snap +4 -4
- package/src/shapes/__snapshots__/Textbox.spec.ts.snap +5 -5
- package/src/util/lang_string.ts +3 -2
- package/src/util/misc/svgParsing.ts +2 -1
- package/tsconfig.spec.json +1 -0
- package/vitest.config.ts +12 -2
- package/vitest.extend.ts +6 -2
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
import type { Transform } from 'fabric';
|
|
2
|
+
import { FabricImage, Canvas, Control, Point } from 'fabric';
|
|
3
|
+
import { createImageCroppingControls } from './croppingControls';
|
|
4
|
+
import {
|
|
5
|
+
changeImageWidth,
|
|
6
|
+
changeImageHeight,
|
|
7
|
+
changeImageCropX,
|
|
8
|
+
changeImageCropY,
|
|
9
|
+
cropPanMoveHandler,
|
|
10
|
+
ghostScalePositionHandler,
|
|
11
|
+
scaleEquallyCropGenerator,
|
|
12
|
+
renderGhostImage,
|
|
13
|
+
} from './croppingHandlers';
|
|
14
|
+
|
|
15
|
+
import { describe, expect, test, beforeEach, afterEach, vi } from 'vitest';
|
|
16
|
+
|
|
17
|
+
describe('croppingHandlers', () => {
|
|
18
|
+
let canvas: Canvas;
|
|
19
|
+
let image: FabricImage;
|
|
20
|
+
let transform: Transform;
|
|
21
|
+
let eventData: any;
|
|
22
|
+
|
|
23
|
+
function prepareTransform(target: FabricImage, corner: string): Transform {
|
|
24
|
+
const origin = canvas._getOriginFromCorner(target, corner);
|
|
25
|
+
return {
|
|
26
|
+
target,
|
|
27
|
+
corner,
|
|
28
|
+
originX: origin.x,
|
|
29
|
+
originY: origin.y,
|
|
30
|
+
} as unknown as Transform;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createMockImage(
|
|
34
|
+
options: Partial<{
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
cropX: number;
|
|
38
|
+
cropY: number;
|
|
39
|
+
elementWidth: number;
|
|
40
|
+
elementHeight: number;
|
|
41
|
+
}> = {},
|
|
42
|
+
): FabricImage {
|
|
43
|
+
const {
|
|
44
|
+
width = 100,
|
|
45
|
+
height = 100,
|
|
46
|
+
cropX = 0,
|
|
47
|
+
cropY = 0,
|
|
48
|
+
elementWidth = 200,
|
|
49
|
+
elementHeight = 200,
|
|
50
|
+
} = options;
|
|
51
|
+
|
|
52
|
+
const imgElement = new Image(elementWidth, elementHeight);
|
|
53
|
+
const img = new FabricImage(imgElement, {
|
|
54
|
+
left: 50,
|
|
55
|
+
top: 50,
|
|
56
|
+
width,
|
|
57
|
+
height,
|
|
58
|
+
cropX,
|
|
59
|
+
cropY,
|
|
60
|
+
});
|
|
61
|
+
img.controls = createImageCroppingControls();
|
|
62
|
+
|
|
63
|
+
return img;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
canvas = new Canvas();
|
|
68
|
+
image = createMockImage();
|
|
69
|
+
canvas.add(image);
|
|
70
|
+
eventData = {};
|
|
71
|
+
transform = prepareTransform(image, 'mrc');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
canvas.off();
|
|
76
|
+
canvas.clear();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('changeImageWidth', () => {
|
|
80
|
+
test('changes width normally when within bounds', () => {
|
|
81
|
+
expect(image.width).toBe(100);
|
|
82
|
+
const changed = changeImageWidth(eventData, transform, 180, 50);
|
|
83
|
+
expect(changed).toBe(true);
|
|
84
|
+
expect(image.width).toBe(180);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('constrains width to available width (upper limit)', () => {
|
|
88
|
+
// Image element is 200px wide, cropX is 0, so max available is 200
|
|
89
|
+
image = createMockImage({ width: 100, cropX: 50, elementWidth: 200 });
|
|
90
|
+
canvas.add(image);
|
|
91
|
+
transform = prepareTransform(image, 'mrc');
|
|
92
|
+
|
|
93
|
+
// Try to set width beyond available (200 - 50 = 150 available)
|
|
94
|
+
changeImageWidth(eventData, transform, 500, 50);
|
|
95
|
+
expect(image.width).toBe(150);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('constrains width to minimum of 1 (lower limit)', () => {
|
|
99
|
+
image = createMockImage({ width: 100, cropX: 50, elementWidth: 200 });
|
|
100
|
+
transform = prepareTransform(image, 'mrc');
|
|
101
|
+
changeImageWidth(eventData, transform, 0.1, 50);
|
|
102
|
+
expect(image.width).toBe(1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('returns false when no modification occurred', () => {
|
|
106
|
+
image = createMockImage({
|
|
107
|
+
width: 100,
|
|
108
|
+
cropX: 50,
|
|
109
|
+
elementWidth: 200,
|
|
110
|
+
});
|
|
111
|
+
transform = prepareTransform(image, 'mrc');
|
|
112
|
+
const changed = changeImageWidth(eventData, transform, 200, 50);
|
|
113
|
+
expect(changed).toBe(true);
|
|
114
|
+
const changed2 = changeImageWidth(eventData, transform, 200, 50);
|
|
115
|
+
expect(changed2).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('changeImageHeight', () => {
|
|
120
|
+
beforeEach(() => {
|
|
121
|
+
image = createMockImage({
|
|
122
|
+
height: 100,
|
|
123
|
+
cropY: 50,
|
|
124
|
+
elementHeight: 200,
|
|
125
|
+
});
|
|
126
|
+
transform = prepareTransform(image, 'mbc');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('changes height normally when within bounds', () => {
|
|
130
|
+
expect(image.height).toBe(100);
|
|
131
|
+
const changed = changeImageHeight(eventData, transform, 50, 130);
|
|
132
|
+
expect(changed).toBe(true);
|
|
133
|
+
expect(image.height).toBe(130);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('constrains height to available height (upper limit)', () => {
|
|
137
|
+
// Try to set height beyond available (200 - 50 = 150 available
|
|
138
|
+
changeImageHeight(eventData, transform, 50, 500);
|
|
139
|
+
expect(image.height).toBeLessThanOrEqual(150);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('constrains height to minimum of 1 (lower limit)', () => {
|
|
143
|
+
// Mock to simulate setting negative height
|
|
144
|
+
changeImageHeight(eventData, transform, 50, 0.1);
|
|
145
|
+
expect(image.height).toBe(1);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('returns false when no modification occurred', () => {
|
|
149
|
+
const changed = changeImageHeight(eventData, transform, 50, 200);
|
|
150
|
+
expect(changed).toBe(true);
|
|
151
|
+
const changed2 = changeImageHeight(eventData, transform, 50, 200);
|
|
152
|
+
expect(changed2).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('changeImageCropX', () => {
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
image = createMockImage({
|
|
159
|
+
width: 100,
|
|
160
|
+
cropX: 50,
|
|
161
|
+
elementWidth: 200,
|
|
162
|
+
});
|
|
163
|
+
// Use 'ml' corner for cropX - changing left side moves cropX
|
|
164
|
+
transform = prepareTransform(image, 'mlc');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('changes cropX and width together', () => {
|
|
168
|
+
const changed = changeImageCropX(eventData, transform, 20, 50);
|
|
169
|
+
expect(image.cropX).toBe(70);
|
|
170
|
+
expect(image.width).toBe(80);
|
|
171
|
+
expect(changed).toBe(true);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('constrains cropX to minimum of 0 and adjusts width accordingly', () => {
|
|
175
|
+
image = createMockImage({ width: 100, cropX: 10, elementWidth: 200 });
|
|
176
|
+
transform = prepareTransform(image, 'mlc');
|
|
177
|
+
|
|
178
|
+
changeImageCropX(eventData, transform, -10, 50);
|
|
179
|
+
|
|
180
|
+
// newCropX is clamped to 0 (was -10)
|
|
181
|
+
expect(image.cropX).toBe(0);
|
|
182
|
+
// width = 100 + 10 - 0 = 110
|
|
183
|
+
expect(image.width).toBe(110);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('constrains cropX so image stays within element bounds and adjusts width accordingly', () => {
|
|
187
|
+
changeImageCropX(eventData, transform, 50, 50);
|
|
188
|
+
// newCropX = 100, but clamped to elementWidth - width = 200 - 100 = 100 (stays 100)
|
|
189
|
+
expect(image.cropX).toBe(100);
|
|
190
|
+
// width = 100 + 50 - 100 = 50
|
|
191
|
+
expect(image.width).toBe(50);
|
|
192
|
+
// cropX + width should not exceed element width (200)
|
|
193
|
+
expect(image.cropX + image.width).toBeLessThanOrEqual(200);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test('returns false when no modification occurred', () => {
|
|
197
|
+
const changed = changeImageCropX(eventData, transform, 0, 50);
|
|
198
|
+
expect(changed).toBe(false);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('changeImageCropY', () => {
|
|
203
|
+
beforeEach(() => {
|
|
204
|
+
image = createMockImage({
|
|
205
|
+
height: 100,
|
|
206
|
+
cropY: 50,
|
|
207
|
+
elementHeight: 200,
|
|
208
|
+
});
|
|
209
|
+
// Use 'mt' corner for cropY - changing top side moves cropY
|
|
210
|
+
transform = prepareTransform(image, 'mtc');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('changes cropY and height together', () => {
|
|
214
|
+
const changed = changeImageCropY(eventData, transform, 50, 20);
|
|
215
|
+
// newCropY = 50 + 100 - 80 = 70
|
|
216
|
+
// height = 100 + 50 - 70 = 80
|
|
217
|
+
expect(image.cropY).toBe(70);
|
|
218
|
+
expect(image.height).toBe(80);
|
|
219
|
+
expect(changed).toBe(true);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test('constrains cropY to minimum of 0 and adjusts height accordingly', () => {
|
|
223
|
+
image = createMockImage({ height: 100, cropY: 10, elementHeight: 200 });
|
|
224
|
+
canvas.add(image);
|
|
225
|
+
transform = prepareTransform(image, 'mtc');
|
|
226
|
+
|
|
227
|
+
changeImageCropY(eventData, transform, 50, -30);
|
|
228
|
+
|
|
229
|
+
// newCropY is clamped to 0 (was -10)
|
|
230
|
+
expect(image.cropY).toBe(0);
|
|
231
|
+
// height = 100 + 10 - 0 = 110
|
|
232
|
+
expect(image.height).toBe(110);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('returns false when no modification occurred', () => {
|
|
236
|
+
const changed = changeImageCropY(eventData, transform, 50, 0);
|
|
237
|
+
expect(changed).toBe(false);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe('cropPanMoveHandler', () => {
|
|
242
|
+
beforeEach(() => {
|
|
243
|
+
image = createMockImage({
|
|
244
|
+
width: 100,
|
|
245
|
+
height: 100,
|
|
246
|
+
cropX: 50,
|
|
247
|
+
cropY: 50,
|
|
248
|
+
elementWidth: 300,
|
|
249
|
+
elementHeight: 300,
|
|
250
|
+
});
|
|
251
|
+
canvas.add(image);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test('pans the image by adjusting cropX and cropY', () => {
|
|
255
|
+
const original = {
|
|
256
|
+
left: image.left,
|
|
257
|
+
top: image.top,
|
|
258
|
+
cropX: image.cropX,
|
|
259
|
+
cropY: image.cropY,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Simulate moving the image 10px to the right and 10px down
|
|
263
|
+
image.left = original.left + 10;
|
|
264
|
+
image.top = original.top + 10;
|
|
265
|
+
|
|
266
|
+
const moveEvent = {
|
|
267
|
+
transform: {
|
|
268
|
+
target: image,
|
|
269
|
+
original,
|
|
270
|
+
} as unknown as Transform,
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
cropPanMoveHandler(moveEvent as any);
|
|
274
|
+
|
|
275
|
+
// cropX should decrease (panning right means showing more of the left side)
|
|
276
|
+
expect(image.cropX).toBeLessThan(original.cropX);
|
|
277
|
+
// cropY should decrease (panning down means showing more of the top)
|
|
278
|
+
expect(image.cropY).toBeLessThan(original.cropY);
|
|
279
|
+
// Position should be restored to original
|
|
280
|
+
expect(image.left).toBe(original.left);
|
|
281
|
+
expect(image.top).toBe(original.top);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test('constrains cropX to minimum of 0', () => {
|
|
285
|
+
const original = {
|
|
286
|
+
left: image.left,
|
|
287
|
+
top: image.top,
|
|
288
|
+
cropX: 10,
|
|
289
|
+
cropY: 50,
|
|
290
|
+
};
|
|
291
|
+
image.cropX = 10;
|
|
292
|
+
|
|
293
|
+
// Move far right to try to get negative cropX
|
|
294
|
+
image.left = original.left + 100;
|
|
295
|
+
image.top = original.top;
|
|
296
|
+
|
|
297
|
+
const moveEvent = {
|
|
298
|
+
transform: {
|
|
299
|
+
target: image,
|
|
300
|
+
original,
|
|
301
|
+
} as unknown as Transform,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
cropPanMoveHandler(moveEvent as any);
|
|
305
|
+
|
|
306
|
+
expect(image.cropX).toBeGreaterThanOrEqual(0);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test('constrains cropY to minimum of 0', () => {
|
|
310
|
+
const original = {
|
|
311
|
+
left: image.left,
|
|
312
|
+
top: image.top,
|
|
313
|
+
cropX: 50,
|
|
314
|
+
cropY: 10,
|
|
315
|
+
};
|
|
316
|
+
image.cropY = 10;
|
|
317
|
+
|
|
318
|
+
// Move far down to try to get negative cropY
|
|
319
|
+
image.left = original.left;
|
|
320
|
+
image.top = original.top + 100;
|
|
321
|
+
|
|
322
|
+
const moveEvent = {
|
|
323
|
+
transform: {
|
|
324
|
+
target: image,
|
|
325
|
+
original,
|
|
326
|
+
} as unknown as Transform,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
cropPanMoveHandler(moveEvent as any);
|
|
330
|
+
|
|
331
|
+
expect(image.cropY).toBeGreaterThanOrEqual(0);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
test('constrains cropX so crop area stays within element bounds', () => {
|
|
335
|
+
const original = {
|
|
336
|
+
left: image.left,
|
|
337
|
+
top: image.top,
|
|
338
|
+
cropX: 150, // Near the right edge (element is 300px wide)
|
|
339
|
+
cropY: 50,
|
|
340
|
+
};
|
|
341
|
+
image.cropX = 150;
|
|
342
|
+
|
|
343
|
+
// Move far left to try to exceed element width
|
|
344
|
+
image.left = original.left - 200;
|
|
345
|
+
image.top = original.top;
|
|
346
|
+
|
|
347
|
+
const moveEvent = {
|
|
348
|
+
transform: {
|
|
349
|
+
target: image,
|
|
350
|
+
original,
|
|
351
|
+
} as unknown as Transform,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
cropPanMoveHandler(moveEvent as any);
|
|
355
|
+
|
|
356
|
+
// cropX + width should not exceed element width
|
|
357
|
+
expect(image.cropX + image.width).toBeLessThanOrEqual(300);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test('constrains cropY so crop area stays within element bounds', () => {
|
|
361
|
+
const original = {
|
|
362
|
+
left: image.left,
|
|
363
|
+
top: image.top,
|
|
364
|
+
cropX: 50,
|
|
365
|
+
cropY: 150, // Near the bottom edge (element is 300px tall)
|
|
366
|
+
};
|
|
367
|
+
image.cropY = 150;
|
|
368
|
+
|
|
369
|
+
// Move far up to try to exceed element height
|
|
370
|
+
image.left = original.left;
|
|
371
|
+
image.top = original.top - 200;
|
|
372
|
+
|
|
373
|
+
const moveEvent = {
|
|
374
|
+
transform: {
|
|
375
|
+
target: image,
|
|
376
|
+
original,
|
|
377
|
+
} as unknown as Transform,
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
cropPanMoveHandler(moveEvent as any);
|
|
381
|
+
|
|
382
|
+
// cropY + height should not exceed element height
|
|
383
|
+
expect(image.cropY + image.height).toBeLessThanOrEqual(300);
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
describe('ghostScalePositionHandler', () => {
|
|
388
|
+
beforeEach(() => {
|
|
389
|
+
image = createMockImage({
|
|
390
|
+
width: 100,
|
|
391
|
+
height: 100,
|
|
392
|
+
cropX: 50,
|
|
393
|
+
cropY: 50,
|
|
394
|
+
elementWidth: 300,
|
|
395
|
+
elementHeight: 300,
|
|
396
|
+
});
|
|
397
|
+
canvas.add(image);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test('positions top-left corner control correctly', () => {
|
|
401
|
+
const control = new Control({ x: -0.5, y: -0.5 });
|
|
402
|
+
const result = ghostScalePositionHandler.call(
|
|
403
|
+
control,
|
|
404
|
+
new Point(100, 100),
|
|
405
|
+
[1, 2, 3, 4, 5, 6], // this matrix is not used
|
|
406
|
+
image,
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
expect(result).toEqual({ x: -50, y: -50 });
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
test('positions bottom-right corner control correctly', () => {
|
|
413
|
+
const control = new Control({ x: 0.5, y: 0.5 });
|
|
414
|
+
const result = ghostScalePositionHandler.call(
|
|
415
|
+
control,
|
|
416
|
+
new Point(100, 100),
|
|
417
|
+
[1, 2, 3, 4, 5, 6], // this matrix is not used
|
|
418
|
+
image,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
expect(result).toEqual({ x: 250, y: 250 });
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('positions top-right corner control correctly', () => {
|
|
425
|
+
const control = new Control({ x: 0.5, y: -0.5 });
|
|
426
|
+
const result = ghostScalePositionHandler.call(
|
|
427
|
+
control,
|
|
428
|
+
new Point(100, 100),
|
|
429
|
+
[1, 2, 3, 4, 5, 6], // this matrix is not used
|
|
430
|
+
image,
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
expect(result).toEqual({ x: 250, y: -50 });
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test('positions bottom-left corner control correctly', () => {
|
|
437
|
+
const control = new Control({ x: -0.5, y: 0.5 });
|
|
438
|
+
const result = ghostScalePositionHandler.call(
|
|
439
|
+
control,
|
|
440
|
+
new Point(100, 100),
|
|
441
|
+
[1, 2, 3, 4, 5, 6], // this matrix is not used
|
|
442
|
+
image,
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
expect(result).toEqual({ x: -50, y: 250 });
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
describe('scaleEquallyCropGenerator', () => {
|
|
450
|
+
beforeEach(() => {
|
|
451
|
+
image = createMockImage({
|
|
452
|
+
width: 100,
|
|
453
|
+
height: 100,
|
|
454
|
+
cropX: 50,
|
|
455
|
+
cropY: 50,
|
|
456
|
+
elementWidth: 300,
|
|
457
|
+
elementHeight: 300,
|
|
458
|
+
});
|
|
459
|
+
canvas.add(image);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
test('returns a TransformActionHandler function', () => {
|
|
463
|
+
const handler = scaleEquallyCropGenerator(-0.5, -0.5);
|
|
464
|
+
expect(typeof handler).toBe('function');
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test('scales image uniformly from top-left corner', () => {
|
|
468
|
+
const handler = scaleEquallyCropGenerator(-0.5, -0.5);
|
|
469
|
+
transform = prepareTransform(image, 'tls');
|
|
470
|
+
expect(image.scaleX).toBe(1);
|
|
471
|
+
// Simulate dragging to scale up
|
|
472
|
+
const result = handler(eventData, transform, -400, -400);
|
|
473
|
+
|
|
474
|
+
// The handler should return a boolean
|
|
475
|
+
expect(result).toBe(true);
|
|
476
|
+
expect(image.scaleX.toFixed(2)).toBe('2.17');
|
|
477
|
+
expect(image.scaleX).toBe(image.scaleY);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test('scales image uniformly from bottom-right corner', () => {
|
|
481
|
+
const handler = scaleEquallyCropGenerator(0.5, 0.5);
|
|
482
|
+
transform = prepareTransform(image, 'brs');
|
|
483
|
+
expect(image.scaleX).toBe(1);
|
|
484
|
+
const result = handler(eventData, transform, 400, 400);
|
|
485
|
+
expect(result).toBe(true);
|
|
486
|
+
expect(image.scaleX).toBe(1.5);
|
|
487
|
+
expect(image.scaleX).toBe(image.scaleY);
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test('returns false when scaling would exceed element bounds', () => {
|
|
491
|
+
// Set up image near the edge of element
|
|
492
|
+
image = createMockImage({
|
|
493
|
+
width: 250,
|
|
494
|
+
height: 250,
|
|
495
|
+
cropX: 25,
|
|
496
|
+
cropY: 25,
|
|
497
|
+
elementWidth: 300,
|
|
498
|
+
elementHeight: 300,
|
|
499
|
+
});
|
|
500
|
+
canvas.add(image);
|
|
501
|
+
|
|
502
|
+
const handler = scaleEquallyCropGenerator(-0.5, -0.5);
|
|
503
|
+
transform = prepareTransform(image, 'tls');
|
|
504
|
+
|
|
505
|
+
// Try to scale down significantly which might push bounds
|
|
506
|
+
const result = handler(eventData, transform, 10, 10);
|
|
507
|
+
|
|
508
|
+
expect(result).toBe(false);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test('adjusts cropX and cropY when scaling from negative corner', () => {
|
|
512
|
+
image = createMockImage({
|
|
513
|
+
width: 90,
|
|
514
|
+
height: 90,
|
|
515
|
+
cropX: 25,
|
|
516
|
+
cropY: 25,
|
|
517
|
+
elementWidth: 300,
|
|
518
|
+
elementHeight: 300,
|
|
519
|
+
});
|
|
520
|
+
canvas.add(image);
|
|
521
|
+
const handler = scaleEquallyCropGenerator(-0.5, -0.5);
|
|
522
|
+
transform = prepareTransform(image, 'tls');
|
|
523
|
+
expect(image.cropX).toBe(25);
|
|
524
|
+
expect(image.cropY).toBe(25);
|
|
525
|
+
const result = handler(eventData, transform, 5, 5);
|
|
526
|
+
expect(result).toBe(true);
|
|
527
|
+
// When scaling from top-left, cropX and cropY should be recalculated
|
|
528
|
+
expect(image.cropX).toBe(0);
|
|
529
|
+
expect(image.cropY).toBe(0);
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
describe('renderGhostImage', () => {
|
|
534
|
+
beforeEach(() => {
|
|
535
|
+
image = createMockImage({
|
|
536
|
+
width: 100,
|
|
537
|
+
height: 100,
|
|
538
|
+
cropX: 50,
|
|
539
|
+
cropY: 50,
|
|
540
|
+
elementWidth: 300,
|
|
541
|
+
elementHeight: 300,
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test('draws image at correct position based on crop values', () => {
|
|
546
|
+
const mockCtx = {
|
|
547
|
+
globalAlpha: 1,
|
|
548
|
+
drawImage: vi.fn(),
|
|
549
|
+
} as unknown as CanvasRenderingContext2D;
|
|
550
|
+
|
|
551
|
+
renderGhostImage.call(image, { ctx: mockCtx });
|
|
552
|
+
|
|
553
|
+
// Should draw at (-width/2 - cropX, -height/2 - cropY)
|
|
554
|
+
// = (-50 - 50, -50 - 50) = (-100, -100)
|
|
555
|
+
expect(mockCtx.drawImage).toHaveBeenCalledWith(
|
|
556
|
+
image._element,
|
|
557
|
+
-100,
|
|
558
|
+
-100,
|
|
559
|
+
);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
test('temporarily reduces globalAlpha by 50%', () => {
|
|
563
|
+
let alphaWhenDrawing: number | undefined;
|
|
564
|
+
const mockCtx = {
|
|
565
|
+
globalAlpha: 0.8,
|
|
566
|
+
drawImage: vi.fn(() => {
|
|
567
|
+
alphaWhenDrawing = mockCtx.globalAlpha;
|
|
568
|
+
}),
|
|
569
|
+
} as unknown as CanvasRenderingContext2D;
|
|
570
|
+
|
|
571
|
+
renderGhostImage.call(image, { ctx: mockCtx });
|
|
572
|
+
|
|
573
|
+
// During draw, alpha should be 0.8 * 0.5 = 0.4
|
|
574
|
+
expect(alphaWhenDrawing).toBe(0.4);
|
|
575
|
+
// After render, alpha should be restored
|
|
576
|
+
expect(mockCtx.globalAlpha).toBe(0.8);
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
});
|