日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

前端悬浮窗效果_Flutter自绘组件:微信悬浮窗(一)

發布時間:2024/7/23 HTML 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 前端悬浮窗效果_Flutter自绘组件:微信悬浮窗(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? 看微信公眾號的時候時常會想退出去回復消息,但又不想放棄已經閱讀一半的文章,因為回復信息后再從公眾號找到該篇文章之間有不必要的時間花費,微信懸浮窗的出現解決了這個煩惱,回復完消息之后只需要點擊懸浮窗就可以回到之前在閱讀的文章中。在對比多篇公眾號文章的時候懸浮窗也使得在不同文章之間的切換更方便。懸浮窗的出現主要是為了省去用戶在不同頁面之間頻繁切換時不必要的時間和精力的開銷,這個組件小細節比較多,完整復刻估計需要分成三或四部分來講,這篇文章主要講述的是懸浮窗在點擊前的按鈕形態的實現,下一篇文章講解按鈕在不同形態之間切換,動起來。

先上最終效果對比圖:

實現思路

通過觀察可得,懸浮按鈕處于邊緣的時候是屬于不規則形狀。屬于官方的UI庫中是不存在的不規則圖形,也無法通過組合已有組件實現。這時候就需要用到Flutter提供的CustomPaint和CustomPainter來實現自繪組件,但具體的繪制工作是由Canvas類和Paint類進行的。

按鈕圖解

對微信的懸浮按鈕進行分析后,發現微信懸浮按鈕主要有三種形態:左邊緣按鈕形態,中心按鈕形態,和右邊緣按鈕形態,而每一個形態在按下的時候會有一個陰影,表示已選中,松開的時候陰影消失,表示未選中。具體形態圖解如下(按畫布大小為50x50設計):

圖解非一步到位,只是對原組件進行分析后大概畫一下,在繪圖的過程中再進行一些細微的調整達到較好的視覺效果(并非專業UI設計師,隨便畫畫)。進行分析圖解后,便可以著手對每一個形態進行繪制了。

使用到的類

在著手開始繪制前,我們需要了解我們繪制使用到的幾個類:

CustomPaint

構造函數如下:

CustomPaint({
Key key,
this.painter, //背景畫筆
this.foregroundPainter, //前景畫筆
this.size = Size.zero, //畫布即繪制區域的大小
this.isComplex = false, //是否為復雜繪制,若是Flutter則會啟用一些緩存策略減少繪制的開銷
this.willChange = false, //和isComplex配合使用,當啟用緩存,表示下一幀中繪制是否會改變
Widget child, //子節點,可以為空
})

CustomPainter

CustomPainter中定義的虛函數paint,主要的繪制工作都是在這個函數中完成,主要定義如下:

void paint(Canvas canvas, Size size);

size: 表示繪制區域的大小,傳遞自CustomPaint中的size

canvas: 畫布,其內封裝了大量的繪制方法,此處列舉本文中用到方法:?

API名稱功能
drawCircle繪制圓形
drawPath繪制路徑
drawImageRect根據給出的圖片及原矩形(src)和目標矩形(dst)繪制圖片
clipRRect根據給出的圓角矩形對畫布進行剪裁(超出區域不繪制)
drawShadow繪制陰影
drawColor根據模式繪制顏色(本文用于繪制圖片背景填充顏色)

Paint

如果說Canvas是畫布,那么Paint就是畫筆。Canvas中封裝的很多繪制方法都需要一個畫筆參數去進行繪制。畫筆Paint中定義了一些畫筆的基本屬性,如畫筆寬度,畫筆顏色,筆觸類型等,例子如下:

Paint _paint = Paint()
..color = Colors.blue //畫筆顏色,此處為藍色
..strokeCap = StrokeCap.round //畫筆筆觸類型
..isAntiAlias = true //是否啟動抗鋸齒
..blendMode = BlendMode.exclusion //顏色混合模式
..style = PaintingStyle.fill //繪畫風格,默認為填充
..colorFilter = ColorFilter.mode(Colors.blueAccent,
BlendMode.exclusion) //顏色渲染模式
..maskFilter = MaskFilter.blur(BlurStyle.inner, 3.0) //模糊遮罩效果
..filterQuality = FilterQuality.high //顏色渲染模式的質量
..strokeWidth = 15.0; //畫筆的寬度

我們在實際使用中根據需要去選擇相應的屬性,并不需要全部初始化。

Path

路徑,使用drawPath中繪制不規則圖形可以通過函數或者點間連線、曲線等表示。本文用到的方法如下:

API名稱功能
moveTo將路徑起點移動到指定位置
lineTo從當前位置連線到指定位置
arcTo曲線

開始繪制

對于工程其他的文件和布局不做討論,主要討論如何實現繼承CustomPainter實現paint方法的繪制,對于如何使用這個Painter,可以參考《Flutter實戰》(https://book.flutterchina.club/chapter10/custom_paint.html)。

邊緣按鈕的繪制

由于左右邊緣按鈕的圖形繪制方法類似,因此我們主要討論 左邊緣按鈕 的實現。由圖解可知左邊緣按鈕是不規則的形狀。我們的畫布的尺寸為50x50,我們可以把這個形狀看作一個圓形和一個正方形的重合,這是一種思路。

但我寫的是另一種思路:看作是三條直線和一段圓弧所組成路徑,以邊緣按鈕內層的具體代碼實現為例:

//edgePath: 按鈕外邊緣路徑,黑色線條
var edgePath = new Path() ..moveTo(size.width / 2, size.height); //移動去x軸中點(25,0)
edgePath.lineTo(0.0, size.height); //第一條直線
edgePath.lineTo(0.0, 0.0);//第二條直線
edgePath.lineTo(size.width / 2,0.0);//第三條直線
//圓弧在圓心在(25,25),半徑為25的圓上
Rect rect1 = Rect.fromCircle(center:Offset(size.width / 2,size.height / 2),radius: 25);
edgePath.arcTo(rect1,pi * 1.5,pi,true); //右半圓,從 3/2 Π處起步 經過Π 個角度。

var paint = new Paint()
..isAntiAlias = true //畫曲線時抗鋸齒看起來更圓潤
..strokeWidth = 0.75 //
..strokeCap = StrokeCap.round
..maskFilter = MaskFilter.blur(BlurStyle.normal,0.25) //線條模糊
..style = PaintingStyle.stroke //線條,不填充
..color = Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);

canvas.drawPath(edgePath, paint);//繪制路徑

效果如下:

同理繪制內層陰影部分為:

//繪制按鈕內層
var paint = Paint()
..isAntiAlias = false
..style = PaintingStyle.fill
..color = Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);
//..color = Color.fromRGBO(0xDA,0xDA,0xDA,0.9);

