亚洲AVI,黑人巨茎大战欧美白妇,初高中生洗澡自慰高清网站,欧美日韩无砖专区一中文字

重慶分公司,新征程啟航

為企業(yè)提供網(wǎng)站建設(shè)、域名注冊、服務(wù)器等服務(wù)

flutter畫幾何,flutter繪制曲線

Flutter浪潮下的音視頻研發(fā)探索

文/陳爐軍

昭蘇網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、自適應(yīng)網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。成都創(chuàng)新互聯(lián)從2013年開始到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)。

整理/LiveVideoStack

大家好,我是阿里巴巴閑魚事業(yè)部的陳爐軍,本次分享的主題是Flutter浪潮下的音視頻研發(fā)探索,主要內(nèi)容是針對閑魚APP在當(dāng)下流行的跨平臺框架Flutter的大規(guī)模實(shí)踐,介紹其在音視頻領(lǐng)域碰到的一些困難以及解決方案。

分享內(nèi)容主要分為四個方面,首先會對Flutter有一個簡單介紹以及選擇Flutter作為跨平臺框架的原因,其次會介紹Flutter中與音視頻關(guān)系非常大的外接紋理概念,以及對它做出的一些優(yōu)化。之后會對閑魚在音視頻實(shí)踐過程中碰到的一些Flutter問題提出了一些解決方案——TPM音視頻框架。最后是閑魚Flutter多媒體開源組件的介紹。

Flutter

Flutter是一個跨平臺框架,以往的做法是將音頻、視頻和網(wǎng)絡(luò)這些模塊都下沉到C++層或者ARM層,在其上封裝成一個音視頻的SDK,供UI層的PC、iOS和Android調(diào)用。

而Flutter做為一個UI層的跨平臺框架,顧名思義就是在UI層也實(shí)現(xiàn)了一個跨平臺開發(fā)??梢灶A(yù)想的是未Flutter發(fā)展的好的話,會逐漸變?yōu)橐粋€從底層到UI層的一個全鏈路的跨平臺開發(fā),技術(shù)人員分別負(fù)責(zé)SDK和UI層的開發(fā)。

在Flutter之前已經(jīng)有很多跨平臺UI解決方案,那為什么選擇Flutter呢?

我們主要考慮性能和跨平臺的能力。

以往的跨平臺方案比如Weex,ReactNative,Cordova等等因?yàn)榧軜?gòu)的原因無法滿足性能要求,尤其是在音視頻這種性能要求幾乎苛刻的場景。

而諸如Xamarin等,雖然性能可以和原生App一致,但是大部分邏輯還是需要分平臺實(shí)現(xiàn)。

我們可以看一下,為什么Flutter可以實(shí)現(xiàn)高性能:

原生的native組件渲染以IOS為例,蘋果的UIKit通過調(diào)用平臺自己的繪制框架QuaztCore來實(shí)現(xiàn)UI的繪制,圖形繪制也是調(diào)用底層的API,比如OpenGL、Metal等。

而Flutter也是和原生API邏輯一致,也是通過調(diào)用底層的繪制框架層SKIA實(shí)現(xiàn)UI層。這樣相當(dāng)于Flutter他自己實(shí)現(xiàn)了一套UI框架,提供了一種性能超越原生API的跨平臺可能性。

但是我們說一個框架最終性能怎樣,其實(shí)取決于設(shè)計(jì)者和開發(fā)者。至于現(xiàn)在到底是一個什么狀況:

在閑魚的實(shí)踐中,我們發(fā)現(xiàn)在正常的開發(fā)沒有特意的去優(yōu)化UI代碼的情況下,在一些低端機(jī)上,F(xiàn)lutter界面的流暢性是比Native界面要好的。

雖然現(xiàn)在閑魚某些場景下會有卡頓閃退等情況,但是這是一個新事物發(fā)展過程中的必然問題,我們相信未來性能肯定不會成為限制Flutter發(fā)展的瓶頸的。

在閑魚實(shí)踐Flutter的過程中,混合棧和音視頻是其中比較難解決的兩個問題,混合棧是指一個APP在Flutter過程中不可能一口氣將所有業(yè)務(wù)全部重寫為Flutter,所以這是一個逐步迭代的過程,這期間原生native界面與Flutter界面共存的狀態(tài)就稱之為混合棧。閑魚在混合棧上也有一些比較好的輸出,例如FlutterBoost。

