原文地址:medium.com/icnh/flutte…canvas
原文做者:medium.com/@eibaan_546…windows
发布时间:2020年5月2日 - 3分钟阅读框架
这是一个实验。让咱们在不使用Flutter框架的状况下,建立在移动设备上显示一些东西所需的最小代码。dom
不美不美,自成一派ide
Flutter是一个高级GUI框架,它使用dart:ui
(Skia引擎的一个抽象)来实际显示一些东西,并与这些东西所显示的平台进行交互。固然,咱们本身也能够直接使用这个低级别的dart:ui
库。函数
让咱们建立一个新的Flutter项目。ui
$ flutter create flutter_without_flutter
复制代码
而后用这段代码替换lib/main.dart
。spa
import ‘dart:ui’;
void main() {
window.onBeginFrame = beginFrame;
window.scheduleFrame();
}
void beginFrame(Duration duration) {
}
复制代码
解释:咱们用window
单例注册一个名为beginFrame
的全局函数,而后要求图形引擎对该函数进行回调。它应该准备并最终绘制一个帧,也就是在设备屏幕上显示一些东西。翻译
由于我习惯用逻辑单位而不是物理像素来工做,因此 beginFrame
的第一步是将设备的物理屏幕尺寸转换为更熟悉的数值。code
void beginFame(Duration duration) {
final pixelRatio = window.devicePixelRatio;
final size = window.physicalSize / pixelRatio;
final physicalBounds = Offset.zero & size * pixelRatio;
…
复制代码
下一步是设置一个使用逻辑单元的Canvas
。
…
final recorder = PictureRecorder();
final canvas = Canvas(recorder, physicalBounds);
canvas.scale(pixelRatio, pixelRatio);
…
复制代码
而后让咱们用红色的颜料画一个圆圈。
…
final paint = Paint()..color = Color(0xFFF44336);
final center = size.center(Offset.zero);
canvas.drawCircle(center, size.shortestSide / 4, paint);
…
复制代码
最后一步是将调用Canvas
方法建立的录音,用它来构建一个所谓的场景,而后由windows
单例渲染。
…
final picture = recorder.endRecording();
final sceneBuilder = SceneBuilder()
..pushClipRect(physicalBounds)
..addPicture(Offset.zero, picture)
..pop();
window.render(sceneBuilder.build());
}
复制代码
顺便说一下,一般的Flutter项目的即时热代码重载仍然有效,这就是为何它可能会有用,使用这种设置来建立低水平的图形应用程序,例如2D游戏。
为了移动圆圈并使其在屏幕上弹跳,咱们将把它的中心点存储在一个全局变量(称为center
)中,把它的速度存储在另外一个全局变量(称为velocity
)中,每次调用beginFrame
时修改这些变量,而后在beginFrame
函数结束时使用scheduleFrame
请求另外一次绘制操做。
下面是相关的修改。
Offset center;
Offset velocity;
void beginFrame(Duration duration) {
…
canvas.scale(pixelRatio, pixelRatio);
final radius = size.shortestSide / 4;
if (center == null) {
center = size.center(Offset.zero);
velocity = Offset(3, 5);
} else {
if (center.dx < radius || center.dx > size.width — radius)
velocity = velocity.scale(-1, 1);
if (center.dy < radius || center.dy > size.height — radius)
velocity = velocity.scale(1, -1);
center += velocity;
}
final paint = Paint()..color = Color(0xFFF44336);
canvas.drawCircle(center, radius, paint);
…
window.scheduleFrame();
}
复制代码
由于咱们没法控制beginFrame
函数的调度频率,因此咱们可能应该利用传递的duration
,并将圆的速度与传递的时间绑定。为了计算调用之间所通过的时间,咱们引入另外一个全局变量,叫作lastDuration
。
Duration lastDuration;
void beginFrame(Duration duration) {
…
if (center == null) {
…
} else {
…
final delta = (duration — lastDuration).inMilliseconds / 1000;
center += velocity * delta;
}
lastDuration = duration;
…
复制代码
若是你保持应用程序的运行,只是修改了soure代码,圆圈应该已经开始移动,就像魔法同样。
最后但一样重要的是,让咱们在每次点击屏幕时改变速度。为了检测触摸,咱们须要添加另外一个回调函数onPointerDataPacket
。这个函数接收一个PointerData
对象的列表,这些对象描述了触摸向下、触摸移动和触摸向上的事件。不过咱们只对 PointerChange.up
类型的事件感兴趣。
这里是新的主函数。
void main() {
window.onBeginFrame = beginFrame;
window.onPointerDataPacket = pointerDataPacket;
window.scheduleFrame();
}
复制代码
这里是新的回调函数。
void pointerDataPacket(PointerDataPacket packet) {
for (final data in packet.data) {
if (data.change == PointerChange.up) {
velocity = Offset.fromDirection(
_random.nextDouble() * pi * 2,
_random.nextDouble() * 800–400,
);
}
}
}
final _random = Random();
复制代码
利用这个基础,你应该能够从头开始建立本身的相似Flutter的GUI框架。
我将此做为一个练习留给读者:-)
经过www.DeepL.com/Translator(免费版)翻译