//path : 按鈕內邊緣路徑
var path = new Path() ..moveTo(size.width / 2 , size.height - 1.5);
path.lineTo(0.0, size.height - 1.5);
path.lineTo(0.0, 1.5);
path.lineTo(size.width / 2 ,1.5);
Rect rect = Rect.fromCircle(center:Offset(size.width / 2,size.height / 2),radius: 23.5);
path.arcTo(rect,pi * 1.5,pi,true);
canvas.drawPath(path, paint);

合起來效果為:

最重點是中間圖標logo的繪制。?

此處有踩坑點,canvas中繪制圖片的方法

void drawImageRect(Image image, Rect src, Rect dst, Paint paint)?

參數image的類型Image并不是我們日常使用到的Image類型,而是封裝在ui庫中的一個官方私有類,只能通過監聽圖片流返回一個Future,再通過FutureBuilder把image傳遞給CustomPainter的子類。詳情看 ui.Image加載探索(https://cloud.tencent.com/developer/article/1622733)這篇博客。具體內容在下一篇文章中再解釋,本文暫時不涉及,不需要理解。

? 先講繪制。對于中間logo的繪制,我們將原圖片的大小輸出為目標區域的大小。其次,區域為矩形,而我們需要輸出的logo是圓形區域,因此需要對畫布進行剪裁,示意圖如下:

具體實現代碼如下:

//繪制中間圖標
paint = new Paint();
canvas.save(); //剪裁前保存圖層
RRect imageRRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 17.5,size.width / 2 - 17.5, 35, 35),Radius.circular(17.5));
canvas.clipRRect(imageRRect);//圖片為圓形,圓形剪裁
canvas.drawColor(Colors.white, BlendMode.srcOver); //設置填充顏色為白色
Rect srcRect = Rect.fromLTWH(0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
Rect dstRect = Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
canvas.restore();//圖片繪制完畢恢復圖層

完整的左邊緣按鈕繪制代碼:

//繪制左邊界懸浮按鈕
void paintLeftEdgeButton(Canvas canvas,Size size,bool isPress)
{
//繪制按鈕內層
var paint = Paint()
..isAntiAlias = false
..style = PaintingStyle.fill
..color = Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);
//..color = Color.fromRGBO(0xDA,0xDA,0xDA,0.9);

//path : 按鈕內邊緣路徑
var path = new Path() ..moveTo(size.width / 2 , size.height - 1.5);
path.lineTo(0.0, size.height - 1.5);
path.lineTo(0.0, 1.5);
path.lineTo(size.width / 2 ,1.5);
Rect rect = Rect.fromCircle(center:Offset(size.width / 2,size.height / 2),radius: 23.5);
path.arcTo(rect,pi * 1.5,pi,true);
canvas.drawPath(path, paint);


//edgePath: 按鈕外邊緣路徑,黑色線條
var edgePath = new Path() ..moveTo(size.width / 2, size.height);
edgePath.lineTo(0.0, size.height);
edgePath.lineTo(0.0, 0.0);
edgePath.lineTo(size.width / 2,0.0);
Rect rect1 = Rect.fromCircle(center:Offset(size.width / 2,size.height / 2),radius: 25);
edgePath.arcTo(rect1,pi * 1.5,pi,true);

paint
..isAntiAlias = true
..strokeWidth = 0.75
..strokeCap = StrokeCap.round
..maskFilter = MaskFilter.blur(BlurStyle.normal,0.25) //線條模糊
..style = PaintingStyle.stroke
..color = Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
canvas.drawPath(edgePath, paint);

//按下則畫陰影,表示選中
if(isPress) canvas.drawShadow(edgePath, Color.fromRGBO(0xDA, 0xDA, 0xDA, 0.3), 0, false);

//繪制中間圖標
paint = new Paint();
canvas.save(); //剪裁前保存圖層
RRect imageRRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 17.5,size.width / 2 - 17.5, 35, 35),Radius.circular(17.5));
canvas.clipRRect(imageRRect);//圖片為圓形,圓形剪裁
canvas.drawColor(Colors.white, BlendMode.srcOver); //設置填充顏色為白色
Rect srcRect = Rect.fromLTWH(0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
Rect dstRect = Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
canvas.restore();//圖片繪制完畢恢復圖層
}

