效果

最终实现的整体效果如下:

实现

看完效果以后,接下来就带领大家来看看是怎样一步一步实现最终效果的,在正式动手写代码之前,先对整个效果做一个简单的拆分,将其分为五个部分:

  1. 点击弹出红包
  2. 红包整体布局
  3. 金币点击旋转
  4. 红包开启动画
  5. 结果页面弹出

拆分后如下图所示:

接下来就一步一步来实现。

红包弹出

红包弹出主要分为两部分:从小到大缩放动画、半透明遮罩。很自然的想到了使用 Dialog 来实现,最终也确实使用 Dialog 实现了对应的效果,但是在最后展示结果页的时候出现问题了,因为红包开启与结果展示是同时进行的,结果页在红包下面,使用 Dialog 的话会存在结果页在 Dialog 上面遮住红包的效果,最后使用了 Overlay 在顶层添加一个 Widget 来实现。

创建一个 RedPacket 的 Widget:

class RedPacket extends StatelessWidget {const RedPacket({Key? key}) : super(key: key);@overrideWidget build(BuildContext context) {return Center(child: Container(width: 0.8.sw,height: 1.2.sw,color: Colors.redAccent,));}
}

内容很简单,就是一个居中的宽高分别为 0.8.sw1.2.sw 的 Container,颜色为红色。这里 sw 是代表屏幕宽度,即红包宽度为屏幕宽度的 0.8 倍,高度为屏幕宽度的 1.2 倍。

然后点击按钮时通过 Overlay 展示出来, 创建一个 showRedPacket 的方法:

void showRedPacket(BuildContext context){OverlayEntry entry = OverlayEntry(builder: (context) => RedPacket());Overlay.of(context)?.insert(entry);
}

效果如下:

红包是弹出来了,但因为没有缩放动画,很突兀。为了实现缩放动画,在 Container 上包裹 ScaleTransition 用于缩放动画,同时将 RedPacket 改为 StatefulWidget ,因为使用动画需要用到 AnimationController 传入 SingleTickerProviderStateMixin ,实现如下:

