在60Hz的设备上,屏幕每〜16.67ms刷新一次(我已经将其称为“刷新率和翻转计数”)。对于某些研究,这实际上是相当长的时间。屏幕刷新或开始刷新时,有什么方法可以获取准确的时间戳或事件?
答案 0 :(得分:2)
在iOS上,您可以使用CADisplayLink
:https://developer.apple.com/documentation/quartzcore/cadisplaylink
一个计时器对象,允许您的应用程序将其图形与显示的刷新率同步。
示例:
import Foundation
import UIKit
public class DisplayFrameSync {
private var displayLink: CADisplayLink? = nil
private var frameUpdate: (() -> Void)? = nil
deinit {
self.stop()
}
@objc
private func onTick(displayLink: CADisplayLink) {
if let onFrameUpdate = self.frameUpdate {
onFrameUpdate()
}
}
public func start(_ update: () -> Void) {
self.displayLink?.remove(from: .main, forMode: .default)
self.displayLink?.remove(from: .main, forMode: .tracking)
self.displayLink?.invalidate()
self.displayLink = nil
self.frameUpdate = update
self.displayLink = CADisplayLink(target: self, selector: #selector(onTick(displayLink:)))
self.displayLink?.preferredFramesPerSecond = 60
self.displayLink?.add(to: .main, forMode: .default)
self.displayLink?.add(to: .main, forMode: .tracking)
}
public func stop() {
self.displayLink?.remove(from: .main, forMode: .default)
self.displayLink?.remove(from: .main, forMode: .tracking)
self.displayLink?.invalidate()
self.displayLink = nil
self.frameUpdate = nil
}
}
用法:
let ds = DisplayFrameSync()
ds.start({
print("Frame Update")
});
DispatchQueue.main.asyncAfter(.now() + 2.0) {
ds.stop()
}
使用上述方法,您还可以制作自己的animateWithDuration
并使用Date.timeIntervalSinceReferenceDate
跟踪时间,并从当前时间减去每一帧。
对于Android,您可以使用Choreographer
:
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Choreographer;
private class DisplayFrameSync extends Thread implements Choreographer.FrameCallback {
private volatile Handler signal;
private Consumer<Long> update;
public DisplayFrameSync(Consumer<Long> update) {
super();
this.update = update;
}
@Override
public void run() {
setName("DisplayFrameSyncThread");
Looper.prepare();
signal = new Handler() {
public void handleMessage(Message msg) {
Looper.myLooper().quit();
}
};
Choreographer.getInstance().postFrameCallback(this);
Looper.loop();
Choreographer.getInstance().removeFrameCallback(this);
}
public void signal() {
if (this.signal != null) {
this.signal.sendEmptyMessage(0);
this.signal = null;
}
}
@Override
public void doFrame(long timeNano) {
if (this.update != null) {
this.update.accept(timeNano);
}
Choreographer.getInstance().postFrameCallback(this);
}
}
这已经包含了为您渲染每个帧的时间。
用法:
DisplayFrameSync ds = new DisplayFrameSync((timeNano) -> {
System.out.println("Frame Update");
});
ds.start();
//Later..
ds.stop();