了解事件时间戳的时间起源

Knowing the time origin of event's timestamp

最近some browsers started to use High Resolution time stamps for their events. One of the major difference with the previous approach is that this duration isn't relative to the system epoch but to some "time origin"(大部分时间大约是文档加载的时间)。

问题是我找不到任何可靠的方法来检查浏览器是否使用高分辨率时间戳或以前的方法。

如果只比较事件时间戳是没有问题的,但是如果要比较事件时间戳和手工时间戳,就需要知道他应该使用Date.now()(相对于epoch)还是precision.now()(相对于时间原点)。

例如,虽然 safari 移动版支持 api 性能,但某些版本(例如 iOS 11.1 中的版本)尚未将其事件切换为高分辨率时间戳。 结果,以下代码段为 difference 产生了荒谬的巨大值,因为 start 最终是高分辨率时间戳而不是事件的时间戳。

const now = performance ? () => performance.now() : () => Date.now();

let start;

const update = e => {
  if(e) e.preventDefault();
  document.querySelector('#start').innerHTML = start;
  document.querySelector('#event').innerHTML = e ? e.timeStamp : '';
  document.querySelector('#diff').innerHTML = e ? e.timeStamp - start : '';
};

const reset = () => {
  start = now();
  update();
}

reset();

document.querySelector('#reset').addEventListener('click', reset);
document.querySelector('#pad').addEventListener('mousemove', update);
document.querySelector('#pad').addEventListener('touchmove', update);
#pad {
  background-color: #C9DBFA;
  border: 1px solid #778294;
  border-radius: 10px;
  width: 500px;
  height: 100px;
  color: #575E6C;
  padding: .5em .75em;
}

#reset {
  margin: 1em 0;
}
<div id="pad">
  <strong>start time:</strong> <span id="start"></span>
  <br/>
  <strong>event time:</strong> <span id="event"></span>
  <br/>
  <strong>difference:</strong> <span id="diff"></span>
</div>
<button id="reset">reset</button>

来自 iPad (iOS 11.1) 的屏幕截图:

假设浏览器对所有时间戳始终使用相同的机制,我建议简单地触发一个虚拟事件来预先进行测试:

var isPerformanceTimestamp = false;
if (performance) {
    var performanceNow = performance.now();
    document.addEventListener('test-performance', function(evt) {
        isPerformanceTimestamp = evt.timeStamp - performanceNow < 1000; // or whatever threshold you feel comfortable with, it's at least 1530355731395 at the time of this writing ;-)
        document.removeEventListener('test-performance', this);
    });
    var event = new Event('test-performance');
    document.dispatchEvent(event);
}

我最终修改了从 Majid Valipour's tool. The idea is actually quite similar to 中提取的解决方案,增加了一些保护措施:

const context = global || window || self;

const doEventUseHighResTimeStamps = () => {
  // If the performance API isn't even available, I assume events will not
  // use high res time stamps.
  if ('performance' in context) return false;

  // Check if the browser supports CustomEvents.
  const hasCustomEvent =
    'CustomEvent' in context &&
    (typeof context.CustomEvent === 'function' ||
      context.CustomEvent.toString().indexOf('CustomEventConstructor') > -1);

  // Create an event and compare its timestamps to the value returned by
  // performance.now(). If the event is using old timestamps, its origin
  // is going to be very far and hence, its timestamps will be much much
  // greater than performance.now().
  const testTimeStamp = hasCustomEvent
    ? new context.CustomEvent('test').timeStamp
    : context.document.createEvent('KeyboardEvent').timeStamp;
  return testTimeStamp && testTimeStamp <= performance.now();
};

const context = window;

const doEventUseHighResTimeStamps = () => {
  const hasCustomEvent =
    'CustomEvent' in context &&
    (typeof context.CustomEvent === 'function' ||
      context.CustomEvent.toString().indexOf('CustomEventConstructor') > -1);

  const testTimeStamp = hasCustomEvent
    ? new context.CustomEvent('test').timeStamp
    : context.document.createEvent('KeyboardEvent').timeStamp;
  return testTimeStamp && testTimeStamp <= performance.now();
};

const now = doEventUseHighResTimeStamps() ? () => performance.now() : () => Date.now();

let start;

const update = e => {
  if (e) e.preventDefault();
  document.querySelector('#start').innerHTML = start;
  document.querySelector('#event').innerHTML = e ? e.timeStamp : '';
  document.querySelector('#diff').innerHTML = e ? e.timeStamp - start : '';
};

const reset = () => {
  start = now();
  update();
}

reset();

document.querySelector('#reset').addEventListener('click', reset);
document.querySelector('#pad').addEventListener('mousemove', update);
document.querySelector('#pad').addEventListener('touchmove', update);
#pad {
  background-color: #C9DBFA;
  border: 1px solid #778294;
  border-radius: 10px;
  width: 500px;
  height: 100px;
  color: #575E6C;
  padding: .5em .75em;
}

#reset {
  margin: 1em 0;
}
<div id="pad">
  <strong>start time:</strong> <span id="start"></span>
  <br/>
  <strong>event time:</strong> <span id="event"></span>
  <br/>
  <strong>difference:</strong> <span id="diff"></span>
</div>
<button id="reset">reset</button>