class RedPacket extends StatefulWidget {const RedPacket({Key? key}) : super(key: key);@overrideState<RedPacket> createState() => _RedPacketState();
}class _RedPacketState extends State<RedPacket> with SingleTickerProviderStateMixin {late AnimationController scaleController = AnimationController(vsync: this)..duration = const Duration(milliseconds: 500)..forward();@overrideWidget build(BuildContext context) {return Container(color: Color(0x88000000), /// 半透明遮罩child: Center(child: ScaleTransition(scale: Tween<double>(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: scaleController, curve: Curves.fastOutSlowIn)),child: Container(width: 0.8.sw,height: 1.2.sw,color: Colors.redAccent,),)),);}
}

ScaleTransition 设置动画从 0.0 到 1.0 即从无到原本大小,动画时间为 500 毫秒;同时在外层再包裹一层 Container 并为其添加半透明颜色实现半透明遮罩,最终实现效果:

这样就实现了第一部分的功能。

红包布局

标题说了是使用 Canvas 来实现,所以红包布局主要是使用 Canvas 来实现,将前面红包的 Container 换成 CustomPaint, 然后创建 RedPacketPainter 继承自 CustomPainter :

ScaleTransition(scale: Tween<double>(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: scaleController, curve: Curves.fastOutSlowIn)),child: CustomPaint(size: Size(1.sw, 1.sh),painter: RedPacketPainter(),),
)

考虑到后续动画,这里将画布的大小设置为全屏。红包布局的核心代码就在 RedPacketPainter 里,首先绘制红包的背景,背景分为上下两部分,上部分又由一个矩形和一个圆弧组成,下半部分同样是由一个矩形和一个圆弧组成,上半部分的圆弧是凸出来的,而下半部分的是凹进去的,示意图如下:

初始化:

/// 画笔
late final Paint _paint = Paint()..isAntiAlias = true;
/// 路径
final Path path = Path();/// 红包的高度:1.2倍的屏幕宽度
late double height = 1.2.sw;/// 上半部分贝塞尔曲线的结束点
late double topBezierEnd = (1.sh - height)/2 + height/8*7;
/// 上半部分贝塞尔曲线的起点
late double topBezierStart= topBezierEnd - 0.2.sw;
/// 下半部分贝塞尔曲线的起点
late double bottomBezierStart = topBezierEnd - 0.4.sw;/// 金币中心点,后续通过path计算
Offset goldCenter = Offset.zero;/// 横向的中心点
final double centerWidth = 0.5.sw;/// 红包在整个界面的left
late double left = 0.1.sw;
/// 红包在整个界面的right
late double right = 0.9.sw;
/// 红包在整个界面的top
late double top = (1.sh - height)/2;
/// 红包在整个界面的bottom
late double bottom = (1.sh - height)/2 + height;

上半部分

代码实现如下:

void drawTop(ui.Canvas canvas) {  path.reset();path.addRRect(RRect.fromLTRBAndCorners(left, top, right, topBezierStart, topLeft: const Radius.circular(5), topRight: const Radius.circular(5)));var bezierPath = getTopBezierPath();path.addPath(bezierPath, Offset.zero);path.close();canvas.drawShadow(path, Colors.redAccent, 2, true);canvas.drawPath(path, _paint);
}

这里使用 Path 来进行绘制,首先向路径中添加一个圆角矩形,也就是示意图中的第①部分,然后通过 getTopBezierPath 获取一个贝塞尔曲线的 bezierPath 并将其添加到 path 路径中,getTopBezierPath 源码如下:

Path getTopBezierPath() {Path bezierPath = Path();bezierPath.moveTo(left, topBezierStart);bezierPath.quadraticBezierTo(centerWidth, topBezierEnd, right , topBezierStart);var pms = bezierPath.computeMetrics();var pm = pms.first;goldCenter = pm.getTangentForOffset(pm.length / 2)?.position ?? Offset.zero;return bezierPath;
}

getTopBezierPath 源码分为两部分,第一部分是创建贝塞尔曲线的 path ,使用的是最开始初始化的数据创建,实现示意图中的第②部分内容;然后根据创建好的贝塞尔曲线的 path 计算出路径中中间点的坐标,作为金币中心点坐标。示意图如下:

图中红点就是贝塞尔曲线的点,中间实线就是贝塞尔曲线,也就是上面代码中创建的贝塞尔曲线路径,实线中间的点就是金币位置的中心点。

贝塞尔曲线绘制完成后调用 drawShadow 绘制阴影,作用是突出上下两部分连接处的效果,最后通过 path 绘制出整个上半部分的效果,如下:

下半部分

代码实现如下:

void drawBottom(ui.Canvas canvas) {path.reset();path.moveTo(left, bottomBezierStart );path.quadraticBezierTo(centerWidth, topBezierEnd, right , bottomBezierStart);path.lineTo(right, topBezierEnd);path.lineTo(left, topBezierEnd);path.addRRect(RRect.fromLTRBAndCorners(left, topBezierEnd, right, bottom, bottomLeft: const Radius.circular(5), bottomRight: const Radius.circular(5)));path.close();canvas.drawShadow(path, Colors.redAccent, 2, true);canvas.drawPath(path, _paint);
}

下半部分实现同样分为两部分,首先绘制出贝塞尔曲线,即示意图第③部分,然后再添加一个圆角矩形,即示意图第④部分;然后绘制下半部分的阴影和图形,单独展示下半部分效果如下:

将上下两部分结合起来,就实现了红包背景的效果,如下:

金币绘制

背景绘制完成后接下来进行金币的绘制,前面已计算出金币的中心点坐标,静态金币的绘制就相对来说比较简单了,就是一个圆形,代码如下:

void drawGold(ui.Canvas canvas){Path path = Path();canvas.save();canvas.translate(0.5.sw, goldCenter.dy);_paint.style = PaintingStyle.fill;path.addOval(Rect.fromLTRB(-40.w , -40.w, 40.w , 40.w));_paint.color = const Color(0xFFFCE5BF);canvas.drawPath(path, _paint);canvas.restore();
} 

这里将画布移动到到金币的中心点,然后向 Path 中添加添加一个半径为 40.w 的圆,最后将 path 绘制出来即可。效果如下:

金币文字绘制

金币绘制出来后,还需在金币上绘制一个繁体的 “開” 字,代码如下:

void drawOpenText(ui.Canvas canvas) {if(controller.showOpenText){TextPainter textPainter = TextPainter(text: TextSpan(text: "開",style: TextStyle(fontSize: 34.sp, color: Colors.black87, height: 1.0, fontWeight: FontWeight.w400)),textDirection: TextDirection.ltr,maxLines: 1,textWidthBasis: TextWidthBasis.longestLine,textHeightBehavior: const TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false))..layout();canvas.save();canvas.translate(0.5.sw, goldCenter.dy);textPainter.paint(canvas, Offset(- textPainter.width / 2, -textPainter.height/2));canvas.restore();}
} 

