Hello everybody, I share this in hope that you don’t waste as much time as I did on this, it still needs one more bug to fix but more on that below.

### What is this?

I have created a function that enables pinch to zoom using ionics **Gesture** import, for the **Pan and pinch** events to a div container with **Vertical** elements. Since I have googled this a lot a couldn’t find an answer that would let me do this without the content being an image, I found that Ionic actually uses HammerJS, to grab the touch gestures, and wraps them on the “Gesture” import as seen Here

### Where can I test it?

https://github.com/p-sebastian/ionic2-pinchzoom

### The Magic

There are 3 files needed

- ~/src/pages/home/home.ts
- ~/src/pages/home/home.scss
- ~/src/pages/home/home.html

### home.html

The `<div #zoom class="zoom">`

element is the **fixed container** that where we’ll attach the events to, and its **children** are the items that will be zoomed.

### home.scss

`.zoom`

to fill the container size, with a fixed position and the important `touch-action: none`

which HammerJS needs. I have added the border color red, to show where the boundaries of each children are, so that we can see it doesn’t overflow.

### home.ts

```
/*
* @Author: Sebastian Penafiel Torres
* @Date: 2017-04-23 19:25:39
* @Last Modified by: Sebastian Penafiel Torres
* @Last Modified time: 2017-04-23 19:25:39
*/
import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavController, Gesture, Content } from 'ionic-angular';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
@ViewChild(Content) content: Content;
@ViewChild('zoom') zoom: ElementRef;
constructor(public navCtrl: NavController) {
}
ionViewDidEnter(): void {
// Page must be fully rendered, ionViewDidLoad, doesnt work for this. Because it shows clientHeight without the margin of the header
this._pinchZoom(this.zoom.nativeElement, this.content);
}
private _pinchZoom(elm: HTMLElement, content: Content): void {
const _gesture = new Gesture(elm);
// max translate x = (container_width - element absolute_width)px
// max translate y = (container_height - element absolute_height)px
let ow = 0;
let oh = 0;
for (let i = 0; i < elm.children.length; i++) {
let c = <HTMLElement>elm.children.item(i);
ow = c.offsetWidth;
oh += c.offsetHeight;
}
const original_x = content.contentWidth - ow;
const original_y = content.contentHeight - oh;
let max_x = original_x;
let max_y = original_y;
let min_x = 0;
let min_y = 0;
let x = 0;
let y = 0;
let last_x = 0;
let last_y = 0;
let scale = 1;
let base = scale;
_gesture.listen();
_gesture.on('pan', onPan);
_gesture.on('panend', onPanend);
_gesture.on('pancancel', onPanend);
// _gesture.on('tap', onTap);
_gesture.on('pinch', onPinch);
_gesture.on('pinchend', onPinchend);
_gesture.on('pinchcancel', onPinchend);
function onPan(ev) {
setCoor(ev.deltaX, ev.deltaY);
transform();
}
function onPanend() {
// remembers previous position to continue panning.
last_x = x;
last_y = y;
}
function onTap(ev) {
if (ev.tapCount === 2) {
let reset = false;
scale += .5;
if (scale > 2) {
scale = 1;
reset = true;
}
setBounds();
reset ? transform(max_x/2, max_y/2) : transform();
}
}
function onPinch(ev) {
// formula to append scale to new scale
scale = base + (ev.scale * scale - scale)/scale
setBounds();
transform();
}
function onPinchend(ev) {
if (scale > 4) {
scale = 4;
}
if (scale < 0.5) {
scale = 0.5;
}
// lets pinch know where the new base will start
base = scale;
setBounds();
transform();
}
function setBounds() {
// I am scaling the container not the elements
// since container is fixed, the container scales from the middle, while the
// content scales down and right, with the top and left of the container as boundaries
// scaled = absolute width * scale - already set width divided by 2;
let scaled_x = Math.ceil((elm.offsetWidth * scale - elm.offsetWidth) / 2);
let scaled_y = Math.ceil((elm.offsetHeight * scale - elm.offsetHeight) / 2);
// for max_x && max_y; adds the value relevant to their overflowed size
let overflow_x = Math.ceil(original_x * scale - original_x); // returns negative
let overflow_y = Math.ceil(oh * scale - oh);
max_x = original_x - scaled_x + overflow_x;
min_x = 0 + scaled_x;
// remove added height from container
max_y = original_y + scaled_y - overflow_y;
min_y = 0 + scaled_y;
setCoor(-scaled_x, scaled_y);
console.info(`x: ${x}, scaled_x: ${scaled_x}, y: ${y}, scaled_y: ${scaled_y}`)
}
function setCoor(xx: number, yy: number) {
x = Math.min(Math.max((last_x + xx), max_x), min_x);
y = Math.min(Math.max((last_y + yy), max_y), min_y);
}
// xx && yy are for resetting the position when the scale return to 1.
function transform(xx?: number, yy?: number) {
elm.style.webkitTransform = `translate3d(${xx || x}px, ${yy || y}px, 0) scale3d(${scale}, ${scale}, 1)`;
}
}
}
```

It takes the **Content** to figure out the actual size without the footer and headers getting in the way, the for loop just adds the absolute height of each children of #zoom. The original sizes equal to the viewport that the user sees minus the actual size of the content, since we override the scroll, the viewing of the content which will most likely overflow the bottom and right (because its fixed and can be any size), will be done via **panning** so we must know how much its overflowing.

the **max sizes** to pan are the original overflowed size, for now before zooming, and **scale** will equal to 1 at first.

**base** is to remember where we left off when pinched to zoom.

We initiate the event listening individually*, **panend & pancancel** as well as **pinchend & pinchcancel** are each called depending on the timing when you release the fingers so both must be added, to call their respective onEnd function.

#### setCoor function

sets the **x & y** coordinates making sure that it doesn’t go past it’s **max**

#### transform function

moves “translate”, and scales the #zoom element, the **xx & yy** are for resetting the position

#### onPinchend function

sets the **base**, and sets the limits of the **scale**

#### onPinch function

sets the scale depending where it left off.

#### setBounds function

// hard to explain.

### TO-DO

The part that I am missing is that it zooms to the previous x & y coordinates, I need it to zoom to the center of the pinch.

Also to take account of negative margins the children might have.

Hope this helps somebody.