外接紋理

在講音視頻之前需要簡要介紹一下外接紋理的概念,我們將它稱之為是Flutter和Frame之間的橋梁。

Flutter渲染一幀屏幕數(shù)據(jù)首先要做的是,GPU發(fā)出的VC信號在Flutter的UI線程,通過AOT編譯的機(jī)器碼結(jié)合當(dāng)前Dart Runtime,生成Layer Tree UI樹,Layer Tree上每一個葉子節(jié)點(diǎn)都代表了當(dāng)前屏幕上所需要渲染的每一個元素,包含了這些元素渲染所需要的內(nèi)容。將Layer Tree拋給GPU線程,在GPU線程內(nèi)調(diào)用Skia去完成整個UI的渲染過程。Layer Tree中有PictureLayer和TextureLayer兩個比較重要的節(jié)點(diǎn)。PictureLayer主要負(fù)責(zé)屏幕圖片的渲染,F(xiàn)lutter內(nèi)部實(shí)現(xiàn)了一套圖片解碼邏輯,在IO線程將圖片讀取或者從網(wǎng)絡(luò)上拉取之后,通過解碼能夠在IO線程上加載出紋理,交給GPU線程將圖片渲染到屏幕上。但是由于音視頻場景下系統(tǒng)API太過繁多,業(yè)務(wù)場景過于復(fù)雜。Flutter沒有一套邏輯去實(shí)現(xiàn)跨平臺的音視頻組件,所以說Flutter提出了一種讓第三方開發(fā)者來實(shí)現(xiàn)音視頻組件的方式,而這些音視頻組件的視頻渲染出口,就是TextureLayer。

在整個Layer Tree渲染的過程中,TextureLayer的數(shù)據(jù)紋理需要由外部第三方開發(fā)者來指定,可以把視頻數(shù)據(jù)和播放器數(shù)據(jù)送到TextureLayer里,由Flutter將這些數(shù)據(jù)渲染出來。

TextureLayer渲染過程:首先判斷Layer是否已經(jīng)初始化,如果沒有就創(chuàng)建一個Texture,然后將Texture Attach到一個SufaceTexture上。

這個SufaceTexture是音視頻的native代碼可以獲取到的對象,通過這個對象創(chuàng)建的Suface,我們可以將視頻數(shù)據(jù)、攝像頭數(shù)據(jù)解碼放到Suface中,然后Flutter端通過監(jiān)聽SufaceTexture的數(shù)據(jù)更新就可以順利把剛才創(chuàng)建的數(shù)據(jù)更新到它的紋理中,然后再將紋理交給SKIA渲染到屏幕上。

然而我們?nèi)绻枰肍lutter實(shí)現(xiàn)美顏,濾鏡,人臉貼圖等等功能,就需要將視頻數(shù)據(jù)讀取出來,更新到紋理中,再將GPU紋理經(jīng)過美顏濾鏡處理后生成一個處理后的紋理。按Flutter提供的現(xiàn)有能力,必須先將紋理中的數(shù)據(jù)從GPU讀出到CPU中,生成Bitmap后再寫入Surface中,這樣在Flutter中才能順利的更新到視頻數(shù)據(jù),這樣做對系統(tǒng)性能的消耗很大。

通過對Flutter渲染過程分析,我們知道Flutter底層需要渲染的數(shù)據(jù)就是GPU紋理,而我們經(jīng)過美顏濾鏡處理完成以后的結(jié)果也是GPU紋理,如果可以將它直接交給Flutter渲染,那就可以避免GPU-CPU-GPU這樣的無用循環(huán)。這樣的方法是可行的,但是需要一個條件,就是OpenGL上下文共享。

OpenGL

在說上下文之前,得提到一個和上線文息息相關(guān)的概念:線程。

Flutter引擎啟動后會啟動四個線程:

第一個線程是UI線程,這是Flutter自己定義的UI線程,主要負(fù)責(zé)GPU發(fā)出的VSync信號時候用當(dāng)前Dart編譯的機(jī)器碼和當(dāng)前運(yùn)行環(huán)境創(chuàng)建出Layer Tree。

還有就是IO線程和GPU線程。和大部分OpenGL處理解決方案中一樣,F(xiàn)lutter也采取一個線程責(zé)資源加載,一部分負(fù)責(zé)資源渲染這種思路。

