当前位置: 首页 > news >正文

科技网站开发网站优化排名方法有哪些

科技网站开发,网站优化排名方法有哪些,广州网站设计网站制作,wordpress新站注意事项📚 目录 介绍组合多个组件自绘组件 Custompaint绘制边界RepaintBoundaryCustomPainter与Canvas画笔Paint绘制组件例子 本文学习和引用自《Flutter实战第二版》:作者:杜文 1. 介绍 当Flutter提供的现有组件无法满足我们的需求,或…

📚 目录

  1. 介绍
  2. 组合多个组件
  3. 自绘组件
    1. Custompaint
    2. 绘制边界RepaintBoundary
    3. CustomPainter与Canvas
    4. 画笔Paint
    5. 绘制组件例子

本文学习和引用自《Flutter实战·第二版》:作者:杜文

1. 介绍

当Flutter提供的现有组件无法满足我们的需求,或者我们为了共享代码需要封装一些通用组件,这时我们就需要自定义组件。在Flutter中自定义组件有三种方式:通过组合其他组件、自绘和实现RenderObject。

  • 组合多个Widget:通过已有组件来拼装组合成一个新的组件。
  • 通过CustomPaint自绘:通过Flutter中提供的CustomPaint和Canvas来实现UI自绘。
  • 通过RenderObject自绘:RenderObject中最终也是通过Canvas API来绘制的。而CustomPaint只是为了方便开发者封装的一个代理类。

2. 组合多个组件

实现一个渐变色背景,支持圆角,按下有涟漪效果的按钮,并且实现一个带动画的可以旋转的容器,每次点击按钮,容器旋转一点。

