Ar.js: horizontal scrolling on iOS

Created on 24 Apr 2019  路  2Comments  路  Source: jeromeetienne/AR.js

Describe the bug
On iOS (tested 11 and 12) a horizontal swipe will scroll the camera-view out of of the screen. This is happening because the body and video tag are wider than 100% of the view-port. The size of them are calculated within AR.js.
Additionally newer iOS versions ignore any prevention of user-scrolling (e.g. meta tag viewport and css overflow).

To Reproduce
Steps to reproduce the behavior:

  1. Open any AR.js example on a newer iOS device
  2. allow access to the camera
  3. swipe to the left
  4. See full-screen camera image scrolling out of screen boundaries

Expected behavior
Full-screen camera should stay full-screen, no horizontal scrolling should occur

Screenshot (after swiping/scrolling)
image00001

Smartphone (please complete the following information):

  • Device: iPhone6 or higher / iPad
  • OS: iOS 12
  • Browser: Safari

Additional context
While Apple defends their decision to ignore scrolling/zooming prevention as a accessibility/usability feature it leaves us web-app developers no good way to implement full-screen rotation/zoom gestures.

Most helpful comment

In the meantime I use the following workaround:

prevent scrolling

  • Use CSS property overflow: hidden to prevent scrolling on web-standard-conform browsers:
html, body {
    -webkit-text-size-adjust: none;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
  • To prevent scrolling on iOS Safari I use Body scroll lock by @willmcpo. This has the ability to allow scrolling for certain elements, but since I'm going to implement my touch-gestures and their effects by myself I don't make use of it:
bodyScrollLock.disableBodyScroll(document.getElementById("not-used"));

Please notice, that for some reason I had to put an html-element as the first parameter to make the script work. In my case this is just an empty <div id="not-used" />

prevent zooming

  • Use <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1, shrink-to-fit=no,user-scalable=no,minimal-ui,viewport-fit=cover"> to prevent zooming on web-standard-conform browsers.
  • Based on Danny Yaroslavski's solution to prevent zooming on iOS Safari, I catch all touchmove-events, which essentially prevents any touch gesture before it reaches the website. Therefore you need to handle all touch gestures yourself (which we do, as described before in section "prevent scrolling"). Add this script to your root-page:
document.addEventListener("touchmove",
  function(e) {
    e.preventDefault();
  }, { passive: false }
);
  • To also prevent zooming on iOS Safari by double-tapping, you can put touch-action: manipulation to the <body> css-style. Also you might want to set the -webkit-tap-highlight-color to transparent:
body {
    touch-action: manipulation;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

re-activate scrolling for certain elements

Normally you should be able to use Body scroll lock's first parameter to achieve that.
In my case I wanted to enable touch-based-scrolling in an overlay ui, but since I locked scrolling and on top of that captured all touch-events I needed to implement touch-based-scrolling by myself:

  const ui = document.getElementById("ui-content"); // find ui div
  const mc = new Hammer(ui); // init hammer.js
  // get all 'pan' gestures with vertical direction
  mc.get("pan").set({ direction: Hammer.DIRECTION_VERTICAL, threshold: 2 });

  // whenever the screen get's panned vertically on our ui (e.g. the user want's to scroll) ...
  mc.on("panmove", function(ev) {
    switch (ev.direction) { // find the direction of panning
      case 8: // down
        ui.scrollBy(0, 10); // scroll down the ui div by 10 pixels (might need to adjust on different devices)
        break;
      case 16:
        ui.scrollBy(0, -10); // scroll up the ui div by 10 pixels
        break;
      default:
        break;
    }
  });

All 2 comments

In the meantime I use the following workaround:

prevent scrolling

  • Use CSS property overflow: hidden to prevent scrolling on web-standard-conform browsers:
html, body {
    -webkit-text-size-adjust: none;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
  • To prevent scrolling on iOS Safari I use Body scroll lock by @willmcpo. This has the ability to allow scrolling for certain elements, but since I'm going to implement my touch-gestures and their effects by myself I don't make use of it:
bodyScrollLock.disableBodyScroll(document.getElementById("not-used"));

Please notice, that for some reason I had to put an html-element as the first parameter to make the script work. In my case this is just an empty <div id="not-used" />

prevent zooming

  • Use <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1, shrink-to-fit=no,user-scalable=no,minimal-ui,viewport-fit=cover"> to prevent zooming on web-standard-conform browsers.
  • Based on Danny Yaroslavski's solution to prevent zooming on iOS Safari, I catch all touchmove-events, which essentially prevents any touch gesture before it reaches the website. Therefore you need to handle all touch gestures yourself (which we do, as described before in section "prevent scrolling"). Add this script to your root-page:
document.addEventListener("touchmove",
  function(e) {
    e.preventDefault();
  }, { passive: false }
);
  • To also prevent zooming on iOS Safari by double-tapping, you can put touch-action: manipulation to the <body> css-style. Also you might want to set the -webkit-tap-highlight-color to transparent:
body {
    touch-action: manipulation;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

re-activate scrolling for certain elements

Normally you should be able to use Body scroll lock's first parameter to achieve that.
In my case I wanted to enable touch-based-scrolling in an overlay ui, but since I locked scrolling and on top of that captured all touch-events I needed to implement touch-based-scrolling by myself:

  const ui = document.getElementById("ui-content"); // find ui div
  const mc = new Hammer(ui); // init hammer.js
  // get all 'pan' gestures with vertical direction
  mc.get("pan").set({ direction: Hammer.DIRECTION_VERTICAL, threshold: 2 });

  // whenever the screen get's panned vertically on our ui (e.g. the user want's to scroll) ...
  mc.on("panmove", function(ev) {
    switch (ev.direction) { // find the direction of panning
      case 8: // down
        ui.scrollBy(0, 10); // scroll down the ui div by 10 pixels (might need to adjust on different devices)
        break;
      case 16:
        ui.scrollBy(0, -10); // scroll up the ui div by 10 pixels
        break;
      default:
        break;
    }
  });

I could not get body-scroll-lock working reliably, and ended up finding an alternative method.

First, remove embedded from the a-scene element. Then add this ridiculously hacky bit of CSS to override some aframe craziness:

// This is necessary to override this aframe monstrosity
// https://github.com/aframevr/aframe/blob/master/src/style/aframe.css#L19
html.a-fullscreen .a-canvas.a-canvas {
    width: auto !important;
    left: 50% !important;
    transform: translateX(-50%);
}

You will still need to apply the above fixes to prevent zooming.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

pvnr0082t picture pvnr0082t  路  4Comments

usama-ghufran picture usama-ghufran  路  5Comments

vijayrajasekaran picture vijayrajasekaran  路  6Comments

whilemouse picture whilemouse  路  3Comments

Rawphs picture Rawphs  路  6Comments