兩個線程之間紋理共享有兩種方式。一種是EGLImage(IOS是 CVOpenGLESTextureCache)。一種是OpenGL Share Context。Flutter通過Share Context來實(shí)現(xiàn)紋理共享,將IO線程的Context和GPU線程的Context進(jìn)行Share,放到同一個Share Group下面,這樣兩個線程下資源是互相可見可以共享的。

Platform線程是主線程,F(xiàn)lutter中有一個很奇怪的設(shè)定,GPU線程和主線程共用一個Context。并且在主線程也有很多OpenGL 操作。

這樣的設(shè)計(jì)會給音視頻開發(fā)帶來很多問題,后面會詳細(xì)說。

音視頻端美顏處理完成的OpenGL紋理能夠讓Flutter直接使用的條件就是Flutter的上下文需要和平臺音視頻相關(guān)的OpenGL上下文處在一個Share Group下面。

由于Flutter主線程的Context就是GPU的Context,所以在音視頻端主線程中有一些OpenGL操作的話,很有可能使Flutter整個OpenGL被破壞掉。所以需要將所有的OpenGL操作都限制在子線程中。

通過上述這兩個條件的處理,我們就可以在沒有增加GPU消耗的前提下實(shí)現(xiàn)美顏和濾鏡等等功能。

TPM

在經(jīng)過demo驗(yàn)證之后,我們將這個方案應(yīng)用到閑魚音視頻組件中,但改造過程中發(fā)現(xiàn)了一些問題。

上圖是攝像頭采集數(shù)據(jù)轉(zhuǎn)換為紋理的一段代碼,其中有兩個操作:首先是切進(jìn)程,將后面的OpenGL操作都切到cameraQueue中。然后是設(shè)置一次上下文。然后這種限制條件或者說是潛規(guī)則往往在開發(fā)過程中容易被忽略的。而這個條件一旦忽略后果就是出現(xiàn)一些莫名其妙的詭異問題極難排查。因此我們就希望能抽象出一套框架,由框架本身實(shí)現(xiàn)線程的切換、上下文和模塊生命周期等的管理,開發(fā)者接入框架以后只需要安心實(shí)現(xiàn)自己的算法,而不需要關(guān)心這些潛規(guī)則還有其他一些重復(fù)的邏輯操作。

在引入Flutter之前閑魚的音視頻架構(gòu)與大部分音視頻邏輯一樣采用分層架構(gòu):

1:底層是一些獨(dú)立模塊

2:SDK層是對底層模塊的封裝

3:最上層是UI層。

引入Flutter之后,通過分析各個模塊的使用場景,我們可以得出一個假設(shè)或者說是抽象:音視頻應(yīng)用在終端上可以歸納為視頻幀解碼之后視頻數(shù)據(jù)幀在各個模塊之間流動的過程,基于這種假設(shè)去做Flutter音視頻框架的抽象。

咸魚Flutter多媒體開源組件

整個Flutter音視頻框架抽象分為管線和數(shù)據(jù)的抽象、模塊的抽象、線程統(tǒng)一管理和上下文同一管理四部分。

管線,其實(shí)就是視頻幀流動的管道。數(shù)據(jù),音視頻中涉及到的數(shù)據(jù)包括紋理、Bit Map以及時間戳等。結(jié)合現(xiàn)有的應(yīng)用場景我們定義了管線流通數(shù)據(jù)以Texture為主數(shù)據(jù),同時可以選擇性的添加Bit Map等作為輔助數(shù)據(jù)。這樣的數(shù)據(jù)定義方式,避免重復(fù)的創(chuàng)建和銷毀紋理帶來的性能開銷以及多線程訪問紋理帶來的一些問題。也滿足一些特殊模塊對特殊數(shù)據(jù)的需求。同時也設(shè)計(jì)了紋理池來管理管線中的紋理數(shù)據(jù)。

模塊:如果把管線和數(shù)據(jù)比喻成血管和血液,那框架音視頻的場景就可以比喻成器官,我們根據(jù)模塊所在管線的位置抽象出采集、處理和輸出三個基類。這三個基類里實(shí)現(xiàn)了剛才說的線程切換,上下文切換,格式轉(zhuǎn)換等等共同邏輯,各個功能模塊通過集成自這些基類,可以避免很多重復(fù)勞動。

