Skip to content

Commit

Permalink
Merge pull request #1350 from remotion-dev/extend-viewbox
Browse files Browse the repository at this point in the history
  • Loading branch information
JonnyBurger committed Sep 28, 2022
2 parents a5c6eb4 + f26e78e commit 81875f7
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 0 deletions.
4 changes: 4 additions & 0 deletions packages/docs/components/TableOfContents/paths.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export const TableOfContents: React.FC = () => {
<strong>evolvePath()</strong>
<div>Animate an SVG path</div>
</TOCItem>
<TOCItem link="/docs/paths/extend-viewbox">
<strong>extendViewBox()</strong>
<div>Widen an SVG viewBox in all directions</div>
</TOCItem>
</Grid>
</div>
);
Expand Down
62 changes: 62 additions & 0 deletions packages/docs/docs/paths/extend-viewbox.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: extendViewBox()
---

_Part of the [`@remotion/paths`](/docs/paths) package. Available since v3.2.25_

Widens an SVG `viewBox` in all directions by a certain scale factor.

```tsx twoslash
import { extendViewBox } from "@remotion/paths";

const extended = extendViewBox("0 0 1000 1000", 2);
console.log(extended); // "-500 -500 2000 2000"
```

The function will throw if the viewBox is invalid.

## Example: Displaying an SVG path that goes out of bounds

Consider the following SVG:

The path will go from `0` to `1500` on the horizontal axis, but it will be cut off because it goes beyond the viewport area.

```tsx twoslash
const viewBox = "0 0 1000 1000";

export const ViewBoxExample: React.FC = () => {
return (
<svg viewBox={viewBox}>
<path d={"0 500 1500 500"} stroke="black" strokeWidth={4} />
</svg>
);
};
```

We can fix the cutoff by doing two things:

- Scaling the viewBox by a factor of 2
- Applying a 2x scale transform to the SVG.

```tsx twoslash
import { extendViewBox } from "@remotion/paths";

const viewBox = "0 0 1000 1000";

export const ViewBoxExample: React.FC = () => {
return (
<svg style={{ scale: "2" }} viewBox={extendViewBox(viewBox, 2)}>
<path d={"0 500 1500 500"} stroke="black" strokeWidth={4} />
</svg>
);
};
```

By doing that, the each dimensions of the viewBox will be doubled, which will result in the picture being scaled down. By applying a scale transform, this can be corrected.

In this example, a factor of `2` was chosen because it is enough to fix the cutoff problem. The more the SVG path goes outside the container, the higher the factor needs to be to compensate.

## See also

- [`@remotion/paths`](/docs/paths)
- [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/paths/src/extend-viewbox.ts)
1 change: 1 addition & 0 deletions packages/docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ module.exports = {
"paths/normalize-path",
"paths/interpolate-path",
"paths/evolve-path",
"paths/extend-viewbox",
],
},
],
Expand Down
41 changes: 41 additions & 0 deletions packages/paths/src/extend-viewbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Extends a viewbox in all directions by a scale factor.
* @param {string} currentViewBox A valid SVG viewBox
* @link https://remotion.dev/docs/paths/extend-viewbox
*/
export const extendViewBox = (currentViewBox: string, scale: number) => {
const relativeScale = scale - 1;
const splitted = currentViewBox
.split(' ')
.map((a) => a.trim())
.filter((a) => a !== '')
.map(Number);

if (splitted.length !== 4) {
throw new Error(
`currentViewBox must be 4 valid numbers, but got "${currentViewBox}"`
);
}

for (const part of splitted) {
if (Number.isNaN(part)) {
throw new Error(
`currentViewBox must be 4 valid numbers, but got "${currentViewBox}"`
);
}

if (!Number.isFinite(part)) {
throw new Error(
`currentViewBox must be 4 valid numbers, but got "${currentViewBox}"`
);
}
}

const [x, y, width, height] = splitted;
return [
x - (relativeScale * width) / 2,
y - (relativeScale * height) / 2,
width + relativeScale * width,
height + relativeScale * height,
].join(' ');
};
1 change: 1 addition & 0 deletions packages/paths/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export {evolvePath} from './evolve-path';
export {extendViewBox} from './extend-viewbox';
export {getLength} from './get-length';
export {getParts} from './get-parts';
export {getPointAtLength} from './get-point-at-length';
Expand Down
12 changes: 12 additions & 0 deletions packages/paths/src/test/extend-viewbox.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {expect, test} from 'vitest';
import {extendViewBox} from '../extend-viewbox';

test('Should be able to extend a viewBox', () => {
expect(extendViewBox('0 0 1000 1000', 2)).toEqual('-500 -500 2000 2000');
});

test('Should reject an invalid viewBox', () => {
expect(() => extendViewBox('0 0 1000 ', 2)).toThrow(
/currentViewBox must be 4 valid numbers, but got "0 0 1000 "/
);
});

1 comment on commit 81875f7

@vercel
Copy link

@vercel vercel bot commented on 81875f7 Sep 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.