# @valor/nativescript-view-shot Take 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 ``` ## Usage ### Core/flavor agnostic 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; ``` ### Angular ```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, attached: boolean) { 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) => Promise); /** * 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(type: Type | TemplateRef, options?: DrawableOptions): Promise; captureRenderedView(view: View): ImageSource; } ``` ## Important details 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. ## License MIT License