使用 TextPainter 进行文字的绘制,同样是将画布移动到金币的中心,然后绘制文字,效果如下:

头像和文字

经过上面的绘制,效果已经出来了,但是还差红包封面上的用户头像相关文字,使用 Canvas 同样能实现,但这里并没有使用 Canvas 来实现,而是使用 CoustomPaint 的 child 来实现:

CustomPaint(size: Size(1.sw, 1.sh),painter: RedPacketPainter(controller: controller),child: buildChild(),
)Container buildChild() {return Container(padding: EdgeInsets.only(top: 0.3.sh),child: Column(crossAxisAlignment: CrossAxisAlignment.center,children: [Row(mainAxisAlignment: MainAxisAlignment.center,children: [ClipRRect(borderRadius: BorderRadius.circular(3.w),child: Image.network("https://p26-passport.byteacctimg.com/img/user-avatar/32f1f514b874554f69fe265644ca84e4~300x300.image", width: 24.w,)),SizedBox(width: 5.w,),Text("loongwind发出的红包", style: TextStyle(fontSize: 16.sp, color: Color(0xFFF8E7CB), fontWeight: FontWeight.w500),)],),SizedBox(height: 15.w,),Text("恭喜发财", style: TextStyle(fontSize: 18.sp, color: Color(0xFFF8E7CB)),)],),);
} 

CoustomPaint 的 child 允许传入一个 Widget,Widget 的实现就是要显示的头像和文字,代码如上,最终效果如下:

至此红包的整个布局的实现就完成了。

金币旋转

前面完成了红包的静态显示,接下来就看看怎么让红包动起来,首先看看怎么让金币旋转起来。

说到旋转首先想到的就是以金币的中心旋转,可以通过旋转画布的旋转或者 path 的 transform 旋转来实现,但是经过实验使用这种方式能让金币旋转起来,但是做到旋转的立体效果却很复杂。所以最终采用的是使用两个圆在 x 轴上进行一定的偏移,然后压缩圆的宽度来模拟实现旋转效果,示意图如下:

如图所示,绘制两个相同的圆,开始时将两个圆重叠在一起,然后同时压缩圆的宽度并将下层的圆向左偏移一定单位,就形成了旋转的立体效果。

代码:

void drawGold(ui.Canvas canvas){Path path = Path();canvas.save();canvas.translate(0.5.sw, goldCenter.dy);_paint.style = PaintingStyle.fill;path.addOval(Rect.fromLTRB(-40.w , -40.w, 40.w , 40.w));_paint.color = const Color(0xFFE5CDA8);canvas.drawPath(path, _paint);_paint.color = const Color(0xFFFCE5BF);canvas.drawPath(path, _paint);canvas.restore();
} 

修改上面绘制金币的代码,设置不同的颜色再绘制一个圆,这样就在同一个位置绘制了两个不同颜色的圆。那么怎么让它动起来呢?可以使用动画,通过动画执行宽度的缩放,是宽度系数从 1 缩放到 0 再从 0 回到 1。因为 CustomPainter 是继承自 Listenable ,而动画也是 Listenable 所以直接将动画与 CustomPainter 结合起来使用更方便。

为了方便统一控制红包的动画,创建一个 RedPacketController,并在里面创建一个控制金币旋转的动画及控制器:

