原文地址: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(免費版)翻譯