線程:每一個模塊初始化的時候,初始化函數(shù)就會去線程管理的模塊去獲取自己的線程,線程管理模塊可以決定給初始化函數(shù)分配新的線程或者已經(jīng)分配過其他模塊的線程。

這樣有三個好處:

一是可以根據(jù)需要去決定一個線程可以掛載多少模塊,做到線程間的負(fù)載均衡。第二,多線程并發(fā)式能夠保證模塊內(nèi)的OpenGL操作是在當(dāng)前線程內(nèi)而不會跑到主線程去,徹底避免Flutter的OpenGL 環(huán)境被破壞。第三,多線程并行可以充分利用CPU多核架構(gòu),提升處理速度。

從Flutter端修改Flutter引擎將Context取出后,根據(jù)Context創(chuàng)建上下文的統(tǒng)一管理模塊,每一個模塊在初始化的時候會獲取它的線程,獲取之后會調(diào)用上下文管理模塊獲取自己的上下文。這樣可以保證每一個模塊的上下文都是與Flutter的上下文進(jìn)行Share的,每個模塊之間資源都是共享可見的,F(xiàn)lutter和音視頻native之間也是互相共享可見的。

基于上述框架如果要實(shí)現(xiàn)一個簡單的場景,比如畫面實(shí)時預(yù)覽和濾鏡處理功能,

1:需要選擇功能模塊,功能模塊包括攝像頭模塊、濾鏡處理模塊和Flutter畫面渲染模塊,

2:需要配置模塊參數(shù),比如采集分辨率、濾鏡參數(shù)和前后攝像頭設(shè)置等,

3:在創(chuàng)建視頻管線后使用已配置的參數(shù)創(chuàng)建模塊

4:最后管線搭載模塊,開啟管線就可以實(shí)現(xiàn)這樣簡單的功能。

上圖為整個功能實(shí)現(xiàn)的代碼和結(jié)構(gòu)圖。

結(jié)合上述音視頻框架,閑魚實(shí)現(xiàn)了Flutter多媒體開源組件。

組要包含四個基本組件分別是:

1:視頻圖像拍攝組件

2:播放器組件

3:視頻圖像編輯組件

4:相冊選擇組件

現(xiàn)在這些組件正在走內(nèi)部開源流程。預(yù)計(jì)9月份,相冊和播放器會實(shí)現(xiàn)開源。

后續(xù)展望和規(guī)劃

1:實(shí)現(xiàn)開頭所說的從底層SDK到UI的全鏈路的跨端開發(fā)。目前底層框架層和模塊層都是各個平臺各自實(shí)現(xiàn),反而是Flutter的UI端進(jìn)行了跨平臺的統(tǒng)一,所以后續(xù)會將底層也按照音視頻常用做法把邏輯下沉到C++層,盡可能的實(shí)現(xiàn)全鏈路跨平臺。

2:第二部分內(nèi)容為開源共建,閑魚開源的內(nèi)容不僅包括拍攝、編輯組件,還包括了很多底層模塊,希望有開發(fā)者在基于Flutter開發(fā)音視頻應(yīng)用時可以充分利用閑魚開源出的音視頻模塊能力,搭建APP框架,開發(fā)者只要去負(fù)責(zé)實(shí)現(xiàn)特殊需求模塊就可以,盡可能的減少重復(fù)勞動。

iOS開發(fā)之初識OpenGL

既然有了 Metal 我們是否還有學(xué)習(xí) OpenGL ES 的必要呢.我個人認(rèn)為暫時還是有必要的.OpenGL /OpenGL ES/ Metal 在任何項(xiàng)目中解決問題的本質(zhì)就是利用GPU芯片來?效渲染圖形圖像.所以它們底層的原理相近,首先了解OpenGL之后再去了解Metal會更加容易, 其二OpenGL是跨平臺的框架.保不齊以后會在其他的地方用到,所謂技多不壓身.畢竟不可能保證一直都做蘋果開發(fā)吧.

理論枯燥且乏味, 但是我們聯(lián)想一下flutter開發(fā)中用到的context, iOS開發(fā)中CoreGraphics里也有用到context. 是不是此時心中就有了一定的答案.我個人理解其設(shè)計(jì)模式大同小異. 顧名思義我們能夠通過context拿到很多必要的狀態(tài)和數(shù)據(jù).

比如需要顯示一個正方形, 則需要兩個正等邊三角形圖元來完成

