女神节 | 程序员如何低调而又不失逼格
老
孟
一枚
有态度
的程序员
今天。。。是一个非常重要的日子-女神节。作为一名程序员,如何向心仪的人低调而又不失逼格的表达祝福,关系到我们后半生的幸福,祝福的到位,普通朋友加个字变成女朋友,女朋友变成老婆,如果已经是老婆了,那么这个月的零花钱又能多好几百,想想都激动。
回到现实,作为程序员,我们当然要独一无二,要与众不同,要突破天际,要能体现我们的身份,逼格满满,因此我们要给女神开发一个天上地下、唯吾独尊的App绝对是你不二的选择。
废话不多说,No 图 No 说话:
先来分析一波:
首先上面的效果是一直在绘制路径,当绘制完花和叶子时在对其着色,因此这里比较难是如何获取路径的点坐标,只要有点的坐标了一个个的画出来也就实现了上面的效果。
那么现在的重点就是如何获取点坐标,一种方法是人工一个一个的写,然后调整,这种方法工作量太大了,作为程序员怎么能用这种方法呢?怎么才可以让程序生成这些坐标呢?
想想我们在监听手势(鼠标)时是不是可以获取到当前点的坐标,移动的时候也可以获取到一个移动的路径坐标,因此我们只需要在屏幕上先加载想要的图片,然后按照图片上的路径移动,是不是就可以获取到我们想要的路径了啊。
对于任何语言来说都可以按照上面的思路来实现,下面以目前非常火热的Flutter来实现这个功能。
好,首先来加载一张图片,然后监听其手势(鼠标)移动事件,代码如下:
Container(
width: 400,
height: 700,
child: GestureDetector(
onLongPressStart: (LongPressStartDetails details) {
print('${details.localPosition},');
},
onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) {
print('${details.localPosition},');
},
onLongPressEnd: (LongPressEndDetails details) {
print('${details.localPosition},');
},
child: Image.asset(
'images/123.png',
fit: BoxFit.fill,
),
),
)
这里要注意2点:
- 图片显示的大小最好和最终的画布一样大小,这样得到的坐标不需要在转换。
- 图片的宽高比和画布的宽高比要一样。
我们把路径输出到控制台,后台只需将这些坐标拷贝到应用程序即可,将这些坐标定义为数组,如下:
static final List<Offset> flowerPoints = [
Offset(182.0, 136.3),
Offset(182.7, 135.3),
Offset(183.0, 135.3),
Offset(183.3, 135.3),
...
)
点到路径获取到,下面就是绘制了,先绘制红色的花骨朵,在Flutter中绘制路径需要继承CustomPainter类,重写paint
方法,绘制路径及填充颜色代码如下:
@override
void paint(Canvas canvas, Size size) {
//将花变为红色
if (flowerPaths.length >= RoseData.flowerPoints.length) {
var path = Path();
for (int i = 0; i < flowerPaths.length; i++) {
if (i == 0) {
path.moveTo(flowerPaths[i].dx, flowerPaths[i].dy);
} else {
path.lineTo(flowerPaths[i].dx, flowerPaths[i].dy);
}
}
_paint.style = PaintingStyle.fill;
_paint.color = _flowerColor;
canvas.drawPath(path, _paint);
}
//绘制线
_paint.style = PaintingStyle.stroke;
_paint.color = _strokeColor;
canvas.drawPoints(PointMode.polygon, flowerPaths, _paint);
}
这里要注意只有当当花骨朵所有的路径都绘制完之后才填充颜色,而且要先填充颜色,然后绘制路线,不然路线会被填充颜色覆盖。
要想有绘制路径的效果,需要将点依次增加,增加动画控制器,控制绘制路径,代码如下:
AnimationController _controller;
Animation<num> _animation;
@override
void initState() {
_controller = AnimationController(
duration: Duration(seconds: 10), vsync: this)
..addListener(() {
setState(() {
_flowerPaths = RoseData.flowerPoints.sublist(0, _animation.value.floor());
});
});
_animation = Tween(
begin: 0.0,
end: RoseData.flowerPoints.length)
.animate(_controller);
}
构建UI代码如下:
@override
Widget build(BuildContext context) {
Container(
width: 400,
height: 700,
child: CustomPaint(
painter: RosePaint(_flowerPaths),
),
)
}
RosePaint是自定义的CustomPaint。效果如下:
在最终的填充上发现有一部分没有填充上,图中蓝色点为最后一个点,所以需要在增加2个点,绿色和黄色位置的点,把未填充区域填充上。
填充点代码如下:
static final List<Offset> flowerPoints = [
Offset(182.0, 136.3),
Offset(182.7, 135.3),
...
Offset(179.3, 301.7),
Offset(237.7, 144.7),
];
在绘制线的时候需要将这2个点去掉:
if (flowerPaths.length >= RoseData.flowerPoints.length) {
var path = Path();
for (int i = 0; i < flowerPaths.length; i++) {
if (i == 0) {
path.moveTo(flowerPaths[i].dx, flowerPaths[i].dy);
} else {
path.lineTo(flowerPaths[i].dx, flowerPaths[i].dy);
}
}
_paint.style = PaintingStyle.fill;
_paint.color = _flowerColor;
canvas.drawPath(path, _paint);
}
//绘制线
_paint.style = PaintingStyle.stroke;
_paint.color = _strokeColor;
//去掉最后2个点,最后2个点为了绘制红色
var points = flowerPaths.sublist(0, max(0, flowerPaths.length - 2));
canvas.drawPoints(PointMode.polygon, points, _paint);
花骨朵就好了,其他的叶子和部位也是一样的画法,这里就不在介绍了,由于代码比较多,就不全贴在这里了,如果需要可以加我微信。
- 无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(3)删除记录
- 部署Zipkin分布式性能追踪日志系统的操作记录
- 无限级分类(非递归算法/存储过程版/GUID主键)完整数据库示例_(4)显示记录
- Android 2.x中使用actionbar - Actionbarsherlock (2)
- python读txt和xml
- 让Jexus支持高并发请求的优化技巧
- 数据压缩算法LZO (C#)
- Html之初体验
- 基于Wolfpack开发业务监控系统
- Android 2.x中使用actionbar - Actionbarsherlock
- Python-操作Memcache、Redis、RabbitMQ、
- 推荐[搜索引擎架构]的几篇文章
- 中小型商城系统中的分类/产品属性/扩展属性的数据库设计
- Linux下FTP虚拟账号环境部署总结
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 猿实战20——商品发布之sku与笛卡尔乘积的那些事儿
- 浅谈Android单元测试的作用以及简单示例
- linux上传并配置jdk和tomcat的教程详解
- Android SQLite事务处理结合Listview列表显示功能示例
- Android开发之如何自定义数字键盘详解
- 在Android上实现HttpServer的示例代码
- Linux /etc/network/interfaces配置接口方法
- Android中Service和Activity相互通信示例代码
- Android编程实现定时发短信功能示例
- Android实现新浪微博一键分享的实例代码
- Android实现常见的验证码输入框实例代码
- Android实现加载状态视图切换效果
- Retrofit + OkHttp缓存处理的示例代码
- Android图片实现压缩处理的实例代码
- Windows10安装linux子系统的两种方式(图文详解)