效果圖:

同理,右邊緣按鈕的繪制為:

//繪制右邊界按鈕
void paintRightEdgeButton(Canvas canvas,Size size){

var paint = Paint()
..isAntiAlias = false
..style = PaintingStyle.fill
..color = Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);

var path = Path() ..moveTo(size.width / 2, 1.5);
path.lineTo(size.width,1.5);
path.lineTo(size.width, size.height - 1.5);
path.lineTo(size.width / 2, size.height - 1.5);

Rect rect = Rect.fromCircle(center: Offset(size.width / 2,size.height / 2),radius: 23.5);
path.arcTo(rect, pi * 0.5, pi, true);

canvas.drawPath(path, paint);//繪制


//edgePath: 按鈕外邊緣路徑
var edgePath = Path() ..moveTo(size.width / 2,0.0);
edgePath.lineTo(size.width,0.0);
edgePath.lineTo(size.width, size.height);
edgePath.lineTo(size.width / 2, size.height);
Rect edgeRect = Rect.fromCircle(center: Offset(size.width / 2,size.height / 2),radius: 25);
edgePath.arcTo(edgeRect, pi * 0.5, pi, true);

paint
..isAntiAlias = true
..strokeWidth = 0.75
..strokeCap = StrokeCap.round
..maskFilter = MaskFilter.blur(BlurStyle.normal,0.25)
..style = PaintingStyle.stroke
..color = Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
canvas.drawPath(edgePath, paint);

//如果按下則繪制陰影
if(isPress)
canvas.drawShadow(path, Color.fromRGBO(0xDA, 0xDA, 0xDA, 0.3), 0, false);

//繪制中間圖標
paint = new Paint();
canvas.save(); //剪裁前保存圖層
RRect imageRRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 17.5,size.width / 2 - 17.5, 35, 35),Radius.circular(17.5));
canvas.clipRRect(imageRRect);//圖片為圓形,圓形剪裁
canvas.drawColor(Colors.white, BlendMode.srcOver); //設置填充顏色為白色
Rect srcRect = Rect.fromLTWH(0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
Rect dstRect = Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
canvas.restore();//圖片繪制完畢恢復圖層

}