為了讀取效率起見, 提前分配一塊顯存, 將頂點(diǎn)數(shù)組存放在顯存中. 這部分顯存就叫做 頂點(diǎn)緩沖區(qū)

OpenGL渲染的過程中會經(jīng)歷很多節(jié)點(diǎn). 這些節(jié)點(diǎn)串起來就是管線.

常見的著?器主要有: 頂點(diǎn)著?器(VertexShader) , ?段著?器(FragmentShader) , 幾何著?(GeometryShader) , 曲?細(xì)分著?器(TessellationShader)

OpenGL ES 中只?持 頂點(diǎn)著?器 和 片段著?器 .

光柵化就是把頂點(diǎn)數(shù)據(jù)轉(zhuǎn)換為片元的過程。片元中的每一個元素對應(yīng)于幀緩沖區(qū)中的一個像素。

如果把渲染比作是畫畫, 那么頂點(diǎn)著色器操作相當(dāng)與確定畫的內(nèi)容的框架. 而之后往框架里填充內(nèi)容的過程就是光柵化.

填充好內(nèi)容之后就是片元著色器操作像素點(diǎn)填充顏色等操作

這里附上一張流程圖:

紋理可以理解為圖?。 在渲染圖形時需要在頂點(diǎn)圍成的區(qū)域中填充圖?,使得場景更加逼真。?這?使?的圖?,就是常說的紋理。只是在OpenGL,我們更加習(xí)慣叫紋理,?不是圖?。

2020-05-15 flutter BoxConstraints and layout

在出現(xiàn)布局錯誤時能盡快找到錯誤原因。

以下是對關(guān)鍵內(nèi)容的翻譯和注解。

flutter的布局模型是“一步布局模型”(one-pass layout model),在渲染樹中,向下treewalk傳遞給子 盒約束,然后再向上treewalk將計(jì)算好的幾何形狀(比如高度、寬度等)傳遞給父。我理解one-pass layout model就是一遍就將布局計(jì)算好。不會多次treewalk去計(jì)算布局,或多次重繪(repaint)并多次計(jì)算布局。

計(jì)算的好的幾何形狀必須符合盒約束的要求。

盒約束有四個值,minWidth,maxWidth,minHeight,maxHeight,符合盒約束的意思就是說 計(jì)算出的寬高必須在最大值和最小值之間 。

我猜測,在將盒約束向子傳遞的過程中,子會根據(jù)父的盒約束,設(shè)置自己的盒約束,而不是單純的繼承父的盒約束。稍后結(jié)合Flex布局可以解釋。

盒約束的最小值和最大值相等。因此在tight約束下的子的高寬將等于父的高寬,也就是說子是緊緊(tight)貼著父的。

盒約束的最小值為0,也就是說子可以是小于盒約束最大值的任何值,也就是說子是不緊貼(松的,loose)父的。

盒約束的最大值不是infinite(無窮大)

盒約束的最大值是infinite(無窮大)

盒約束的最小值是infinite(無窮大),他的子的寬或高只能取無窮大。

子的寬高(Size)符合盒約束的要求。

以下摘抄原文檔并翻譯,并加以分析。為了關(guān)注要點(diǎn), 忽略crossAxis方向(水平方向)的處理 。

以下圖為Column布局實(shí)例。

給column布局進(jìn)行了以下6步操作

首先給每個非flex子元素,設(shè)置豎直方向unbounded(無界)的盒約束。結(jié)合圖片,也就是將1、2兩個子設(shè)置好豎直方向無界的盒約束。示例中1和2設(shè)置了高度,因此一共占用高度是5+3=8.

按flex的比例給flex元素分配剩余的空間。因?yàn)槭纠挥幸粋€flex元素,即3號元素,因此將占有剩余全部空間,高度是20-5-3 = 12。

在第二步中分配好空間的flex元素,給他設(shè)置的盒約束不是豎直方向unbounded(給非flex元素設(shè)置的是豎直方向unbounded),而是有界的盒約束,盒約束的maxHeight是12,即第二步中被分配的高度。

水平方向不解釋了。高度設(shè)置完了他去設(shè)置寬度。

