设备事件

# Geolocation(地理位置)

Geolocation 接口用于获取用户的地理位置。它使用的方法基于 GPS 或者其他机制(比如 IP 地址、Wifi 热点、手机基站等)。下面的方法,可以检查浏览器是否支持这个接口。

if (navigator.geolocation) {
  // 支持
} else {
  // 不支持
}

## getCurrentPosition:获取当前位置

getCurrentPosition 方法,用来获取用户的地理位置。使用它需要得到用户的授权,浏览器会跳出一个对话框,询问用户是否许可当前页面获取他的地理位置。必须考虑两种情况的回调函数:一种是同意授权,另一种是拒绝授权。如果用户拒绝授权,会抛出一个错误。

navigator.geolocation.getCurrentPosition(geoSuccess,geoError);

上面代码指定了处理当前地理位置的两个回调函数。

  • 用户同意授权

如果用户同意授权,就会调用 geoSuccess。

function geoSuccess(event) { 
   console.log(event.coords.latitude + ', ' + event.coords.longitude);
}

geoSuccess 的参数是一个 event 对象。event 有两个属性:timestamp 和 coords。timestamp 属性是一个时间戳,返回获得位置信息的具体时间。coords 属性指向一个对象,包含了用户的位置信息,主要是以下几个值:

coords.latitude:纬度 - coords.longitude:经度 - coords.accuracy:精度 - coords.altitude:海拔 - coords.altitudeAccuracy:海拔精度(单位:米) - coords.heading:以 360 度表示的方向 - coords.speed:每秒的速度(单位:米)

大多数桌面浏览器不提供上面列表的后四个值。

  • 用户拒绝授权

如果用户拒绝授权,就会调用 getCurrentPosition 方法指定的第二个回调函数 geoError。

function geoError(event) { 
   console.log("Error code " + event.code + ". " + event.message);
}

geoError 的参数也是一个 event 对象。event.code 属性表示错误类型,有四个值:

0:未知错误,浏览器没有提示出错的原因,相当于常量 event.UNKNOWN_ERROR。 - 1:用户拒绝授权,相当于常量 event.PERMISSION_DENIED。 - 2:没有得到位置,GPS 或其他定位机制无法定位,相当于常量 event.POSITION_UNAVAILABLE。 - 3:超时,GPS 没有在指定时间内返回结果,相当于常量 event.TIMEOUT。

  • 设置定位

getCurrentPosition 方法还可以接受一个对象作为第三个参数,用来设置定位行为。

var option = {
            enableHighAccuracy : true,
            timeout : Infinity,
            maximumAge : 0
        };

navigator.geolocation.getCurrentPosition(geoSuccess, geoError, option);

这个参数对象有三个成员:

enableHighAccuracy:如果设为 true,就要求客户端提供更精确的位置信息,这会导致更长的定位时间和更大的耗电,默认设为 false。 - Timeout:等待客户端做出回应的最大毫秒数,默认值为 Infinity(无限)。 - maximumAge:客户端可以使用缓存数据的最大毫秒数。如果设为 0,客户端不读取缓存;如果设为 infinity,客户端只读取缓存。

## watchPosition&clearWatch:持续监听 watchPosition 方法可以用来监听用户位置的持续改变,使用方法与 getCurrentPosition 方法一样。

var watchID = navigator.geolocation.watchPosition(geoSuccess,geoError, option);

一旦用户位置发生变化,就会调用回调函数 geoSuccess。这个回调函数的事件对象,也包含 timestamp 和 coords 属性。watchPosition 和 getCurrentPosition 方法的不同之处在于,前者返回一个表示符,后者什么都不返回。watchPosition 方法返回的标识符,用于供 clearWatch 方法取消监听。

navigator.geolocation.clearWatch(watchID);

# Device Status:设备状态

## Battery API:电池状态 这是一个针对移动设备应用程序的 API,主要用于检测设备电池信息。现在许多移动端 apps 都内嵌着 web 浏览器包装的(不再是完全 native 的应用)。所以我们需要一个方法去获取系统的信息,app 有一些过程是``十分耗电的,然后我们就需要在用户启动时给用户一些警告信息`告诉用户当前设备电量较低。这是一个十分重要简单的 api.相应在不久的将来会发挥应有的作用。

var battery = navigator.battery || navigator.webkitBattery || navigator.mozBattery;  

