Image Pan & Zoom Library For Angular 6+

ng2-panzoom

An Angular component for panning and zooming an element or elements using the mouse and mousewheel. Provides rudimentary support for touchscreens (read section on mobile support). It was adapted from the angular-pan-zoom library for AngularJS, but it has been heavily modified. Many thanks go out to Martin Vindahl Olsen for having written it, and for his blessing in this undertaking.

It is built using Angular CLI 9.x, so it may or may not work with Angular versions earlier than this. Please excuse the ‘ng2’ moniker – I could switch to ‘ngx’, but I honestly can’t be bothered. It is only tested with the corresponding version of Angular.

This library deliberately parts with certain received Angular wisdom of using only Angular-ish methods to accomplish things. We use native event listeners. We apply CSS transforms directly to the DOM. We even use a dash of jQuery. But as this library doesn’t fit the traditional Angular model, as its purpose is only to alter a certain part of the DOM using CSS transforms, without adding, moving or changing anything else, it has no impact on an application’s state (except if the app consumes modelChanged observables). By using this approach, it is hoped that compatibility and performance will be maximised.

Mobile Support – Please Send PR’s

I am actively soliciting pull requests for mobile support. Read on.

The library implements some basic support that may work with some mobile devices, though pinch-to-zoom still needs considerable work. As the application that this library was developed for was never intended for use with mobile devices, there are no plans to implement full mobile suppor. As long as this remains the case, I respecfully ask for no more issues concerning mobile support, please. I realise that this will limit adoption, but for an enterprising developer out there, I can’t imagine that adding mobile support would be nearly as big of a challenge as it was to port the library to Angular from AngularJS!

Demo

Click here for a demo of the module. The demo source can be found here.

Features

  • Zoom using mouse wheel, touch surface, double click, or API controls tied to your own UI.
  • Pan using click/touch and drag, or API calls. When releasing the mouse button or touch surface whilst panning, the pan will come to a gradual stop.

Version 9.0.0 Changes