Column組件的總高度是由mainAxisSize屬性決定的,如果值是MainAxisSize.max,Column的高度就是Column的盒約束的maxHeight值,示例中我們給Column設(shè)置了高度為20的bounded盒約束,假設(shè)Column.mainAxisSize=MainAxisSize.max,那么Column的高度就是20.如果mainAxisSize=MainAxisSize.min,Column的高度將由其子元素的高度和決定。假設(shè)3號flex元素不設(shè)置成flex元素,而是固定高度為8,那么Column的高度就是5+3+8=16.

設(shè)置子元素的位置,即設(shè)置靠左,靠右,居中,分散等,與本示例關(guān)系不大。

根據(jù)第一步,inner column被outer column設(shè)置了無界(unbounded)的盒約束,Column會緊包c(diǎn)hildren,而inner column的Expanded要撐開以占用inner column的剩余空間,這就沖突了。

解決方案:給inner column設(shè)置有界的盒約束即可。比如給inner column外包一層有高度的Container。

下面這種方案,給inner column包一層Expanded也可以,是因?yàn)樵趏uter column中,Expanded會被設(shè)置成有界的盒約束(結(jié)合第三步),因此Expanded就可以向外擴(kuò)展(expand)了。

Flutter CustomPaint 使用介紹

CustomPaint class提供了讓用戶自定義widget的能力,它暴露了一個canvas,可以通過這個canvas來繪制widget,CustomPaint會先調(diào)用painter繪制背景,然后再繪制child,最后調(diào)用foregroundPainter來繪制前景,CustomPaint的定義如下

CustomPaint的繪制過程都將會交給CustomPainter來完成,CustomPainter是個抽象接口,在子類化CustomPainter的時候必須要重寫它的 paint 跟 shouldRepaint 接口,可以根據(jù)自己的場景來選擇性的重寫 hitTest 跟 shouldRebuildSemantics 方法。

canvas--畫布,真正的繪制是由canvas跟paint來完成的,畫布提供了各種繪制的接口來繪制圖形,除此以外畫布還提供了平移、縮放、旋轉(zhuǎn)等矩陣變換接口,畫布都有固定大小跟形狀,還可以使用畫布提供的裁剪接口來裁剪畫布的大小形狀等等。

常用的繪制接口有 更多請查看官方文檔

Paint---筆畫,是用來設(shè)置在畫布上面繪制圖形時的一些筆畫屬性,如:顏色、線寬、繪制模式、抗鋸齒等等。常用屬性有 更多請查看官方文檔

color : 設(shè)置畫筆顏色

isAntiAlias : 設(shè)置畫筆是否扛鋸齒

shader : 著色器,填充形狀或者畫線時用到,如果沒設(shè)置將會使用color

strokeWidth : 設(shè)置畫筆畫線寬度

style :繪制模式,畫線或充滿

下面這個例子來自于官方,通過 CustomPaint 畫出了一個藍(lán)天跟太陽出來

效果如下:

Flutter面試:渲染原理

頁面中的各界面元素(Widget)以樹的形式組織,即控件樹。Flutter通過控件樹中的每個控件創(chuàng)建不同類型的渲染對象,組成渲染對象樹。而渲染對象樹在Flutter的展示過程分為三個階段:布局、繪制、合成和渲染。

(一)布局

Flutter采用深度優(yōu)先機(jī)制遍歷渲染對象樹,決定渲染對象樹中各渲染對象在屏幕上的位置和尺寸。在布局過程中,渲染對象樹中的每個渲染對象都會接收父對象的布局約束參數(shù),決定自己的大小,然后父對象按照控件邏輯決定各個子對象的位置,完成布局過程。

為了防止因子節(jié)點(diǎn)發(fā)生變化而導(dǎo)致整個控件樹重新布局,F(xiàn)lutter加入了一個機(jī)制——布局邊界(Relayout Boundary),可以在某些節(jié)點(diǎn)自動或手動地設(shè)置布局邊界,當(dāng)邊界內(nèi)的任何對象發(fā)生重新布局時,不會影響邊界外的對象,反之亦然。

二)繪制

布局完成后,渲染對象樹中的每個節(jié)點(diǎn)都有了明確的尺寸和位置。Flutter會把所有的渲染對象繪制到不同的圖層上。與布局過程一樣,繪制過程也是深度優(yōu)先遍歷,而且總是先繪制自身,再繪制子節(jié)點(diǎn)。

以下圖為例:節(jié)點(diǎn)1在繪制完自身后,會再繪制節(jié)點(diǎn)2,然后繪制它的子節(jié)點(diǎn)3、4和5,最后繪制節(jié)點(diǎn)6。