// 电池属性  
console.warn("Battery charging: ", battery.charging); // 当前电池是否在充电 true  
console.warn("Battery level: ", battery.level); // 0.58  
console.warn("Battery discharging time: ", battery.dischargingTime);  

// 添加事件监听器  
battery.addEventListener("chargingchange", function(e) {  
  console.warn("Battery charge change: ", battery.charging);  
}, false);

## Orientation API(方向检测)

Orientation API 用于检测手机的摆放方向(竖放或横放),使用下面的代码检测浏览器是否支持该 API:

if (window.DeviceOrientationEvent) {
  // 支持
} else {
  // 不支持
}

一旦设备的方向发生变化,会触发 deviceorientation 事件,可以对该事件指定回调函数。

window.addEventListener("deviceorientation", callback);

回调函数接受一个 event 对象作为参数。

function callback(event){
    console.log(event.alpha);
    console.log(event.beta);
    console.log(event.gamma);
}

上面代码中,event 事件对象有 alpha、beta 和 gamma 三个属性,它们分别对应手机摆放的三维倾角变化。要理解它们,就要理解手机的方向模型。当手机水平摆放时,使用三个轴标示它的空间位置:x 轴代表横轴、y 轴代表竖轴、z 轴代表垂直轴。event 对象的三个属性就对应这三根轴的旋转角度。

- alpha:表示围绕 z 轴的旋转,从 0 到 360 度。当设备水平摆放时,顶部指向地球的北极,alpha 此时为 0。 - beta:表示围绕 x 轴的旋转,从-180 度到 180 度。当设备水平摆放时,beta 此时为 0。 - gramma:表示围绕 y 轴的选择,从-90 到 90 度。当设备水平摆放时,gramma 此时为 0。

# User Perception:用户感知

## Vibration API:设备振动

Vibration 接口用于在浏览器中发出命令,使得设备振动。显然,这个 API 主要针对手机,适用场合是向用户发出提示或警告,游戏中尤其会大量使用。由于振动操作很耗电,在低电量时最好取消该操作。使用下面的代码检查该接口是否可用。目前,只有 Chrome 和 Firefox 的 Android 平台最新版本支持它。

navigator.vibrate =
  navigator.vibrate ||
  navigator.webkitVibrate ||
  navigator.mozVibrate ||
  navigator.msVibrate;

if (navigator.vibrate) {
  // 支持
}

vibrate 方法可以使得设备振动,它的参数就是振动持续的毫秒数。

navigator.vibrate(1000);

上面的代码使得设备振动 1 秒钟。vibrate 方法还可以接受一个数组作为参数,表示振动的模式。偶数位置的数组成员表示振动的毫秒数,奇数位置的数组成员表示等待的毫秒数。

navigator.vibrate([500, 300, 100]);

上面代码表示,设备先振动 500 毫秒,然后等待 300 毫秒,再接着振动 500 毫秒。vibrate 是一个非阻塞式的操作,即手机振动的同时,JavaScript 代码继续向下运行。要停止振动,只有将 0 毫秒或者一个空数组传入 vibrate 方法。

navigator.vibrate(0);
navigator.vibrate([]);

如果要让振动一直持续,可以使用 setInterval 不断调用 vibrate。

var vibrateInterval;

function startVibrate(duration) {
  navigator.vibrate(duration);
}

function stopVibrate() {
  if (vibrateInterval) clearInterval(vibrateInterval);
  navigator.vibrate(0);
}

function startPeristentVibrate(duration, interval) {
  vibrateInterval = setInterval(function() {
    startVibrate(duration);
  }, interval);
}

## Luminosity API(亮度调节) Luminosity API 用于屏幕亮度调节,当移动设备的亮度传感器感知外部亮度发生显著变化时,会触发 devicelight 事件。目前,只有 Firefox 部署了这个 API。

window.addEventListener('devicelight', function(event) {
  console.log(event.value + 'lux');
});

上面代码表示,devicelight 事件的回调函数,接受一个事件对象作为参数。该对象的 value 属性就是亮度的流明值。这个 API 的一种应用是,如果亮度变强,网页可以显示黑底白字,如果亮度变弱,网页可以显示白底黑字。

window.addEventListener("devicelight", function(e) {
  var lux = e.value;

  if (lux < 50) {
    document.body.className = "dim";
  }
  if (lux >= 50 && lux <= 1000) {
    document.body.className = "normal";
  }
  if (lux > 1000) {
    document.body.className = "bright";
  }
});