import 'package:flutter/material.dart';/// 定义
class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}/// 实现
class HomePageState extends State<HomePage> {double myTurns = 0.0;Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Flutter Home'),),body: Container(alignment: Alignment.center,child: Column(children: [TurnBox(turns: myTurns,speed: 500,child: const Icon(Icons.refresh,size: 50,)),MyButton(colors: const [Colors.green, Colors.orangeAccent],height: 60.0,width: 300.0,borderRadius: const BorderRadius.all(Radius.circular(8)),onPressed: () {setState(() {myTurns += 0.2;});},child: const Text('放手一搏'),)],),));}
}
/// 自定义按钮
class MyButton extends StatelessWidget {const MyButton({Key? key,this.colors,this.width,this.height,this.onPressed,this.borderRadius,required this.child}): super(key: key);// 渐变色数组final List<Color>? colors;// 按钮属性final double? width;final double? height;final BorderRadius? borderRadius;// 点击回调事件final GestureTapCallback? onPressed;final Widget child;Widget build(BuildContext context) {ThemeData theme = Theme.of(context);List<Color> myColors =colors ?? [theme.primaryColor, theme.primaryColorDark];return DecoratedBox(decoration: BoxDecoration(gradient: LinearGradient(colors: myColors),borderRadius: borderRadius),child: Material(type: MaterialType.transparency,child: InkWell(splashColor: Colors.white70,highlightColor: Colors.transparent,borderRadius: borderRadius ?? BorderRadius.circular(8),onTap: onPressed,child: ConstrainedBox(constraints: BoxConstraints.tightFor(height: height, width: width),child: Center(child: Padding(padding: const EdgeInsets.all(8.0),child: DefaultTextStyle(style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18.0),child: child,),),),),),),);}
}/// 自定义旋转
class TurnBox extends StatefulWidget {const TurnBox({Key? key,this.turns = .0, // 旋转的“圈”数,一圈为360度,如0.25圈即90度this.speed = 200, // 过渡动画执行的总时长required this.child}) :super(key: key);final double turns;final int speed;final Widget child;TurnBoxState createState() => TurnBoxState();
}class TurnBoxState extends State<TurnBox> with SingleTickerProviderStateMixin {AnimationController? myController;void initState() {super.initState();myController = AnimationController(vsync: this,lowerBound: -double.infinity,upperBound: double.infinity);myController!.value = widget.turns;}void dispose() {myController!.dispose();super.dispose();}Widget build(BuildContext context) {return RotationTransition(turns: myController!,child: widget.child,);}void didUpdateWidget(TurnBox oldWidget) {super.didUpdateWidget(oldWidget);// 旋转角度发生变化时执行过渡动画if (oldWidget.turns != widget.turns) {myController!.animateTo(widget.turns,duration: Duration(milliseconds: widget.speed??200),curve: Curves.easeOut,);}}
}

3. 自绘组件

对于一些复杂或不规则的UI,我们可能无法通过组合其他组件的方式来实现。比如一个正六边形、一个渐变的圆形进度条、一个棋盘等。在Flutter中,提供了一个CustomPaint 组件,它可以结合画笔CustomPainter来实现自定义图形绘制。

3-1. CustomPaint

画笔CustomPainter绘制时我们需要提供前景或背景画笔,两者也可以同时提供。我们的画笔需要继承CustomPainter类,我们在画笔类中实现真正的绘制逻辑。

属性描述
painter背景画笔,会显示在子节点后面
foregroundPainter前景画笔,会显示在子节点前面
size当child为null时,代表默认绘制区域大小,如果有child则忽略此参数,画布尺寸则为child尺寸。如果有child但是想指定画布为特定大小,可以使用SizeBox包裹CustomPaint实现。
isComplex是否复杂的绘制,如果是,Flutter会应用一些缓存策略来减少重复渲染的开销。
willChange和isComplex配合使用,当启用缓存时,该属性代表在下一帧中绘制是否会改变。

3-2. 绘制边界RepaintBoundary

如果CustomPaint有子节点,为了避免子节点不必要的重绘并提高性能,通常情况下都会将子节点包裹在RepaintBoundary组件中,这样会在绘制时就会创建一个新的绘制层(Layer),其子组件将在新的Layer上绘制,而父组件将在原来Layer上绘制,也就是说RepaintBoundary 子组件的绘制将独立于父组件的绘制,RepaintBoundary会隔离其子节点和CustomPaint本身的绘制边界。

CustomPaint(// 指定画布大小size: Size(300, 300),painter: MyPainter(),child: RepaintBoundary(child:...), 
)

3-3. CustomPainter与Canvas

CustomPainter中提定义了一个虚函数paint。它有两个参数,Canvas和Size。Canvas是Flutter中绘制UI的底层组件,它是一个画布,包括各种绘制方法。Size是当前绘制区域大小。Canvas常用API如下:

API描述
drawLine画线
drawPoint画点
drawPath画路径
drawImage画图像
drawRect画矩形
drawCircle画圆
drawOval画椭圆
drawArc画圆弧

3-4. 画笔Paint

Flutter提供了Paint类来实现画笔。在Paint中,我们可以配置画笔的各种属性如粗细、颜色、样式等。如下例子:

// 创建一个画笔并配置其属性
var paint = Paint()..isAntiAlias = true // 是否抗锯齿..style = PaintingStyle.fill // 画笔样式:填充..color = Color(0x77cdb175); // 画笔颜色

3-5. 绘制组件例子

如下,是一个自定义绘制的饼状图。

  • 饼图完整代码
import 'dart:math';
import 'package:flutter/material.dart';typedef PieChartViewTap = Function(int index);
typedef OutsideText = Text Function(PieChartModel model, String scale);class PieChartView extends ImplicitlyAnimatedWidget {final List<PieChartModel> models;/// 是否显示内部圆final bool isShowHole;/// 内部圆的半径final double holeRadius;/// 内部圆的颜色final Color holeColor;/// 扇形分割线宽度final double spaceWidth;/// 溢出上方文字final OutsideText? outsideTopText;/// 溢出下方文字final OutsideText? outsideBottomText;/// 扇形点击事件final PieChartViewTap? onTap;const PieChartView(this.models, {Key? key,this.holeRadius = 55.0,this.isShowHole = true,this.holeColor = Colors.white,this.spaceWidth = 2.0,this.outsideTopText,this.outsideBottomText,this.onTap,Curve curve = Curves.linear,Duration duration = const Duration(milliseconds: 150),}) : super(key: key,curve: curve,duration: duration,);CustomPieViewState createState() => CustomPieViewState();
}class CustomPieViewState extends AnimatedWidgetBaseState<PieChartView> {CustomPieTween? customPieTween;List<PieChartModel> get end => widget.models.map((e) => PieChartModel(value: e.value, color: e.color, name: e.name, radius: e.radius)).toList();Widget build(BuildContext context) {return CustomPaint(size: Size.infinite,painter: PieChartPainter(context,customPieTween!.evaluate(animation),holeRadius: widget.holeRadius,isShowHole: widget.isShowHole,holeColor: widget.holeColor,spaceWidth: widget.spaceWidth,outsideTopText: widget.outsideTopText,outsideBottomText: widget.outsideBottomText,onTap: widget.onTap,),);}void forEachTween(TweenVisitor<dynamic> visitor) {customPieTween = visitor(customPieTween, end, (dynamic value) {return CustomPieTween(begin: value, end: end);}) as CustomPieTween;}
}class CustomPieTween extends Tween<List<PieChartModel>> {CustomPieTween({List<PieChartModel>? begin, List<PieChartModel>? end}): super(begin: begin, end: end);List<PieChartModel> lerp(double t) {List<PieChartModel> list = [];begin?.asMap().forEach((index, model) {list.add(model..radius = lerpDouble(model.radius, end?[index].radius ?? 100.0, t));});return list;}double lerpDouble(double radius, double radius2, double t) {if (radius == radius2) {return radius;}var d = (radius2 - radius) * t;var value = radius + d;return value;}
}class PieChartPaint extends CustomPaint {const PieChartPaint({Key? key}) : super(key: key);
}class PieChartPainter extends CustomPainter {final BuildContext context;final List<PieChartModel> models;final bool isShowHole;final double holeRadius;final Color holeColor;final double spaceWidth;final OutsideText? outsideTopText;final OutsideText? outsideBottomText;final PieChartViewTap? onTap;final List<Path> paths = [];final Path holePath = Path();Offset oldTapOffset = Offset.zero;PieChartPainter(this.context,this.models, {this.holeRadius = 60.0,this.isShowHole = true,this.holeColor = Colors.white,this.spaceWidth = 2.0,this.outsideTopText,this.outsideBottomText,this.onTap,});void paint(Canvas canvas, Size size) {//移动到中心点canvas.translate(size.width / 2, size.height / 2);//绘制饼状图_drawPie(canvas, size);//绘制分割线_drawSpaceLine(canvas);// 绘制中心圆_drawHole(canvas, size);// drawLineAndText(canvas);}bool shouldRepaint(CustomPainter oldDelegate) => oldDelegate != this;bool? hitTest(Offset position) {return _interceptTouchEvent(position);}bool _interceptTouchEvent(Offset offset) {if (oldTapOffset.dx == offset.dx && oldTapOffset.dy == offset.dy) {return false;}oldTapOffset = offset;for (int i = 0; i < paths.length; i++) {if (paths[i].contains(offset) && !holePath.contains(offset)) {onTap?.call(i);oldTapOffset = offset;return true;}}onTap?.call(-1);return false;}/// 绘制分割线void _drawSpaceLine(Canvas canvas) {var sumValue = models.fold<double>(0.0, (sum, model) => sum + model.value);var startAngle = 0.0;for (var model in models) {_drawLine(canvas, startAngle, model.radius);startAngle += model.value / sumValue * 360;_drawLine(canvas, startAngle, model.radius);}}void _drawLine(Canvas canvas, double angle, double radius) {var endX = cos(angle * pi / 180) * radius;var endY = sin(angle * pi / 180) * radius;Paint paint = Paint()..style = PaintingStyle.fill..color = Colors.white..strokeWidth = spaceWidth;canvas.drawLine(Offset.zero, Offset(endX, endY), paint);}/// 绘制饼状图void _drawPie(Canvas canvas, Size size) {var startAngle = 0.0;var sumValue = models.fold<double>(0.0, (sum, model) => sum + model.value);for (var model in models) {Paint paint = Paint()..style = PaintingStyle.fill..color = model.color;var sweepAngle = model.value / sumValue * 360;canvas.drawArc(Rect.fromCircle(radius: model.radius, center: Offset.zero),startAngle * pi / 180, sweepAngle * pi / 180, true, paint);Path path = Path();var centerX = size.width / 2;var centerY = size.height / 2;path.addArc(Rect.fromCircle(radius: model.radius, center: Offset(centerX, centerY)),startAngle * pi / 180,sweepAngle * pi / 180);path.moveTo(centerX, centerY);path.lineTo(centerX + cos(startAngle * pi / 180) * model.radius,centerY + sin(startAngle * pi / 180) * model.radius);path.lineTo(centerX + cos((sweepAngle + startAngle) * pi / 180) * model.radius,centerY + sin((sweepAngle + startAngle) * pi / 180) * model.radius);paths.add(path);// 为每一个区域绘制延长线和文字_drawLineAndText(canvas, size, model.radius, startAngle, sweepAngle, model);startAngle += sweepAngle;}}/// 绘制延长线和文字void _drawLineAndText(Canvas canvas, Size size, double radius,double startAngle, double sweepAngle, PieChartModel model) {var ratio = (sweepAngle / 360.0 * 100).toStringAsFixed(2);var top = outsideTopText?.call(model, ratio) ??Text(model.name,style: const TextStyle(color: Colors.black38),);var topTextPainter = getTextPainter(top);var bottom = outsideBottomText?.call(model, ratio) ??Text("$ratio%",style: const TextStyle(color: Colors.black38),);var bottomTextPainter = getTextPainter(bottom);// 绘制横线// 计算开始坐标以及转折点的坐标var startX = radius * (cos((startAngle + (sweepAngle / 2)) * (pi / 180)));var startY = radius * (sin((startAngle + (sweepAngle / 2)) * (pi / 180)));var firstLine = radius / 5;var secondLine =max(bottomTextPainter.width, topTextPainter.width) + radius / 4;var pointX = (radius + firstLine) *(cos((startAngle + (sweepAngle / 2)) * (pi / 180)));var pointY = (radius + firstLine) *(sin((startAngle + (sweepAngle / 2)) * (pi / 180)));// 计算坐标在左边还是在右边// 并计算横线结束坐标// 如果结束坐标超过了绘制区域,则改变结束坐标的值var endX = 0.0;// 距离绘制边界的偏移量var marginOffset = 20.0;if (pointX - startX > 0) {endX = min(pointX + secondLine, size.width / 2 - marginOffset);secondLine = endX - pointX;} else {endX = max(pointX - secondLine, -size.width / 2 + marginOffset);secondLine = pointX - endX;}Paint paint = Paint()..style = PaintingStyle.fill..strokeWidth = 1..color = Colors.grey;// 绘制延长线canvas.drawLine(Offset(startX, startY), Offset(pointX, pointY), paint);canvas.drawLine(Offset(pointX, pointY), Offset(endX, pointY), paint);// 文字距离中间横线上下间距偏移量var offset = 4;var textWidth = bottomTextPainter.width;var textStartX = 0.0;textStartX = _calculateTextStartX(pointX, startX, textWidth, secondLine, textStartX, offset);bottomTextPainter.paint(canvas, Offset(textStartX, pointY + offset));textWidth = topTextPainter.width;var textHeight = topTextPainter.height;textStartX = _calculateTextStartX(pointX, startX, textWidth, secondLine, textStartX, offset);topTextPainter.paint(canvas, Offset(textStartX, pointY - offset - textHeight));// 绘制文字前面的小圆点paint.color = model.color;canvas.drawCircle(Offset(textStartX - 8, pointY - 4 - topTextPainter.height / 2),4,paint);}double _calculateTextStartX(double stopX, double startX, double w,double line2, double textStartX, int offset) {if (stopX - startX > 0) {if (w > line2) {textStartX = (stopX + offset);} else {textStartX = (stopX + (line2 - w));}} else {if (w > line2) {textStartX = (stopX - offset - w);} else {textStartX = (stopX - (line2 - w) - w);}}return textStartX;}TextPainter getTextPainter(Text text) {TextPainter painter = TextPainter(locale: Localizations.localeOf(context),maxLines: text.maxLines,textDirection: TextDirection.ltr,text: TextSpan(text: text.data,style: text.style,),);painter.layout();return painter;}/// 绘制中心圆void _drawHole(Canvas canvas, Size size) {if (isShowHole) {holePath.reset();Paint paint = Paint()..style = PaintingStyle.fill..color = Colors.white;canvas.drawCircle(Offset.zero, holeRadius, paint);var centerX = size.width / 2;var centerY = size.height / 2;holePath.addArc(Rect.fromCircle(radius: holeRadius, center: Offset(centerX, centerY)),0,360 * pi / 180);}}
}/// 数据
class PieChartModel {double value;Color color;String name;double radius;PieChartModel({required this.value,required this.color,required this.name,this.radius = 100,});
}
  • 使用
import 'package:flutter/material.dart';
import 'package:demo1/widget/MyPieChart.dart';/// 定义
class HomePage extends StatefulWidget {const HomePage({super.key});State<HomePage> createState() => HomePageState();
}/// 实现
class HomePageState extends State<HomePage> {double myTurns = 0.0;Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Flutter Home'),),body: Container(alignment: Alignment.center,// 饼图child: PieChartView([PieChartModel(value: 35,name: 'A',color: Colors.blue,radius: 100,),PieChartModel(value: 15,name: 'B',color: Colors.red,radius: 100,),PieChartModel(value: 22,name: 'C',color: Colors.yellow,radius: 100,),PieChartModel(value: 18,name: 'D',color: Colors.orange,radius: 100,),PieChartModel(value: 39,name: 'F',color: Colors.green,radius: 100,),],),));}
}

本次分享就到这儿啦,我是鹏多多,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~

往期文章

  • 手把手教你搭建规范的团队vue项目,包含commitlint,eslint,prettier,husky,commitizen等等
  • Web Woeker和Shared Worker的使用以及案例
  • Vue2全家桶+Element搭建的PC端在线音乐网站
  • vue3+element-plus配置cdn
  • 助你上手Vue3全家桶之Vue3教程
  • 助你上手Vue3全家桶之VueX4教程
  • 助你上手Vue3全家桶之Vue-Router4教程
  • 超详细!Vue的九种通信方式
  • 超详细!Vuex手把手教程
  • 使用nvm管理node.js版本以及更换npm淘宝镜像源
  • vue中利用.env文件存储全局环境变量,以及配置vue启动和打包命令
  • 超详细!Vue-Router手把手教程

个人主页

  • CSDN
  • GitHub
  • 简书
  • 博客园
  • 掘金

文章转载自:
http://crosshead.hkpn.cn
http://beachfront.hkpn.cn
http://caidos.hkpn.cn
http://berkshire.hkpn.cn
http://tink.hkpn.cn
http://globulet.hkpn.cn
http://rauvite.hkpn.cn
http://miter.hkpn.cn
http://recollection.hkpn.cn
http://ngbandi.hkpn.cn
http://pouch.hkpn.cn
http://olympic.hkpn.cn
http://malaceous.hkpn.cn
http://compassionate.hkpn.cn
http://disyllable.hkpn.cn
http://teratosis.hkpn.cn
http://uproarious.hkpn.cn
http://irrationally.hkpn.cn
http://courthouse.hkpn.cn
http://extorsive.hkpn.cn
http://floodgate.hkpn.cn
http://masturbate.hkpn.cn
http://paranoiac.hkpn.cn
http://intoxication.hkpn.cn
http://baaroque.hkpn.cn
http://coexist.hkpn.cn
http://childing.hkpn.cn
http://philanthropic.hkpn.cn
http://sizar.hkpn.cn
http://interconvertible.hkpn.cn
http://zymase.hkpn.cn
http://corpuscule.hkpn.cn
http://drowsily.hkpn.cn
http://subedit.hkpn.cn
http://jungian.hkpn.cn
http://offaly.hkpn.cn
http://arteritis.hkpn.cn
http://warcraft.hkpn.cn
http://depolarize.hkpn.cn
http://donatist.hkpn.cn
http://refusable.hkpn.cn
http://debrett.hkpn.cn
http://hotspur.hkpn.cn
http://spidery.hkpn.cn
http://circulatory.hkpn.cn
http://besotted.hkpn.cn
http://antatrophic.hkpn.cn
http://mizenyard.hkpn.cn
http://spiroscope.hkpn.cn
http://leftism.hkpn.cn
http://botcher.hkpn.cn
http://intercolumniation.hkpn.cn
http://inwall.hkpn.cn
http://interpellant.hkpn.cn
http://pinger.hkpn.cn
http://covered.hkpn.cn
http://typoscript.hkpn.cn
http://cookstove.hkpn.cn
http://motuca.hkpn.cn
http://thiller.hkpn.cn
http://mesquite.hkpn.cn
http://reproval.hkpn.cn
http://monthly.hkpn.cn
http://cornily.hkpn.cn
http://irritative.hkpn.cn
http://sonneteer.hkpn.cn
http://forehead.hkpn.cn
http://isabelline.hkpn.cn
http://sectarian.hkpn.cn
http://quadriform.hkpn.cn
http://trichromat.hkpn.cn
http://pamplegia.hkpn.cn
http://spicate.hkpn.cn
http://billycock.hkpn.cn
http://corpuscule.hkpn.cn
http://viniferous.hkpn.cn
http://formulist.hkpn.cn
http://psoitis.hkpn.cn
http://sinneh.hkpn.cn
http://sss.hkpn.cn
http://repressor.hkpn.cn
http://perplex.hkpn.cn
http://colostomy.hkpn.cn
http://outbluff.hkpn.cn
http://housebound.hkpn.cn
http://voltolization.hkpn.cn
http://ethnocide.hkpn.cn
http://woofy.hkpn.cn
http://transmigrant.hkpn.cn
http://octal.hkpn.cn
http://lawrentian.hkpn.cn
http://judoman.hkpn.cn
http://jalap.hkpn.cn
http://persuade.hkpn.cn
http://equimultiple.hkpn.cn
http://gmat.hkpn.cn
http://warty.hkpn.cn
http://reflexible.hkpn.cn
http://armada.hkpn.cn
http://woodpie.hkpn.cn
http://www.hrbkazy.com/news/77332.html

相关文章:

  • 新服务器做网站株洲seo优化
  • 网站子页面怎么做的最近三天的新闻大事小学生
  • 金融网站建设方案书西安关键词快速排名
  • 人工做流量的网站全世界足球排名国家
  • 重庆酉阳网站设计公司搜索关键词的软件
  • 网站建设平台赚钱做seo推广一年大概的费用
  • 怎么做老虎机网站的做营销怎样才能吸引客户
  • 广东建设信息网站网络营销网站推广方法
  • 榆林网站建设关键词调词平台
  • 做网站主要用哪种语言友谊平台
  • 做网站用旧域名好不好想要推广网页正式版
  • 手机ps网页版在线制作厦门seo推广外包
  • 做餐饮的网站seo网站自动推广
  • 做网站温州福建优化seo
  • 新网站怎么做seo优化google chrome官网入口
  • 学校网站建设需要多少钱公司个人怎么做网络推广
  • ppt模板做的好的网站有哪些推广普通话奋进新征程手抄报
  • 什么网站做推广比较好百度一下百度网站
  • html建站贵州seo学校
  • 一个网站建设域名的构思搜索引擎营销的模式有哪些
  • 无锡崇安网站建设百度代理公司
  • 福州做公司网站哪些平台可以发布软文
  • 青岛官网优化收费标准网店seo排名优化
  • 长沙微网站开发怎么推广产品最有效
  • 呼伦贝尔做网站的公司微信营销
  • 沪浙网站网络营销推广处点
  • 高端网站建设成都seo推广服务哪家好
  • 怀化网站建设哪家便宜百度大全
  • 可以免费做会计题的网站简述网站推广的方式
  • 自己做效果图的网站合肥网络推广外包