Turn any NativeScript View into an image!
npm install @valor/nativescript-view-shotTake a screenshot of an existing view, or render a view in the background and take a screenshot of it.
``javascript`
ns plugin add @valor/nativescript-view-shot
Render a visible view:
`xml`
`ts`
renderVisible() {
const hostView: GridLayout = this.page.getViewById('visibleView');
const result = renderToImageSource(hostView);
// work with result
}
Render an invisible view:
`xml`
`ts`
render() {
const hostView: GridLayout = this.page.getViewById('hostView');
// measure and layout the detached view with the desired width/height
measureAndLayout(hostView, Screen.mainScreen.widthDIPs);
const result = renderToImageSource(hostView);
// work with result
}
Create and render a view completely detached:
`ts`
renderDetached() {
const myView = new GridLayout();
const renderedViewData = loadViewInBackground(myView);
// measure and layout the detached view with the desired width/height
measureAndLayout(renderedViewData.hostView, Screen.mainScreen.widthDIPs);
const result = renderToImageSource(renderedViewData.hostView);
disposeBackgroundView(renderedViewData);
// work with result
}
#### API
`ts
import { ImageSource, View } from '@nativescript/core';
export interface BackgroundViews {
// parent of the view that will be rendered to ensure margins are respected
hostView: View;
// a view that wraps the hostView and is collapsed
hiddenHost: View;
// a view container that will be inserted in the view tree to make it part of the view hierarchy
// (enables CSS inheritance)
logicalContainer?: View;
}
export function measureAndLayout(hostView: View, width?: number, height?: number): void;
export class LogicalViewContainer extends LayoutBase {}
export function renderToImageSource(hostView: View): ImageSource;
export function loadViewInBackground(view: View, host?: View): BackgroundViews;
export function disposeBackgroundView(backgroundViews: BackgroundViews): void;
`
`html`
`ts
export class NativescriptViewShotComponent {
viewShotService = inject(ViewShotService);
@ViewChild('viewHost', { read: ViewContainerRef }) vcRef: ViewContainerRef;
srcUrl = https://picsum.photos/${Screen.mainScreen.widthPixels}/200;
webImage$ = ImageSource.fromUrl(this.srcUrl);
webImage2: ImageSource;
imgSrc: ImageSource;
async renderTemplate(template: TemplateRef
const width = Screen.mainScreen.widthDIPs;
// reusing imageSource on iOS sometimes doesn't work, so we create a new one each time
this.webImage2 = new ImageSource((await this.webImage$).ios);
this.loading = false;
this.imgSrc = await this.viewShotService.captureInBackground(template, {
logicalHost: attached ? this.vcRef : undefined,
width,
delay: 0,
});
}
screenshotView(view: View) {
this.imgSrc = renderToImageSource(view);
}
}
`
#### API
`ts
import { Injector, TemplateRef, Type, ViewContainerRef } from '@angular/core';
import { NgViewRef } from '@nativescript/angular';
import { ImageSource, View, ViewBase } from '@nativescript/core';
export interface DrawableOptions
/**
* target width of the view and image, in dip. If not specified, the measured width of the view will be used.
*/
width?: number;
/**
* target height of the view and image, in dip. If not specified, the measured height of the view will be used.
*/
height?: number;
/**
* injector to use for the component. If not specified, the injector of the logical host will be used.
* if there is not logical host, then this service's injector will be used. (most likely the root injector)
*/
injector?: Injector;
/**
* how much should we delay the rendering of the view into the image.
* This is useful if you want to wait for an image to load before rendering the view.
* If using a function, it will be called with the NgViewRef as the first argument.
* The NgViewRef can be used to get the EmbeddedViewRef/ComponentRef and the NativeScript views.
* This is useful as you can fire an event in your views when the view is ready, and then complete
* the promise to finish rendering to image.
*/
delay?: number | ((viewRef: NgViewRef
/**
* The logical host of the view. This is used to specify where in the DOM this view should lie.
* The practical use of this is if you want the view to inherit CSS styles from a parent.
* If this is not specified, the view will be handled as a root view,
* meaning no ancestor styles will be applied, similar to dropping the view in app.component.html
*/
logicalHost?: ViewBase | ViewContainerRef;
}
export declare class ViewShotService {
captureInBackground
captureRenderedView(view: View): ImageSource;
}
`
Some views (like images) take a while to load. So you might need to add a delay between start rendering a view and generating the image. In angular, the delay option is available for this purpose, and it can be a number, for a simple wait, or a function that will receive the NgViewRef` as a parameter and must return a promise. This can be used to listen to events on the view, or to wait for a specific amount of time.
MIT License