feat(2d): add smooth corners and sharpness to rect (#310)

This commit is contained in:
Gustav Neustadt
2023-02-11 16:59:32 +01:00
committed by GitHub
parent 9c062b26e4
commit f7fbefd27f
2 changed files with 70 additions and 3 deletions

View File

@@ -5,17 +5,28 @@ import {
} from '@motion-canvas/core/lib/types'; } from '@motion-canvas/core/lib/types';
import {Shape, ShapeProps} from './Shape'; import {Shape, ShapeProps} from './Shape';
import {drawRoundRect} from '../utils'; import {drawRoundRect} from '../utils';
import {initial, signal} from '../decorators';
import {spacingSignal} from '../decorators/spacingSignal'; import {spacingSignal} from '../decorators/spacingSignal';
import {SignalValue} from '@motion-canvas/core/lib/signals'; import {SignalValue, SimpleSignal} from '@motion-canvas/core/lib/signals';
export interface RectProps extends ShapeProps { export interface RectProps extends ShapeProps {
radius?: SignalValue<PossibleSpacing>; radius?: SignalValue<PossibleSpacing>;
smoothCorners?: SignalValue<boolean>;
cornerSharpness?: SignalValue<number>;
} }
export class Rect extends Shape { export class Rect extends Shape {
@spacingSignal('radius') @spacingSignal('radius')
public declare readonly radius: SpacingSignal<this>; public declare readonly radius: SpacingSignal<this>;
@initial(false)
@signal()
public declare readonly smoothCorners: SimpleSignal<boolean, this>;
@initial(0.6)
@signal()
public declare readonly cornerSharpness: SimpleSignal<number, this>;
public constructor(props: RectProps) { public constructor(props: RectProps) {
super(props); super(props);
} }
@@ -23,8 +34,10 @@ export class Rect extends Shape {
protected override getPath(): Path2D { protected override getPath(): Path2D {
const path = new Path2D(); const path = new Path2D();
const radius = this.radius(); const radius = this.radius();
const smoothCorners = this.smoothCorners();
const cornerSharpness = this.cornerSharpness();
const rect = RectType.fromSizeCentered(this.size()); const rect = RectType.fromSizeCentered(this.size());
drawRoundRect(path, rect, radius); drawRoundRect(path, rect, radius, smoothCorners, cornerSharpness);
return path; return path;
} }
@@ -37,8 +50,10 @@ export class Rect extends Shape {
const path = new Path2D(); const path = new Path2D();
const rippleSize = this.rippleSize(); const rippleSize = this.rippleSize();
const radius = this.radius().addScalar(rippleSize); const radius = this.radius().addScalar(rippleSize);
const smoothCorners = this.smoothCorners();
const cornerSharpness = this.cornerSharpness();
const rect = RectType.fromSizeCentered(this.size()).expand(rippleSize); const rect = RectType.fromSizeCentered(this.size()).expand(rippleSize);
drawRoundRect(path, rect, radius); drawRoundRect(path, rect, radius, smoothCorners, cornerSharpness);
return path; return path;
} }

View File

@@ -39,6 +39,8 @@ export function drawRoundRect(
context: CanvasRenderingContext2D | Path2D, context: CanvasRenderingContext2D | Path2D,
rect: Rect, rect: Rect,
radius: Spacing, radius: Spacing,
smoothCorners: boolean,
cornerSharpness: number,
) { ) {
if ( if (
radius.top === 0 && radius.top === 0 &&
@@ -70,6 +72,56 @@ export function drawRoundRect(
rect, rect,
); );
if (smoothCorners) {
const sharpness = (radius: number): number => {
const val = radius * cornerSharpness;
return radius - val;
};
context.moveTo(rect.left + topLeft, rect.top);
context.lineTo(rect.right - topRight, rect.top);
context.bezierCurveTo(
rect.right - sharpness(topRight),
rect.top,
rect.right,
rect.top + sharpness(topRight),
rect.right,
rect.top + topRight,
);
context.lineTo(rect.right, rect.bottom - bottomRight);
context.bezierCurveTo(
rect.right,
rect.bottom - sharpness(bottomRight),
rect.right - sharpness(bottomRight),
rect.bottom,
rect.right - bottomRight,
rect.bottom,
);
context.lineTo(rect.left + bottomLeft, rect.bottom);
context.bezierCurveTo(
rect.left + sharpness(bottomLeft),
rect.bottom,
rect.left,
rect.bottom - sharpness(bottomLeft),
rect.left,
rect.bottom - bottomLeft,
);
context.lineTo(rect.left, rect.top + topLeft);
context.bezierCurveTo(
rect.left,
rect.top + sharpness(topLeft),
rect.left + sharpness(topLeft),
rect.top,
rect.left + topLeft,
rect.top,
);
return;
}
context.moveTo(rect.left + topLeft, rect.top); context.moveTo(rect.left + topLeft, rect.top);
context.arcTo(rect.right, rect.top, rect.right, rect.bottom, topRight); context.arcTo(rect.right, rect.top, rect.right, rect.bottom, topRight);
context.arcTo(rect.right, rect.bottom, rect.left, rect.bottom, bottomRight); context.arcTo(rect.right, rect.bottom, rect.left, rect.bottom, bottomRight);