中心按鈕的繪制

中心按鈕為規則的兩個圓型+中間的logo,因此繪制很簡單,具體代碼如下:

//繪制中心按鈕
void paintCenterButton(Canvas canvas,Size size)
{
//繪制按鈕內層
var paint = new Paint()
..isAntiAlias = false
..style = PaintingStyle.fill
..color = Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);
canvas.drawCircle(Offset(size.width / 2,size.height / 2), 23.5, paint);

//繪制按鈕外層邊線
paint
..isAntiAlias = true
..style = PaintingStyle.stroke
..strokeWidth = 0.75
..maskFilter = MaskFilter.blur(BlurStyle.normal,0.25)
..color = Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
canvas.drawCircle(Offset(size.width / 2,size.height / 2), 25, paint);

//如果按下則繪制陰影
if(isPress){
var circleRect = Rect.fromCircle(center: Offset(size.width / 2,size.height / 2),radius: 25);
var circlePath = new Path() ..moveTo(size.width / 2, size.height / 2);
circlePath.arcTo(circleRect, 0, 2 * 3.14, true);
canvas.drawShadow(circlePath, Color.fromRGBO(0xCF, 0xCF, 0xCF, 0.3), 0.5, false);
}

//繪制中間圖標
paint = new Paint();
canvas.save(); //圖片剪裁前保存圖層
RRect imageRRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 17.5,size.width / 2 - 17.5, 35, 35),Radius.circular(35));
canvas.clipRRect(imageRRect);//圖片為圓形,圓形剪裁
canvas.drawColor(Colors.white, BlendMode.srcOver); //設置填充顏色為白色
Rect srcRect = Rect.fromLTWH(0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
Rect dstRect = Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
canvas.restore();//恢復剪裁前的圖層

}

如何選擇繪制

細心的你可能發現了一個變量isPress,他代表著什么呢,代表著的是否手指按下懸浮按鈕,若按下則繪制表示選中的陰影部分。當你有了邊緣按鈕和中心按鈕,組件的繪制是一幀一幀的畫面,paint(Canvas canvas,Size size)繪制的是一幀的畫面,而如何判斷這一幀時選擇繪制左邊緣按鈕還是右邊緣按鈕,中心按鈕?因此,在FloatingButtonPainter中還應該定義幾個判斷的變量。

FloatingButtonPainter({
Key key,
@required this.isLeft,
@required this.isEdge,
@required this.isPress,
@required this.buttonImage
});

//按鈕是否在屏幕左側,屏幕寬度中線為準
final bool isLeft;
//按鈕是否在屏幕邊界,左/右邊界
final bool isEdge;
//按鈕是否被按下
final bool isPress;
//內按鈕logo ui.image
final ui.Image buttonImage;

在paint方法中進行判斷

@overridevoid paint(Canvas canvas, Size size) {// TODO: implement paint //按鈕是否在邊緣 if(isEdge){//按鈕在屏幕左邊或右邊 if(isLeft)
paintLeftEdgeButton(canvas, size);//繪制左邊緣按鈕 else paintRightEdgeButton(canvas, size);//繪制右邊緣按鈕 }else{
paintCenterButton(canvas, size);//繪制中心按鈕 }
}

完整代碼

FloatingButtonPainter完整代碼:

import 'package:flutter/material.dart';
import 'dart:math';
import 'dart:ui' as ui;

class FloatingButtonPainter extends CustomPainter
{
FloatingButtonPainter({
Key key,
@required this.isLeft,
@required this.isEdge,
@required this.isPress,
@required this.buttonImage
});

//按鈕是否在屏幕左側,屏幕寬度 / 2
final bool isLeft;
//按鈕是否在屏幕邊界,左/右邊界
final bool isEdge;
//按鈕是否被按下
final bool isPress;
//內按鈕圖片 ui.image
final ui.Image buttonImage;
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
if(isEdge){
if(isLeft)
paintLeftEdgeButton(canvas, size);//繪制左邊緣按鈕
else
paintRightEdgeButton(canvas, size);//繪制右邊緣按鈕
}
else{
paintCenterButton(canvas, size); //繪制中心按鈕
}
}