CSS 下一个版本的 Media Query 可以单独设置亮度,一旦浏览器支持,就可以用来取代 Luminosity API。

@media (light-level: dim) {
  /* 暗光环境 */
}

@media (light-level: normal) {
  /* 正常光环境 */
}

@media (light-level: washed) {
  /* 明亮环境 */
}

Touch & Tap

Touch Events

从 Mobile 的角度来看,Touch 才是最基本的事件,就像 React 官方库在Issue 436中所描述的,React 官方只提供了onTouchStart等 4 个与 Touch 相关的事件,而推荐是采用<Tappable onTap={}>的方式包裹一层监听组件,从而避免核心库的冗余。 |

这些事件可以被附着到页面的任意元素上,并且同样可以通过 Event 对象传递给处理函数,可以包含触碰点的位置信息等。可以使用element.addEventListener来添加事件响应函数,譬如:

window.addEventListener(
  "load",
  function() {
    // on page load
    document.body.addEventListener(
      "touchstart",
      function(e) {
        alert(e.changedTouches[0].pageX); // alert pageX coordinate of touch point
      },
      false
    );
  },
  false
);

Tap & 300ms:Tap 事件与 300ms 的延迟

绝大部分基于 Touch 的移动端浏览器都会在你轻按屏幕 300ms 之后再触发对应的事件,这是为了判断你是否是 Double-Tapping 来缩放界面。如果浏览器等待一段时间之后,你不再 Tap,那么就会触发 Click 事件。这一设定对于响应式界面与多点触摸的界面是个很好的阈值设置,不过有时候会让用户觉得 Web 应用响应的如此缓慢,压根和原生应用不能比。譬如在一个纯阅读的网站中,用户并不是很关心触摸控制,只是想单纯的点个菜单然后进入具体的文章进行浏览,在这种情况下我们就要防止这 300ms 的延迟。 (1)在 Chrome 或者 Firefox 中禁止缩放

Android 版本中的 Chrome 与 Firefox 在用户禁止缩放的情况下会自动不再等待 300ms,可以在 HTMLhead中进行如下设置:

<meta name="viewport" content="width=device-width, user-scalable=no">

或者

<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">

不过这种方式在 Safar(iOS 的 WebView)上行不通,有时候甚至导致元素无法点击访问。 (2)Chrome 版本 32 以上设置 Viewpoint 为 device-width 在 Chrome 32 或者以上版本中,将 viewpoint 设置为任何小于或者等于 device-width 的尺寸会禁止掉缩放,譬如:

<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=3"

(3)使用 PointerEvents(IE 10+)

a, button, .myelements
{
	-ms-touch-action: manipulation;	/* IE10*/
	touch-action: manipulation;		/* IE11+ */
}

(4)自定义 Touchend Event Handlers 不同于clicktouchstarttouchend事件是不会延迟 300ms 触发的,这一点在与 WebGLS 或者 Canvas 的游戏中非常适用。不过你并不能完全依赖于touchend,有如下原因:

  • 如果用户在不同的元素上触发了touchstarttouchend事件,那么不应该触发 Click 事件。

  • 如果某个用户在触发了touchstart事件之后,因为有较大移动在touchend之前触发了touchmove,这个时候就不能触发 Click 事件。

  • 对于非手持设备同样需要处理点击事件,还需要注意不能对于同一事件触发两次

  • FastClick from FT Labs attaches appropriate listeners on mobile browsers when they’re required, i.e. solutions 2 to 4 above have not been used. The script compresses to around 10Kb.

  • Tappy! from the Filament Group is a normalized tap event library, similar in concept to PointerEvents, which compresses to 1Kb.

# Gesture Recognize Types

Multiple Touch

Drag&Drop

Scroll

Zoom

Shake

Hover

Hover on Touch

当你点住某个元素的时候会自动隐藏原本元素并且显示第二个元素。如果你在 250ms 之内放手那么会自动触发点击事件并且自动跳转到对应的响应。而如果你按住了更长的时间,那么触发的是Taphold事件,悬浮层就会自动显示出来,即如上图所示。 (1)Installation

<script src="http://hammerjs.github.io/dist/hammer.min.js"></script>
<script src="your/path/hoverontouch.js"></script>

(2)HTML Structure

<div class="object" data-triggerlink="https://www.example.com">
  <div class="cover">
    // Put the cover Informaton here
  </div>
  <div class="info">
    //Put the secondary Information here
  </div>
</div>
上一页
下一页