class RedPacketController{final SingleTickerProviderStateMixin tickerProvider;late AnimationController angleController;late Animation<double> angleCtrl;RedPacketController({required this.tickerProvider}){initAnimation();}void initAnimation() {angleController = AnimationController(duration: const Duration(seconds: 3),vsync: tickerProvider)..repeat(reverse: true);angleCtrl = angleController.drive(Tween(begin: 1.0, end: 0.0));}void dispose(){angleController.dispose();timer?.cancel();}
} 

为了看到旋转的效果,将动画执行时间设置为 3 秒,并且让其重复执行,重复执行时设置 reverse 为 true,即反向执行,然后改造 _RedPacketState 混入 SingleTickerProviderStateMixin 并创建 RedPacketController :

class _RedPacketState extends State<RedPacket> with SingleTickerProviderStateMixin{late RedPacketController controller = RedPacketController(tickerProvider: this);Widget buildRedPacket() {return GestureDetector(onTapUp: controller.clickGold,child: CustomPaint(size: Size(1.sw, 1.sh),painter: RedPacketPainter(controller: controller),child: buildChild(),),);}/// ...
}class RedPacketPainter extends CustomPainter{RedPacketController controller;RedPacketPainter({required this.controller}) : super(repaint:controller.angleController);/// ...
} 

RedPacketPainter 的构造方法调用了 super 并传入了 repaint 参数,即创建的动画控制器。

这样就能在绘制金币的时候使用动画的值了:

void drawGold(ui.Canvas canvas){Path path = Path();double angle = controller.angleCtrl.value;canvas.save();canvas.translate(0.5.sw, goldCenter.dy);_paint.style = PaintingStyle.fill;path.addOval(Rect.fromLTRB(-40.w * angle , -40.w, 40.w * angle, 40.w));_paint.color = const Color(0xFFE5CDA8);canvas.drawPath(path, _paint);_paint.color = const Color(0xFFFCE5BF);canvas.drawPath(path, _paint);canvas.restore();
} 

通过 controller.angleCtrl.value 获取当前动画的值,然后在圆的 left 和 right 参数上乘以这个值,看一下效果:

效果已经有了,但是发现在旋转到最小的时候中间是空的,这不符合我们的预期,那怎么办呢?将两个圆的边一一连接起来是不是中间就不空了,如图所示:

代码实现:

void drawGoldCenterRect(ui.Path path, ui.Path path2, ui.Canvas canvas) {var pms1 = path.computeMetrics();var pms2 = path2.computeMetrics();var pathMetric1 = pms1.first;var pathMetric2 = pms2.first;var length = pathMetric1.length;Path centerPath = Path();for(int i = 0; i <length; i++){var position1 = pathMetric1.getTangentForOffset(i.toDouble())?.position;var position2 = pathMetric2.getTangentForOffset(i.toDouble())?.position;if(position1 == null || position2 == null){continue;}centerPath.moveTo(position1.dx, position1.dy);centerPath.lineTo(position2.dx, position2.dy);}Paint centerPaint = Paint()..color = const Color(0xFFE5CDA8)..style = PaintingStyle.stroke..strokeWidth=1;canvas.drawPath(centerPath, centerPaint);
} 

使用 Path.computeMetrics 计算两个 path 的路径点,循环路径每一个点,将两个 path 的每一个点连接起来然后绘制出来,再来看一下效果:

效果好多了,但是仔细观察发现还是有一个问题,金币看着不是旋转的而是左右摇摆的,这是因为实现的立体的效果一直在一边导致的,需要根据旋转的时机将立体效果的方向切换,从 1 到 0 时在右边,从 0 到 1 时在左边,通过动画的状态进行判断,修改代码如下:

var frontOffset = 0.0;
var backOffset = 0.0;
if(controller.angleCtrl.status == AnimationStatus.reverse){frontOffset = 4.w;backOffset = -4.w;
}else if(controller.angleCtrl.status == AnimationStatus.forward){frontOffset = -4.w;backOffset = 4.w;
}var path2 = path.shift(Offset(backOffset * (1 - angle),  0));
path = path.shift(Offset(frontOffset * (1 - angle), 0)); 

