Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 59 additions & 1 deletion packages/studio/src/components/SelectedOutlineOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ type SelectedOutlineRotationDragTarget = {
readonly keyframeDisplayOffset: number;
readonly nodePath: SequencePropsSubscriptionKey;
readonly schema: SequenceSchema;
readonly transformOriginValue: string;
};

export type SelectedOutlineDragState = {
Expand Down Expand Up @@ -328,6 +329,36 @@ export const getSelectedOutlineRotationCornerInfo = (
};
};

export const getSelectedOutlineRotationPivot = ({
dimensions,
points,
transformOriginValue,
}: {
readonly dimensions: SelectedOutline['dimensions'];
readonly points: SelectedOutline['points'];
readonly transformOriginValue: string;
}): OutlinePoint => {
if (dimensions === null) {
return getOutlineCenter(points);
}

const parsed = parseTransformOrigin(transformOriginValue);
if (parsed === null) {
return getOutlineCenter(points);
}

const uv = parsedTransformOriginToUv({
parsed,
width: dimensions.width,
height: dimensions.height,
});
if (uv === null) {
return getOutlineCenter(points);
}

return getUvHandlePosition(points, uv);
};

const rectToPoints = (
elementRect: DOMRect,
containerRect: DOMRect,
Expand Down Expand Up @@ -2204,7 +2235,14 @@ const SelectedOutlineRotationCornerHandle: React.FC<{
forceSpecificCursor(cornerInfo.cursor);

const svgRect = svg.getBoundingClientRect();
const center = svgPointToClientPoint(cornerInfo.center, svgRect);
const center = svgPointToClientPoint(
getSelectedOutlineRotationPivot({
dimensions: outline.dimensions,
points: outline.points,
transformOriginValue: rotationDrag.transformOriginValue,
}),
svgRect,
);
const dragStates = getSelectedOutlineRotationDragStates({
dragTargets: selected ? allRotationDragTargets : [rotationDrag],
getDragOverrides,
Expand Down Expand Up @@ -2347,6 +2385,8 @@ const SelectedOutlineRotationCornerHandle: React.FC<{
cornerInfo,
getDragOverrides,
onDraggingChange,
outline.dimensions,
outline.points,
onSelect,
rotationDrag,
selected,
Expand Down Expand Up @@ -2683,6 +2723,23 @@ export const SelectedOutlineOverlay: React.FC<{
controls?.schema[transformOriginFieldKey];
const transformOriginPropStatus =
nodePropStatuses?.[transformOriginFieldKey];
const rotationSourceFrame = timelinePosition - keyframeDisplayOffset;
const transformOriginValueForRotation =
transformOriginFieldSchema?.type === 'transform-origin' &&
(transformOriginPropStatus?.status === 'static' ||
transformOriginPropStatus?.status === 'keyframed')
? String(
Internals.getEffectiveVisualModeValue({
propStatus: transformOriginPropStatus,
dragOverrideValue: (getDragOverrides(nodePath) ?? {})[
transformOriginFieldKey
],
defaultValue: transformOriginFieldSchema.default,
frame: rotationSourceFrame,
shouldResortToDefaultValueIfUndefined: true,
}) ?? transformOriginFieldSchema.default,
)
: '50% 50%';
const canDragStatus =
propStatus?.status === 'static' ||
(propStatus?.status === 'keyframed' &&
Expand Down Expand Up @@ -2800,6 +2857,7 @@ export const SelectedOutlineOverlay: React.FC<{
keyframeDisplayOffset,
nodePath,
schema: controls.schema,
transformOriginValue: transformOriginValueForRotation,
}
: null,
transformOriginDrag: canTransformOriginDrag
Expand Down
50 changes: 50 additions & 0 deletions packages/studio/src/test/timeline-selection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
getSelectedOutlineRotationDeltaDegrees,
getSelectedOutlineRotationDragChanges,
getSelectedOutlineRotationDragValues,
getSelectedOutlineRotationPivot,
getSelectedOutlineScaleDragChanges,
getSelectedOutlineScaleDragValues,
getSelectedOutlineScaleEdgeInfo,
Expand Down Expand Up @@ -2155,6 +2156,7 @@ test('Selected outline corner dragging rotates selected sequences', () => {
keyframeDisplayOffset: 30,
nodePath: firstNodePath,
schema,
transformOriginValue: '50% 50%',
},
},
{
Expand All @@ -2170,6 +2172,7 @@ test('Selected outline corner dragging rotates selected sequences', () => {
keyframeDisplayOffset: 30,
nodePath: secondNodePath,
schema,
transformOriginValue: '50% 50%',
},
},
] satisfies SelectedOutlineRotationDragState[];
Expand Down Expand Up @@ -2237,6 +2240,7 @@ test('Selected outline corner dragging keyframed rotation adds a keyframe at the
keyframeDisplayOffset: 30,
nodePath,
schema,
transformOriginValue: '50% 50%',
},
},
] satisfies SelectedOutlineRotationDragState[];
Expand Down Expand Up @@ -2303,6 +2307,52 @@ test('Selected outline rotation corners use the outline corners and center', ()
expect(topRight.cursor).toContain('") 12 12, alias');
});

test('Selected outline rotation pivot follows transform origin', () => {
const points = [
{x: 0, y: 0},
{x: 100, y: 0},
{x: 100, y: 50},
{x: 0, y: 50},
] as const;
const dimensions = {width: 100, height: 50};

expect(
getSelectedOutlineRotationPivot({
dimensions,
points,
transformOriginValue: 'center',
}),
).toEqual({x: 50, y: 25});
expect(
getSelectedOutlineRotationPivot({
dimensions,
points,
transformOriginValue: 'left bottom',
}),
).toEqual({x: 0, y: 50});
expect(
getSelectedOutlineRotationPivot({
dimensions,
points,
transformOriginValue: '25px top',
}),
).toEqual({x: 25, y: 0});
expect(
getSelectedOutlineRotationPivot({
dimensions,
points,
transformOriginValue: 'calc(50% + 1px) center',
}),
).toEqual({x: 50, y: 25});
expect(
getSelectedOutlineRotationPivot({
dimensions: null,
points,
transformOriginValue: 'left top',
}),
).toEqual({x: 50, y: 25});
});

test('Selected outline rotation cursors use the outline rotation', () => {
const points = [
{x: 0, y: 0},
Expand Down
Loading