可以看到,由于一些其他原因(比如,視圖手動合并)導(dǎo)致2的子節(jié)點(diǎn)5與它的兄弟節(jié)點(diǎn)6處于了同一層,這樣會導(dǎo)致當(dāng)節(jié)點(diǎn)2需要重繪的時候,與其無關(guān)的節(jié)點(diǎn)6也會被重繪,帶來性能損耗。

為了解決這一問題,F(xiàn)lutter提出了與布局邊界對應(yīng)的機(jī)制——重繪邊界(Repaint Boundary)。在重繪邊界內(nèi),F(xiàn)lutter會強(qiáng)制切換新的圖層,這樣就可以避免邊界內(nèi)外的互相影響,避免無關(guān)內(nèi)容置于同一圖層引起不必要的重繪。

重繪邊界的一個典型場景是Scrollview。ScrollView滾動的時候需要刷新視圖內(nèi)容,從而觸發(fā)內(nèi)容重繪。而當(dāng)滾動內(nèi)容重繪時,一般情況下其他內(nèi)容是不需要重繪的,這時候重繪邊界就派上用場了。

(三)合成和渲染

終端設(shè)備的頁面越來越復(fù)雜,因此Flutter的渲染樹層級通常很多,直接交付給渲染引擎進(jìn)行多圖層渲染,可能會出現(xiàn)大量渲染內(nèi)容的重復(fù)繪制,所以還需要先進(jìn)行一次圖層合成,即將所有的圖層根據(jù)大小、層級、透明度等規(guī)則計(jì)算出最終的顯示效果,將相同的圖層歸類合并,簡化渲染樹,提高渲染效率。

合并完成后,F(xiàn)lutter會將幾何圖層數(shù)據(jù)交由Skia引擎加工成二維圖像數(shù)據(jù),最終交由GPU進(jìn)行渲染,完成界面的展示。

四、總結(jié)

咱們從各種業(yè)界主流跨端方案與Flutter的對比開始,到Flutter的簡要介紹以及Flutter的運(yùn)行機(jī)制,并以界面渲染過程為例,從布局、繪制、合成和渲染三個階段講述了Flutter的實(shí)現(xiàn)原理。相信大家對Flutter已經(jīng)有一個整體認(rèn)知,趕快一起上手操作起來吧!

flutter貝塞爾曲線

1.要繪制貝塞爾線,我們需要四個點(diǎn): 起點(diǎn) , 終點(diǎn) 和 兩個控制點(diǎn) ,如下圖所示。移動控制點(diǎn)會改變曲線的斜率。您可以在此 在線工具中 使用控制點(diǎn)。

我們可以使用類Path的cubicTo方法繪制貝塞爾曲線:

使用控制點(diǎn)(x1,y1)和(x2,y2)添加從當(dāng)前點(diǎn)到給定點(diǎn)(x3,y3)的曲線的三次貝塞爾曲線段。

如您所見,該cubicTo方法接受三個參數(shù)。其中兩個是控制點(diǎn),最后一個參數(shù)是終點(diǎn)。起點(diǎn)是您的筆已經(jīng)位于畫布上的位置。

不要忘記在畫布坐標(biāo)中,左上角是(0,0)點(diǎn),右下角是(size.width,size.height)。因此,請嘗試相應(yīng)地調(diào)整四點(diǎn):

請記住,paint對象就像我們的筆,我們將其顏色設(shè)置為藍(lán)色,寬度設(shè)置為3。

我們用path對象描述了bezier路徑。該moveTo方法已用于將筆移動到路徑的起點(diǎn)。然后我們調(diào)用cubicTo方法來定義控制點(diǎn)和終點(diǎn)。之后,我們使用該drawPath方法繪制了路徑。

貝塞爾曲線參考:

///推薦一些曲線圖/折線圖/柱狀圖參考:

all first_rank_v2~rank_v25-2-95632571.nonecaseutm_term=flutter%20%E5%8A%A8%E6%80%81%E7%BB%98%E5%88%B6%E6%9B%B2%E7%BA%BF


新聞名稱:flutter畫幾何,flutter繪制曲線
網(wǎng)址分享:http://news.spvevtbd.cn/article/dsicgsg.html

其他資訊

在線咨詢
服務(wù)熱線
服務(wù)熱線:028-86922220
TOP