再来看一下效果:

这样旋转效果就很完美了,金币中间还缺一个空心的矩形,实现很简单,在圆的 path 路径中叠加一个矩形即可:

path.addRect(Rect.fromLTRB(-10.w * angle , -10.w, 10.w * angle , 10.w));
path.fillType = PathFillType.evenOdd; 

设置fillTypeevenOdd ,这样就形成了中心空心的效果,并且由于上面连接两个圆的路径点,这个空心也自带了立体效果,如图:

最后为金币添加点击事件,点击时开启旋转,并隐藏金币上的文字。点击事件可以直接给 CustomPaint 包裹一个 GestureDetector ,点击时判断点击坐标是否在金币的绘制范围内,可以使用 Path.contains 进行判断,所以需要保存金币的 path 用于点击判断,这里将其保存到 controller 里:

controller.goldPath = path.shift(Offset(0.5.sw, goldCenter.dy)); 

然后在 RedPacketController 里定义对应的变量和方法:

class RedPacketController{Path? goldPath;bool showOpenText = true;bool checkClickGold(Offset point){return  goldPath?.contains(point) == true;}void clickGold(TapUpDetails details) {if(checkClickGold(details.globalPosition)){angleController.repeat(reverse: true);tickerProvider.setState(() {showOpenText = false;});}}/// ...
} 

goldPath 用于保存金币的 path,showOpenText 用于是否显示金币上的文字,点击时判断事件触发点是否在金币范围内,在金币范围内则触发动画启动,并设置金币上的文字不显示。

为 CustomPaint 添加点击事件:

Widget buildRedPacket() {return GestureDetector(onTapUp: controller.clickGold,child: CustomPaint(size: Size(1.sw, 1.sh),painter: RedPacketPainter(controller: controller),child: buildChild(),),);
} 

看一下效果 :

红包开启

红包开启其实就是将红包上下两部分分别进行向上和向下的平移,再加上背景颜色的渐变实现,示意图如下:

加上之前金币的动画,存在多个动画控制器,所以需要将 _RedPacketState 修改为混入 TickerProviderStateMixin:

class _RedPacketState extends State<RedPacket> with TickerProviderStateMixin{/// ...
} 

然后在 RedPacketController 添加平移动画控制器,并添加平移和颜色渐变动画,平移系数从 0 到 1, 颜色渐变从不透明到完全透明。并将平移动画与之前的金币动画合并为 repaint