Version 9.0.0 is compiled using Angular 9.0.1. Per the Angular guidance at the time of writing (https://angular.io/guide/creating-libraries), Ivy is not used for the NPM repo build. The following changes have been made:

  • The project structure has been changed to match new Angular CLI library projects.
  • Updated the remaining project definitions to match new CLI library projects.

Version 8.0.0 Changes

Version 8.0.0 introduces a new versioning scheme to match Angular releases. Version 8 of the library is compiled for version 8.x of Angular. 9.0.0 will be for 9.x, and so on.

  • jQuery is now a peer dependency rather than a dependency – be sure it’s installed in your project with npm install --save jquery.
  • You can probably also remove jQuery from ‘scripts’ in your project’s angular.json, as long as it’s installed in package.json, and if you’re importing it properly in your project (that is if you use jQuery at all). This is because a typescript import is now used, as opposed to accessing the global ‘$’ object.
  • Hardware acceleration is now enabled on the pan frame, in addition to the zoom frame. This could potentially have unintended consequences for some users, so it is configurable with the acceleratePan option.
  • Added config option acceleratePan to control pan acceleration (defaults to true).

Version 2.2.0 Changes

Version 2.2.0 contains two changes:

  • The library has been updated for Angular 8.
  • Addition of configuration parameter noDragFromElementClass.
  • Permit all Angular versions in peer dependencies. This is not a guarantee they will all work, though.

Version 2.1.0 Changes

Version 2.1.0 is a minor release with a couple of small changes:

  • Configuration options can now be passed to the PanZoomConfig constructor, rather than only being settable after initialisation.
  • Pan mouse button is now configurable via config parameter dragMouseButton, with values left, middle, and right.

Version 2.0 Changes

Version 2.0 brings enhanced performance, makes adjustments for modern hardware and browsers, cleans up a lot of underlying code, and may also bring backwards-compatibility for Angular 2 (no promises, though).

  • Version 2.0 has seen a fair number of under-the-bonnet (or hood) changes which should hopefully result in better panning and zooming performance.
  • Free wheel zooming is now the default experience and as such, freeMouseWheel now defaults to true.
  • The mouse wheel default direction has been inverted, so your invertMouseWheel setting may need to be flipped.
  • Several config options have been removed: useHardwareAcceleration, chromeUseTransform, and disableZoomAnimation.
  • The dependency on ng2-mousewheel has been removed.
  • It no longer requires Renderer2, so it may, at least in theory, work with Angular 2. Please send reports either way.
  • It’s 2019, so the library now assumes that all browsers and hardware have hardware acceleration.
  • Older browser-specifc CSS transforms have been removed in favour of newer standards-based transforms (i.e. ‘-webkit’ and ‘-moz’ prefixes and the like have been removed), which may cause breakage with older browsers. If that’s a problem, you should stick with version 1.x.

Differences From the Original

  • The pan-zoom service has been eliminated.
  • Free zoom - zooming is no longer limited to switching between two distinct zoom levels. Zoom can now be smoothly and freely controlled using the mouse wheel or trackpad.
  • zoomToFit() animation - using the zoomToFit() function now will animate the view to the a desired rectangle.
  • A convenience method resetView() has been provided to animate the view back to its initial settings.
  • The zoomIn() and zoomOut() API functions now zoom to the last zoomed point rather than the centre point, unless no zoom point has been defined yet.
  • New API methods panToPoint(), panDelta(), panDeltaPercent(), and panDeltaAbsolute() have been added for panning the view.
  • Many performance improvements.
  • The widget has not been migrated from the original project, though this probably shouldn’t be hard to do. Pull requests are welcome!
  • Touchscreen support works, but it is not great. Work on this will continue.

Dependencies

  • Angular
  • jQuery - Used for calculating positions of DOM elements (way easier than using Angular or native JS methods).

Installation

npm install ng2-panzoom jquery --save

app.module.ts:

import { Ng2PanZoomModule } from 'ng2-panzoom';

@NgModule({
  imports: [  ...,
              Ng2PanZoomModule
           ],
  ...
})

export class MyAppModule { }

Usage

This library exposes a component called ‘pan-zoom’, under which you may place any standard Angular template code. Though the events which trigger panning and zooming are run outside of Angular and thus themselves will not trigger change detection, it should not break change detection for any sub-components.

A configuration object is required, which gets passed in using [config]="myConfig".

It also exposes an API which can be used to interact with the pan/zoom view. The API is obtained through the configuration object (more below).

Top Tip

Be sure to place your pan-zoom component underneath an element with a definite height/width, like an absolute-positioned div. You may not see anything if you don’t do this.

import { PanZoomConfig, PanZoomAPI, PanZoomModel } from 'ng2-panzoom';

@Component({
  selector: 'my-component'
  template: `
    <div style="position: absolute; top: 100px; bottom: 0; left: 0; right: 0;">
      <pan-zoom [config]="panzoomConfig">
        <div style="position: relative;">
          <img src="/myimage1.jpg">
        </div>
      </pan-zoom>
    </div>
  `
})

export class MyComponent {
  ...
  private panZoomConfig: PanZoomConfig = new PanZoomConfig;
  ...
}

Configuration

You must first create and then pass in a configuration object (of type PanZoomConfig) via the config input property. This configuration object also contains RXJS Observables which can be used to work with the API and also observe changes to the panzoom view.

private panZoomConfig: PanZoomConfig = new PanZoomConfig;

API

The panzoom library provides an API for interacting with, observing, and controlling it. The following methods and objects are available from the PanZoomAPI:

  • model: PanZoomModel - The current panzoom model - see the PanZoomModel Interface below.

  • config: PanZoomConfig - The current panzooom configuration.

  • changeZoomLevel(newZoomLevel: number, clickPoint: Point) - This method will reset the view to newZoomLevel, with clickPoint as its centre point.

  • zoomIn() - This will zoom the view in to the last zoomed point by one zoom level.

  • zoomOut() - This will zoom the view out from the last zoomed point by one zoom level.

  • zoomToFit(rectangle: Rect, [duration: number]) - Animates the view to focus on a rectangle of the underlying canvas. duration is how long the animation should take (in seconds), and is optional. rectangle is two coordinates on the canvas which the panZoom view is pan/zooming. See the below section on PanZoom Interfaces for its definition.

  • resetView() - A shortcut method to reset the pan and zoom back to the initial view.

  • getViewPosition(modelPosition: Point) - By passing in x,y coordinates of the original, untransformed content canvas, it will return the current pixel position of this point.

  • getModelPosition(viewPosition: Point) - The reverse operation of getViewPosition().

  • panToPoint(point: Point, [duration: number]) - Will animate the view so that the centre point of the view is at the point parameter coordinates, relative to the original, unzoomed content width and height.

  • panDelta(delta: Point, [duration: number]) - Will pan the view left, right, up, or down, based on a number of pixels relative to the original, unzoomed content.

  • panDeltaPercent(deltaPercent: Point, [duration: number]) - Will pan the view up, down, left, or right, based on a percentage of the original, unzoomed content width and height.

  • panDeltaAbsolute(delta: Point, [duration: number]) - Will pan the view left, right, up, or down, based on a number of pixels. This method doesn’t adjust for scale. I’m not sure why you’d want this, but it’s provided just in case.

PanZoom API Interfaces:

interface PanZoomModel {
  zoomLevel: number;
  isPanning?: boolean;
  pan: Point; // how far the view has been moved on the x and y axes.  It is not adjusted for scale
}

interface Point {
  x: number;
  y: number;
}

interface Rect {
  x: number; // the x0 (top left) coordinate
  y: number; // the y0 (top left) coordinate
  width: number; // the x1 (bottom right) coordinate
  height: number; // the y1 (bottom right) coordinate
}

Getting at the API

The panzoom API is exposed through an RXJS observable as a property of the PanZoomConfig class, named api, to which you simply subscribe to obtain the API object. The subscription callback method will be passed the API as its only parameter, of type PanZoomAPI. Because it uses a BehaviorSubject, the callback will immediately trigger when subscribed to, assuming panzoom has already been initialised. If panzoom hasn’t yet been initialised, the subscription callback will fire as soon as initialisation occurs.

import { PanZoomConfig, PanZoomAPI, PanZoomModel } from 'ng2-panzoom';
import { Subscription } from 'rxjs';

@Component({ ... })

export class MyComponent implements OnInit, OnDestroy {

  private panZoomConfig: PanZoomConfig = new PanZoomConfig;
  private panZoomAPI: PanZoomAPI;
  private apiSubscription: Subscription;

  ngOnInit(): void {
    this.apiSubscription = this.panzoomConfig.api.subscribe( (api: PanZoomAPI) => this.panZoomAPI = api );
  }

  ngOnDestroy(): void {
    this.apiSubscription.unsubscribe();  // don't forget to unsubscribe.  you don't want a memory leak!
  }

}

Now that we have our API stored in this.panZoomAPI, we can access it thusly:

this.panZoomAPI.zoomIn();
this.panZoomAPI.zoomOut();

‘Events’

The PanZoomConfig class has an RXJS observable (modelChanged) which can be used to monitor the pan/zoom state from another component. The observable emits type PanZoomModel (see above section on API Interfaces). For instance, when the zoom level reaches a certain level, you may want to display a custom control or content on your page. Another use may be to do something when the panzoom centre point is over a certain part of the view.

Example modelChanged Subscription

import { PanZoomConfig, PanZoomAPI, PanZoomModel } from 'ng2-panzoom';

@Component({ ... })

export class MyComponent implements OnInit, OnDestroy {

  private panZoomConfig: PanZoomConfig = new PanZoomConfig;
  private modelChangedSubscription: Subscription;

  ngOnInit(): void {
    this.modelChangedSubscription = this.panzoomConfig.modelChanged.subscribe( (model: PanZoomModel) => this.onModelChanged(model) );
  }

  ngOnDestroy(): void {
    this.modelChangedSubscription.unsubscribe();  // don't forget to unsubscribe.  you don't want a memory leak!
  }

  onModelChanged(model: PanZoomModel): void {
    // do something after receiving your model update here
  }

}

Download Details:

Author: KensingtonTech

GitHub: https://github.com/KensingtonTech/ng2-panzoom

#angular #angular-js #javascript

Image Pan & Zoom Library For Angular 6+
105.10 GEEK