//繪制左邊界懸浮按鈕
void paintLeftEdgeButton(Canvas canvas,Size size)
{
//繪制按鈕內層
var paint = Paint()
..isAntiAlias = false
..style = PaintingStyle.fill
..color = Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);
//..color = Color.fromRGBO(0xDA,0xDA,0xDA,0.9);

//path : 按鈕內邊緣路徑
var path = new Path() ..moveTo(size.width / 2 , size.height - 1.5);
path.lineTo(0.0, size.height - 1.5);
path.lineTo(0.0, 1.5);
path.lineTo(size.width / 2 ,1.5);
Rect rect = Rect.fromCircle(center:Offset(size.width / 2,size.height / 2),radius: 23.5);
path.arcTo(rect,pi * 1.5,pi,true);
canvas.drawPath(path, paint);


//edgePath: 按鈕外邊緣路徑,黑色線條
var edgePath = new Path() ..moveTo(size.width / 2, size.height);
edgePath.lineTo(0.0, size.height);
edgePath.lineTo(0.0, 0.0);
edgePath.lineTo(size.width / 2,0.0);
Rect rect1 = Rect.fromCircle(center:Offset(size.width / 2,size.height / 2),radius: 25);
edgePath.arcTo(rect1,pi * 1.5,pi,true);

paint
..isAntiAlias = true
..strokeWidth = 0.75
..strokeCap = StrokeCap.round
..maskFilter = MaskFilter.blur(BlurStyle.normal,0.25) //線條模糊
..style = PaintingStyle.stroke
..color = Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
canvas.drawPath(edgePath, paint);

//按下則畫陰影,表示選中
if(isPress) canvas.drawShadow(edgePath, Color.fromRGBO(0xDA, 0xDA, 0xDA, 0.3), 0, false);

//繪制中間圖標
paint = new Paint();
canvas.save(); //剪裁前保存圖層
RRect imageRRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 17.5,size.width / 2 - 17.5, 35, 35),Radius.circular(17.5));
canvas.clipRRect(imageRRect);//圖片為圓形,圓形剪裁
canvas.drawColor(Colors.white, BlendMode.srcOver); //設置填充顏色為白色
Rect srcRect = Rect.fromLTWH(0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
Rect dstRect = Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
canvas.restore();//圖片繪制完畢恢復圖層
}

//繪制右邊界按鈕
void paintRightEdgeButton(Canvas canvas,Size size){

var paint = Paint()
..isAntiAlias = false
..style = PaintingStyle.fill
..color = Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);

var path = Path() ..moveTo(size.width / 2, 1.5);
path.lineTo(size.width,1.5);
path.lineTo(size.width, size.height - 1.5);
path.lineTo(size.width / 2, size.height - 1.5);

Rect rect = Rect.fromCircle(center: Offset(size.width / 2,size.height / 2),radius: 23.5);
path.arcTo(rect, pi * 0.5, pi, true);

canvas.drawPath(path, paint);//繪制


//edgePath: 按鈕外邊緣路徑
var edgePath = Path() ..moveTo(size.width / 2,0.0);
edgePath.lineTo(size.width,0.0);
edgePath.lineTo(size.width, size.height);
edgePath.lineTo(size.width / 2, size.height);
Rect edgeRect = Rect.fromCircle(center: Offset(size.width / 2,size.height / 2),radius: 25);
edgePath.arcTo(edgeRect, pi * 0.5, pi, true);

paint
..isAntiAlias = true
..strokeWidth = 0.75
..strokeCap = StrokeCap.round
..maskFilter = MaskFilter.blur(BlurStyle.normal,0.25)
..style = PaintingStyle.stroke
..color = Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
canvas.drawPath(edgePath, paint);

//如果按下則繪制陰影
if(isPress)
canvas.drawShadow(path, Color.fromRGBO(0xDA, 0xDA, 0xDA, 0.3), 0, false);

//繪制中間圖標
paint = new Paint();
canvas.save(); //剪裁前保存圖層
RRect imageRRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 17.5,size.width / 2 - 17.5, 35, 35),Radius.circular(17.5));
canvas.clipRRect(imageRRect);//圖片為圓形,圓形剪裁
canvas.drawColor(Colors.white, BlendMode.srcOver); //設置填充顏色為白色
Rect srcRect = Rect.fromLTWH(0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
Rect dstRect = Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
canvas.restore();//圖片繪制完畢恢復圖層

}