class RedPacketController{final TickerProviderStateMixin tickerProvider;late AnimationController angleController;late AnimationController translateController;late Animation<double> translateCtrl;late Animation<Color?> colorCtrl;void initAnimation() {/// ...translateController = AnimationController(duration: const Duration(milliseconds: 800),vsync: tickerProvider);translateCtrl = translateController.drive(Tween(begin: 0.0, end: 1.0));colorCtrl = translateController.drive(ColorTween(begin: Colors.redAccent,end: const Color(0x00FF5252)));repaint = Listenable.merge([angleController, translateController]);}
} 

再在 RedPacketPainter 的 super 里传入 repaint:

RedPacketPainter({required this.controller}) : super(repaint:controller.repaint); 

改造绘制红包上半部分代码:

void drawTop(ui.Canvas canvas) {canvas.save();canvas.translate(0, topBezierEnd  * ( - controller.translateCtrl.value));/// ...canvas.restore();
} 

添加画布平移操作,平移的 Y 值为上半部分高度乘以动画值,即从 0 向上移动上半部分高度。

下半部分添加同样的处理,平移方向向下:

void drawBottom(ui.Canvas canvas) {canvas.save();canvas.translate(0, topBezierStart * (controller.translateCtrl.value));/// ...canvas.restore();
} 

效果如下:

背景的平移效果实现了,但是上面的头像和文字没动,接下来给头像和文字的 Widget 添加 AnimatedBuilder 使用相同的动画让其跟着移动:

Widget buildChild() {return AnimatedBuilder(animation: controller.translateController,builder: (context, child) => Container(padding: EdgeInsets.only(top: 0.3.sh * (1 - controller.translateCtrl.value)),child: Column(...),),);} 

通过动态修改 paddingTop 的值,让头像与文字也向上平移。效果如下:

最后在金币点击事件上添加一个定时器,金币旋转 2 秒后执行红包开启动画:

void clickGold(TapUpDetails details) {if(checkClickGold(details.globalPosition)){if(angleController.isAnimating){stop();}else{angleController.repeat(reverse: true);tickerProvider.setState(() {showOpenText = false;});timer = Timer(const Duration(seconds: 2), (){stop();});}}
}
void stop() async{if(angleController.isAnimating){  ///停止金币动画,让动画看起来更自然if(angleController.status == AnimationStatus.forward){await angleController.forward();angleController.reverse();}else if(angleController.status == AnimationStatus.reverse){angleController.reverse();}tickerProvider.setState(() {showOpenBtn = false;});translateController.forward();}
} 

这样就实现了点击金币后,金币旋转 2 秒后开启红包。

结果弹出

结果页是一个新的界面,在红包开启时同步执行,并且拥有一个渐变动画,路由跳转时添加动画实现,代码如下:

void onOpen(){Navigator.push(context,PageRouteBuilder(transitionDuration: const Duration(seconds: 1),pageBuilder: (context, animation, secondaryAnimation) =>FadeTransition(opacity: animation,child: const ResultPage(),)));
} 

RedPacket 添加一个红包开启时的回调和红包动画完成时的回调,前者用于跳转结果页,后者用于移除 Overlay

OverlayEntry? entry;
void showRedPacket(BuildContext context, Function? onOpen){entry = OverlayEntry(builder: (context) => RedPacket(onFinish: _removeRedPacket, onOpen: onOpen,));Overlay.of(context)?.insert(entry!);
}void _removeRedPacket(){entry?.remove();entry = null;
} 

在金币旋转动画停止时调用:

void stop() async{if(angleController.isAnimating){  /// ...translateController.forward();onOpen?.call();}
} 

红包动画完成时调用 onFinish 回调, 给红包最后的平移动画添加监听来实现:

translateController.addStatusListener((status) {if(status == AnimationStatus.completed){onFinish?.call();}
}); 

OK,大功告成,再看看最终的效果:

文末

为了帮助大家更好的理解flutter,我给大家准备了一份《Flutter进阶学习笔记》,相信大家能在它的帮助下快速掌握flutter的知识,有需要的朋友可以扫码自取

《Flutter进阶学习笔记》

目录

第一章 为什么 Flutter 是跨平台开发的终极之选

第二章 在Windows上搭建Flutter开发环境

第三章 编写您的第一个 Flutter App

第四章 Flutter开发环境搭建和调试

第五章 Dart语法篇之基础语法(一)

第六章 Dart语法篇之集合的使用与源码解析(二)

第七章 Dart语法篇之集合操作符函数与源码分析(三)

第八章 Dart语法篇之函数的使用(四)

第九章 Dart语法篇之面向对象基础(五)

第十章 Dart语法篇之面向对象继承和Mixins(六)

第十一章 Dart语法篇之类型系统与泛型(七)

第十二章 Flutter中的widget

Flutter使用Canvas实现微信红包领取效果相关推荐

  1. Canvas实现微信红包照片效果

    本篇文章来源慕课网课程<canvas玩转红包照片>,用canvas及css3结合,实现红包照片的效果,并不做支付过程,代替的是使用2个按钮,显示清晰图片和重置圆圈可见区域.未做移动的屏幕适 ...

  2. html微信拆红包动画特效,利用jQuery实现微信红包领取动画特效

    特效描述:利用jQuery实现 微信红包 领取动画特效.利用jQuery实现微信红包领取动画特效 代码结构 1. 引入CSS 2. 引入JS 3. HTML代码 点击打开 $(function () ...

  3. android仿微信红包动画,Android仿打开微信红包动画效果实现代码

    首先看下效果: 实现原理: 准备3张不同角度的图片,通过AnimationDrawable帧动画进行播放即可 代码实现: 1.编写动画xml文件: 根标签为animation-list,其中onesh ...

  4. php微信红包雨效果,微信红包雨特效口令大全 微信红包雨特效口令有哪些

    微信红包雨特效口令有哪些?新年牛气冲天,在微信有一场大型的红包雨送给玩家.那么特效口令有哪些呢,下面小编给大家带来微信红包雨特效口令大全,一起来看看吧. 微信红包雨特效口令大全 一.王者荣耀 [排位] ...

  5. php微信红包雨效果,【微信红包雨】 原来红包还可以这么玩!你造吗?

    原标题:[微信红包雨] 原来红包还可以这么玩!你造吗? 哈啰,大家好,我是林涛 今天用实战演练的方法带着小伙伴们做"精推". 每一次体验的小伙伴们想必被今晚的红包雨吓到了,事实上我 ...

  6. Canvas模仿微信红包照片

    1.最终效果: 2.实现过程: 1.将图片加载出来,并加上模糊效果: index.html: <!DOCTYPE html> <html> <head lang=&quo ...

  7. css 微信红包,js微信红包实现方案

    大家肯定都使用过微信的红包功能,对微信红包的规则有一定的了解,它的规则是用户可以输入金额和红包的个数,然后供其他人去抽取,每个人最少可以抽到0.01元.一般最多可以抽到200元,那这个微信红包的效果是 ...

  8. 多个微信红包,支付宝红包免费领

    福利分享,亲测有效.多个支付宝,微信红包免费领取,分分钟到账. 最高10元微信红包 最高100元支付宝红包 可帮忙协助提现支付宝红包 最低1元微信红包免费领 最低1元支付宝红包免费领 最低1元微信红包 ...

  9. 炸裂了!来了一波新年微信红包封面,抓紧领取,先到先得!

    新的一年,新气象,你的微信红包封面是不是该换换了.看别人都有与众不同的红包封面,你柠檬了吗? 福利来袭,先到先得,大家抓紧抢数量有限的微信红包封面呀! 微信红包封面 第一款[金典]红包封面 红包封面发 ...

最新文章

  1. linux操作系统颜色,Linux小技巧之man pages设置高亮颜色
  2. Go如何按行读取文本
  3. 关于Linux你了解多少?Linux由来!
  4. [2020-11-23 contest]图(dfs剪枝),劫富济贫(字典树),小A的树(树形DP),游戏(贪心/斜率优化)
  5. 边缘检测后去除噪点_Street Lanes Finder - 检测自动驾驶汽车的车道
  6. Android 颜色如何从十六进制如何转成八进制 + 颜色库
  7. 数据挖掘概念与技术(第三版)课后答案——第一章
  8. L3HCTF bypass出题人视角
  9. 使用手持式频谱分析仪进行TDD信号分析
  10. android pc投屏,如何解决乐播投屏中投屏失败的问题【乐播投屏】
  11. 反激变压器的设计-电子研习社
  12. wps小技巧,wps删除空白页怎么删?
  13. 计算机网络技术报告一份,计算机网络技术》实验的报告.doc
  14. Python——自动签到脚本
  15. 【随笔记】Deepin20系统更换fish,替代bash
  16. linux查询打印机ip,Linux C打印IP地址信息
  17. VMWare虚拟机 网络连接模式
  18. AI+安防,怎样引领安防行业的变革?
  19. Kafka时间轮学习总结
  20. 遵化市10余个局委办一把手调研九次方大数据,详细部署数字遵化建设

热门文章

  1. 基于 Node.js + Koa 构建完整的 Web API (配置 ESLint 和使用 Airbnb 编码规范)
  2. linux脚本出错仍执行后续脚本,crontab执行脚本出错
  3. (排序3)希尔排序时间复杂度与直接选择排序
  4. 双柱状图与双折线图混合
  5. win10专业版2020下载链接
  6. 处女座女的爱情黑暗面 水瓶座男不爱你的表现是什么
  7. 梦中情人sbl新变种snow.exe,snowfall.exe的分析
  8. win10 高DPI 高分屏 解决模糊问题的方法
  9. python计算机视觉-1.2.2 图像轮廓与直方图
  10. Shader学习建议