diff --git a/CHANGELOG.md b/CHANGELOG.md index 37bccc430ec..74bde938a11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [next] +- fix(AligningGuidelines): Align guidless changes aspect ratio on snapping when scaling [#10114](https://github.com/fabricjs/fabric.js/issues/10114) + ## [6.4.1] - fix(): Package.json had wrong path to types for extensions [#10115](https://github.com/fabricjs/fabric.js/pull/10115) diff --git a/extensions/aligning_guidelines/index.ts b/extensions/aligning_guidelines/index.ts index 2c868bca89f..c8cffb08d3f 100644 --- a/extensions/aligning_guidelines/index.ts +++ b/extensions/aligning_guidelines/index.ts @@ -105,6 +105,10 @@ export function initAligningGuidelines( index -= 4; } let point = activeObject.getCoords()[index]; + const uniformIsToggled = e.e[canvas.uniScaleKey!]; + const isUniform = + (canvas.uniformScaling && !uniformIsToggled) || + (!canvas.uniformScaling && uniformIsToggled); for (const object of objects) { const [rect, coords] = getCaCheMapValue(object); const center = new Point( @@ -112,7 +116,7 @@ export function initAligningGuidelines( rect.top + rect.height / 2, ); const list = [...coords, center]; - const props = { activeObject, point, list, isScale, index }; + const props = { activeObject, point, list, isScale, isUniform, index }; const vLines = collectVerticalPoint(props); const hLines = collectHorizontalPoint(props); vLines.forEach((o) => { diff --git a/extensions/aligning_guidelines/util/collect-point.ts b/extensions/aligning_guidelines/util/collect-point.ts index a60236dafc6..dddf3841440 100644 --- a/extensions/aligning_guidelines/util/collect-point.ts +++ b/extensions/aligning_guidelines/util/collect-point.ts @@ -4,9 +4,15 @@ import { getDistance } from './basic'; type CollectPointProps = { activeObject: FabricObject; + /** Operation points of the target element: top-left, bottom-left, top-right, bottom-right */ point: Point; + /** Set of points to consider for alignment: [tl, tr, br, bl, center] */ list: Point[]; + /** Change the zoom or change the size, determine by whether e.transform.action starts with the string "scale" */ isScale: boolean; + /** Whether to change uniformly is determined by canvas.uniformScaling and canvas.uniScaleKey. */ + isUniform: boolean; + /** Which specific point to operate on, 0-3 correspond to top-left, top-right, bottom-right, bottom-left */ index: number; }; const originXArr: TOriginX[] = ['left', 'center', 'right']; @@ -14,23 +20,35 @@ const originYArr: TOriginY[] = ['top', 'center', 'bottom']; export function collectVerticalPoint(props: CollectPointProps) { const aligningLineMargin = aligningLineConfig.margin; - const { activeObject, isScale, index, point, list } = props; + const { activeObject, isScale, isUniform, index, point, list } = props; const { dis, arr } = getDistanceList(point, list, 'x'); const margin = aligningLineMargin / (activeObject.canvas?.getZoom() ?? 1); if (dis > margin) return []; let v = arr[arr.length - 1].x - point.x; - const dir = index == 0 || index == 3 ? -1 : 1; - v *= dir; + // To the left or to the right? + const dirX = index == 0 || index == 3 ? -1 : 1; + // To the top or to the bottom? + const dirY = index < 2 ? -1 : 1; + v *= dirX; - const { width, scaleX, left } = activeObject; + const { width, height, scaleX, scaleY, left, top } = activeObject; const dim = activeObject._getTransformedDimensions(); const sx = (v + dim.x) / dim.x; - if (isScale) activeObject.set('scaleX', scaleX * sx); - else activeObject.set('width', width * sx); - const dArr = [0, (v / 2) * dir, v * dir]; - if (dir < 0) dArr.reverse(); - const d = dArr[originXArr.indexOf(activeObject.originX)]; - activeObject.set('left', left + d); + if (isScale) { + activeObject.set('scaleX', scaleX * sx); + if (isUniform) activeObject.set('scaleY', scaleY * sx); + } else { + activeObject.set('width', width * sx); + if (isUniform) activeObject.set('height', height * sx); + } + const dx = getDisByOriginX(activeObject, v * dirX); + if (isUniform) { + const h = activeObject._getTransformedDimensions().y - dim.y; + const dy = getDisByOriginY(activeObject, h * dirY); + activeObject.set('top', top + dy); + } + activeObject.set('left', left + dx); + activeObject.setCoords(); return arr.map((item) => ({ x: item.x, @@ -41,23 +59,34 @@ export function collectVerticalPoint(props: CollectPointProps) { export function collectHorizontalPoint(props: CollectPointProps) { const aligningLineMargin = aligningLineConfig.margin; - const { activeObject, isScale, index, point, list } = props; + const { activeObject, isScale, isUniform, index, point, list } = props; const { dis, arr } = getDistanceList(point, list, 'y'); const margin = aligningLineMargin / (activeObject.canvas?.getZoom() ?? 1); if (dis > margin) return []; let v = arr[arr.length - 1].y - point.y; - const dir = index < 2 ? -1 : 1; - v *= dir; + // To the left or to the right? + const dirX = index == 0 || index == 3 ? -1 : 1; + // To the top or to the bottom? + const dirY = index < 2 ? -1 : 1; + v *= dirY; - const { height, scaleY, top } = activeObject; + const { width, height, scaleX, scaleY, left, top } = activeObject; const dim = activeObject._getTransformedDimensions(); const sy = (v + dim.y) / dim.y; - if (isScale) activeObject.set('scaleY', scaleY * sy); - else activeObject.set('height', height * sy); - const dArr = [0, (v / 2) * dir, v * dir]; - if (dir < 0) dArr.reverse(); - const d = dArr[originYArr.indexOf(activeObject.originY)]; - activeObject.set('top', top + d); + if (isScale) { + activeObject.set('scaleY', scaleY * sy); + if (isUniform) activeObject.set('scaleX', scaleX * sy); + } else { + activeObject.set('height', height * sy); + if (isUniform) activeObject.set('width', width * sy); + } + const dy = getDisByOriginY(activeObject, v * dirY); + if (isUniform) { + const w = activeObject._getTransformedDimensions().x - dim.x; + const dx = getDisByOriginX(activeObject, w * dirX); + activeObject.set('left', left + dx); + } + activeObject.set('top', top + dy); activeObject.setCoords(); return arr.map((item) => ({ y: item.y, @@ -81,3 +110,14 @@ function getDistanceList(point: Point, list: Point[], type: 'x' | 'y') { } return { dis, arr }; } + +function getDisByOriginX(target: FabricObject, v: number) { + const dArr = [0, v / 2, v]; + if (v < 0) dArr.reverse(); + return dArr[originXArr.indexOf(target.originX)]; +} +function getDisByOriginY(target: FabricObject, v: number) { + const dArr = [0, v / 2, v]; + if (v < 0) dArr.reverse(); + return dArr[originYArr.indexOf(target.originY)]; +}