//繪制中心按鈕
void paintCenterButton(Canvas canvas,Size size)
{
//繪制按鈕內層
var paint = new Paint()
..isAntiAlias = false
..style = PaintingStyle.fill
..color = Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);
canvas.drawCircle(Offset(size.width / 2,size.height / 2), 23.5, paint);

//繪制按鈕外層邊線
paint
..isAntiAlias = true
..style = PaintingStyle.stroke
..strokeWidth = 0.75
..maskFilter = MaskFilter.blur(BlurStyle.normal,0.25)
..color = Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
canvas.drawCircle(Offset(size.width / 2,size.height / 2), 25, paint);

//如果按下則繪制陰影
if(isPress){
var circleRect = Rect.fromCircle(center: Offset(size.width / 2,size.height / 2),radius: 25);
var circlePath = new Path() ..moveTo(size.width / 2, size.height / 2);
circlePath.arcTo(circleRect, 0, 2 * 3.14, true); //使用pi會出錯。
canvas.drawShadow(circlePath, Color.fromRGBO(0xCF, 0xCF, 0xCF, 0.3), 0.5, false);
}

//繪制中間圖標
paint = new Paint();
canvas.save(); //圖片剪裁前保存圖層
RRect imageRRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 17.5,size.width / 2 - 17.5, 35, 35),Radius.circular(35));
canvas.clipRRect(imageRRect);//圖片為圓形,圓形剪裁
canvas.drawColor(Colors.white, BlendMode.srcOver); //設置填充顏色為白色
Rect srcRect = Rect.fromLTWH(0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
Rect dstRect = Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);
canvas.restore();//恢復剪裁前的圖層

}
//測試繪制
void paintTest(Canvas canvas,Size size){
var paint = Paint()
..isAntiAlias = false
..style = PaintingStyle.fill
..color = Color.fromRGBO(0xF3, 0xF3, 0xF3, 0.9);

var path = Path() ..moveTo(size.width / 2, 1.5);
path.lineTo(size.width,1.5);
path.lineTo(size.width, size.height - 1.5);
path.lineTo(size.width / 2, size.height - 1.5);

Rect rect = Rect.fromCircle(center: Offset(size.width / 2,size.height / 2),radius: 23.5);
path.arcTo(rect, pi * 0.5, pi, true);

canvas.drawPath(path, paint);//繪制


//edgePath: 按鈕外邊緣路徑
var edgePath = Path() ..moveTo(size.width / 2,0.0);
edgePath.lineTo(size.width,0.0);
edgePath.lineTo(size.width, size.height);
edgePath.lineTo(size.width / 2, size.height);
Rect edgeRect = Rect.fromCircle(center: Offset(size.width / 2,size.height / 2),radius: 23.5);
edgePath.arcTo(edgeRect, pi * 0.5, pi, true);

paint
..isAntiAlias = true
..strokeWidth = 0.75
..strokeCap = StrokeCap.round
..maskFilter = MaskFilter.blur(BlurStyle.normal,0.25)
..style = PaintingStyle.stroke
..color = Color.fromRGBO(0xCF, 0xCF, 0xCF, 1);
canvas.drawPath(edgePath, paint);

//繪制中間圖標
paint = new Paint();
RRect imageRRect = RRect.fromRectAndRadius(Rect.fromLTWH(size.width / 2 - 20,size.width / 2 - 20, 40, 40),Radius.circular(20));
canvas.clipRRect(imageRRect);//圖片為圓形,圓形剪裁
canvas.drawColor(Colors.white, BlendMode.srcOver); //設置填充顏色為白色
Rect srcRect = Rect.fromLTWH(0.0, 0.0, buttonImage.width.toDouble(), buttonImage.height.toDouble());
Rect dstRect = Rect.fromLTWH(size.width / 2 - 17.5, size.height / 2 - 17.5, 35, 35);
canvas.drawImageRect(buttonImage, srcRect, dstRect, paint);

//如果按下則繪制陰影
if(isPress)
canvas.drawShadow(path, Color.fromRGBO(0xDA, 0xDA, 0xDA, 0.3), 0, false);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return true;
}
}

總結

寫這個的篇幅比我料想中需要的篇幅還多,目前只實現各種形態繪制,實現FloatingButtonPainter類。但如何使用這個類,讓它在各個形態之間切換,讓它動起來,實現最終效果,在下一篇文章中繼續,有興趣的可以關注,點贊,點個在看。

創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的前端悬浮窗效果_Flutter自绘组件:微信悬浮窗(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。