Skip to content

Commit

Permalink
feat: add runtime layout fit type for ios, android, web
Browse files Browse the repository at this point in the history
First steps towards supporting artboard resizing in our runtimes. This PR includes:
- New Fit type `autoResizeArtboard`. After a bit of back and forth, I think this keeps the API simple.
- ScaleFactor which represents a scale value to scale the artboard by in addition to resizing (only applies with `autoResizeArtboard`). This may be useful because an artboard is created at a specific width/height, but it may be deployed to platforms where it is rendered to a much smaller or larger surface/texture. Currently the default is 1.0 (no scale), however, an alternative is to have it default to something like textureSize / artboardSize so we sort of auto normalize it.
- Implemented on iOS. Once this is finalzed, we can work with DevRels to implement across all runtimes.
- TODO : Bubble up an event when the artboard size is changed internally by the .riv

https://github.com/user-attachments/assets/20e9fdda-5c3e-4f3f-b2f5-104ff0291fbe

Diffs=
e71b4cc081 feat: add runtime layout fit type for ios, android, web (#8341)

Co-authored-by: CI <pggordonhayes@gmail.com>
Co-authored-by: David Skuza <david@rive.app>
Co-authored-by: Philip Chung <philterdesign@gmail.com>
  • Loading branch information
4 people committed Oct 30, 2024
1 parent a2836ae commit 7e555bb
Show file tree
Hide file tree
Showing 25 changed files with 434 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c64659be84f36d2855173491d6bcab8255c50950
e71b4cc081aad43f304589d7233fe2d8065fda85
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ git checkout origin/master
cd ../../..
```

4. Install [Emscripten](https://emscripten.org/docs/getting_started/downloads.html). We build against `v3.1.29` in rive-wasm, so make sure to install and activate that version.
4. Install [Emscripten](https://emscripten.org/docs/getting_started/downloads.html). We build against `v3.1.43` in rive-wasm, so make sure to install and activate that version.
5. Install [Premake5](https://premake.github.io/) and add it to your PATH

6. `cd` back into the `js` folder and run `./build.sh` from your terminal/shell to build the latest WASM and builds for JS API's (high and low level) into the `npm/` folder. This may take some time, grab a coffee! This should finish with Webpack building the JS bundles for the high-level API packages (more on that below)
Expand Down
15 changes: 15 additions & 0 deletions js/examples/_frameworks/layout_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Rive Layouts example

This is an example showing Rive Layout - try adjusting the window size or zooming in/out. It uses the high-level JS/TS API from `rive-wasm` referencing the local build for `@rive-app/canvas`.

This is accomplished by setting the `fit` to type `Fit.layout`, and updating the artboard size as the window changes. You can also adjust the `scaleFactor` in the `align` method to account for device pixel ratio, or to allow for zooming in/out of your graphics.

## Install

Run `npm i`

## Run

To run, make sure the `npm/canvas` build is built locally in `js/npm/canvas`. Run `./build.sh` from the `js` folder to build the WASM, and the JS bundler. This should give you a `rive.js` and `rive.wasm` file in the `npm/canvas` folder.

Then run `npm start` in the root of this folder to run the parcel example and navigate to `http://localhost:1234` in the browser to check it out.
Binary file not shown.
26 changes: 26 additions & 0 deletions js/examples/_frameworks/layout_example/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Canvas Layout</title>
<style>
body {
background: #f0f0f0;
margin: 0;
overflow: hidden;
}

canvas {
background-color: red;
display: block;
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<canvas id="riveCanvas"></canvas>
<script src="./index.ts"></script>
</body>
</html>
44 changes: 44 additions & 0 deletions js/examples/_frameworks/layout_example/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import "regenerator-runtime";
import { Rive, Fit, Alignment, Layout } from "@rive-app/canvas";
import RiveLayoutTest from "/assets/layout_test.riv";

async function loadRiveFile() {
return await (await fetch(new Request(RiveLayoutTest))).arrayBuffer();
}

async function main() {
setup();
}

const riveCanvas = document.getElementById("riveCanvas") as HTMLCanvasElement;

async function setup() {
const bytes = await loadRiveFile();

const rive = new Rive({
buffer: bytes,
autoplay: true,
canvas: riveCanvas,
layout: new Layout({
fit: Fit.Layout,
alignment: Alignment.Center,
// layoutScaleFactor: 2, // 2x scale of the layout, when using `Fit.Layout`. This allows you to resize the layout as needed.
}),
stateMachines: ["State Machine 1"],
onLoad: () => {
computeSize();
},
});

function computeSize() {
rive.resizeDrawingSurfaceToCanvas();
}

window.onresize = computeSize;

window
.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`)
.addEventListener("change", computeSize);
}

main();
23 changes: 23 additions & 0 deletions js/examples/_frameworks/layout_example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "parcel_layout_example",
"version": "1.0.0",
"description": "",
"private": true,
"main": "src/index.js",
"scripts": {
"start": "parcel index.html",
"build": "rm -fR docs && parcel build index.html -d docs --public-url ."
},
"author": "",
"license": "MIT",
"devDependencies": {
"audio-play": "^2.3.1",
"dat.gui": "^0.7.7",
"parcel-bundler": "^1.12.5"
},
"dependencies": {
"@rive-app/canvas": "file:../../../npm/canvas_single",
"parcel-plugin-asset-copier": "^1.1.0",
"src": "^1.1.2"
}
}
17 changes: 17 additions & 0 deletions js/src/rive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export enum Fit {
FitHeight = "fitHeight",
None = "none",
ScaleDown = "scaleDown",
Layout = "layout",
}

// Alignment options for the canvas
Expand All @@ -62,6 +63,7 @@ export enum Alignment {
export interface LayoutParameters {
fit?: Fit;
alignment?: Alignment;
layoutScaleFactor?: number;
minX?: number;
minY?: number;
maxX?: number;
Expand All @@ -77,6 +79,7 @@ export class Layout {

public readonly fit: Fit;
public readonly alignment: Alignment;
public readonly layoutScaleFactor: number;
public readonly minX: number;
public readonly minY: number;
public readonly maxX: number;
Expand All @@ -85,6 +88,7 @@ export class Layout {
constructor(params?: LayoutParameters) {
this.fit = params?.fit ?? Fit.Contain;
this.alignment = params?.alignment ?? Alignment.Center;
this.layoutScaleFactor = params?.layoutScaleFactor ?? 1;
this.minX = params?.minX ?? 0;
this.minY = params?.minY ?? 0;
this.maxX = params?.maxX ?? 0;
Expand Down Expand Up @@ -112,6 +116,7 @@ export class Layout {
public copyWith({
fit,
alignment,
layoutScaleFactor,
minX,
minY,
maxX,
Expand All @@ -120,6 +125,7 @@ export class Layout {
return new Layout({
fit: fit ?? this.fit,
alignment: alignment ?? this.alignment,
layoutScaleFactor: layoutScaleFactor ?? this.layoutScaleFactor,
minX: minX ?? this.minX,
minY: minY ?? this.minY,
maxX: maxX ?? this.maxX,
Expand All @@ -138,6 +144,7 @@ export class Layout {
else if (this.fit === Fit.FitWidth) fit = rive.Fit.fitWidth;
else if (this.fit === Fit.FitHeight) fit = rive.Fit.fitHeight;
else if (this.fit === Fit.ScaleDown) fit = rive.Fit.scaleDown;
else if (this.fit === Fit.Layout) fit = rive.Fit.layout;
else fit = rive.Fit.none;

this.cachedRuntimeFit = fit;
Expand Down Expand Up @@ -1503,6 +1510,8 @@ export class Rive {
// Keep a local value of the set volume to update it asynchronously
private _volume = 1;

private _devicePixelRatioUsed = 1;

// Whether the canvas element's size is 0
private _hasZeroSize = false;

Expand Down Expand Up @@ -2009,6 +2018,7 @@ export class Rive {
maxY: _layout.maxY,
},
artboard.bounds,
this._devicePixelRatioUsed * _layout.layoutScaleFactor,
);
}

Expand Down Expand Up @@ -2276,10 +2286,17 @@ export class Rive {
if (this.canvas instanceof HTMLCanvasElement && !!window) {
const { width, height } = this.canvas.getBoundingClientRect();
const dpr = customDevicePixelRatio || window.devicePixelRatio || 1;
this._devicePixelRatioUsed = dpr;
this.canvas.width = dpr * width;
this.canvas.height = dpr * height;
this.startRendering();
this.resizeToCanvas();

if (this.layout.fit === Fit.Layout) {
const scaleFactor = this._layout.layoutScaleFactor;
this.artboard.width = width / scaleFactor;
this.artboard.height = height / scaleFactor;
}
}
}

Expand Down
27 changes: 26 additions & 1 deletion js/src/rive_advanced.mjs.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,17 @@ export interface RiveCanvas {
* @param frame - AABB object representing the bounds of the canvas frame
* @param content - AABB object representing the bounds of what to draw the Rive onto
* (i.e an artboard's size)
* @param scaleFactor - Scale factor of the artboard when using `Fit.layout`
* @returns Mat2D - A Mat2D view matrix
*/
computeAlignment(
fit: Fit,
alignment: Alignment,
frame: AABB,
content: AABB,
scaleFactor?: number,
): Mat2D;

mapXY(matrix: Mat2D, canvasPoints: Vec2D): Vec2D;
/**
* A Rive-specific requestAnimationFrame function; this must be used instead of the global
Expand Down Expand Up @@ -205,8 +208,15 @@ export declare class Renderer extends RendererWrapper {
* @param alignment - Alignment enum value
* @param frame - Bounds of the canvas space
* @param content - Bounds of the Rive content
* @param _scaleFactor - Scale factor of the artboard when using `Fit.layout`
*/
align(fit: Fit, alignment: Alignment, frame: AABB, content: AABB): void;
align(
fit: Fit,
alignment: Alignment,
frame: AABB,
content: AABB,
scaleFactor?: number,
): void;
}

export declare class CommandPath {}
Expand Down Expand Up @@ -462,6 +472,20 @@ export declare class Artboard {
* @param path - Path of where the text exists at an artboard level
*/
textByPath(name: string, path: string): TextValueRun;
/**
* Getter and setter for the artboard width
*/
get width(): number;
set width(val: number);
/**
* Getter and setter for the artboard height
*/
get height(): number;
set height(val: number);
/**
* Reset the artboard size to the original value
*/
resetArtboardSize(): void;
}

export declare class Bone extends TransformComponent {
Expand Down Expand Up @@ -791,6 +815,7 @@ export enum Fit {
fitHeight,
none,
scaleDown,
layout,
}

export enum RenderPaintStyle {
Expand Down
1 change: 1 addition & 0 deletions wasm/examples/layout_example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock.json
18 changes: 18 additions & 0 deletions wasm/examples/layout_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Rive Layouts example

This example uses the advanced runtime, and is built using parcel.
This is an example showing Rive Layout - try adjusting the window size or zooming in/out.

This is accomplished by setting the `fit` to type `Fit.layout`, and updating the artboard size as the window changes. You can also adjust the `scaleFactor` in the `align` method to account for device pixel ratio, or to allow for zooming in/out of your graphics.

To run, at the top of the rive-wasm project:

```
cd wasm
./build_all_wasm.sh
cd examples/layout
npm i
npm start
```

You should now see the example running on http://localhost:1234. Parcel will pick up any changes made to the example html/css/js automatically, but you will need to rebuild wasm with `./build_all_wasm.sh` anytime you make changes to the wasm runtime.
7 changes: 7 additions & 0 deletions wasm/examples/layout_example/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<body>
<canvas id="rive-canvas"></canvas>
<script src="./index.ts"></script>
</body>
</html>
Loading

0 comments on commit 7e555bb

Please sign in to comment.