Content Table

Lambda and Stream 笔记

创建 Stream:

1
2
3
4
Stream<String>  stringStream1  = list.stream();
Stream<String> stringStream2 = Stream.of("taobao");
Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 5);
Stream<Integer> integerStream1 = Stream.iterate(1, item -> item + 1).limit(10);

Fabric 笔记

Fabric 是基于 HTML5 canvas 的图形库,通过操作对象的属性来操作 canvas (叫 object model),已经提供了一些基本的图元,如 Rect, Circle, Ellipse, Line, Polygon, Image, SVG, Text, Textbox, 阴影等,支持动画、拖动放大缩小旋转等,还支持自由绘制,能够序列化为字符串方便保存,需要的时候再把字符串反序列化为 fabric.Canvas 对象。

Fabric.js is a powerful and simple Javascript HTML5 canvas library. Fabric provides interactive object model on top of canvas element. Fabric also has SVG-to-canvas (and canvas-to-SVG) parser.

下面列举一些可能会常用到的知识点:

  • 全局设置默认值: fabric.Object.prototype.transparentCorners = false (cornerSize: 7)
  • 导出时包含指定 / 自定义属性: canvas.toJSON(['name', 'lockScalingY'],默认很多属性都没有到处,根据自己的需要在这里指定
  • 设置选中 item 的属性: canvas.getActiveObject().set("fontFamily", 'Monaco')
  • 修改整个 canvas 的鼠标: canvas.defaultCursor = 'move'
  • <canvas width="100" height="100"> 的大小被 Canvas 中指定的值覆盖: new fabric.Canvas('c', { width: 600, height: 600 })
  • 控制点 controls 指的是选中图形后在图形边框上出现的 8 个用来控制图形大小的小方框,名字为 tl, tr, br, bl, ml, mt, mr, mb
  • canvas.renderAll() 会立即重绘,canvas.requestRenderAll() 则是把重绘的命令放到事件队列里,如果准备放入时已经存在则丢弃,不重复放入队列中
  • 常用属性: Well, as you would expect there are those related to
    • positioning: left, top
    • dimension: width, height
    • rendering: fill, opacity, stroke, strokeWidth
    • scaling and rotation: scaleX, scaleY, angle
    • flipping: flipX, flipY
    • skewing: skewX, skewY

Fabric Demos 里有很多例子,对学习 Fabric 非常有帮助。

微信网页中精确定位

支持 H5 的浏览器提供了获取 GPS 的接口 navigator.geolocation.getCurrentPosition(success, error),由于各种原因,国内得到的定位都是不准确的,百度地图 API 也提供了定位接口 new BMap.Geolocation().getCurrentPosition(callback),不过也不准确,有时候会偏离几十公里,为了能够在浏览器里精确的定位,可以在微信浏览器中打开网页,使用微信的 JS SDK 中的 wx.getLocation() 进行定位得到大地坐标系 WGS84 的坐标,然后转换为百度 BD09 坐标,使用百度 API 获取此坐标对应的中文地址。

动态切换数据源

动态切换数据源的原理为使用 AbstractRoutingDataSource,根据数据源的名字查找数据源:

  1. 在 Spring IoC 容器中创建一个 AbstractRoutingDataSource 实现的对象 routingDataSource (Spring 默认没有使用此数据源)
  2. 创建一个数据源的 Map dataSourceMap,key 为 DataSource 的名字,value 为 DataSource 的对象
  3. 调用 routingDataSource.setTargetDataSources(dataSourceMap) 设置可供使用的数据源
  4. 在需要使用数据源的时候,Spring JDBC 会调用 routingDataSource.determineTargetDataSource() 获取数据源,而要返回哪个数据源,由 routingDataSource.determineCurrentLookupKey() 返回的数据源的名字决定

下面我们从最简单的方式实现数据源切换,然后一步一步的深入优化基于 AbstractRoutingDataSource 的数据源。

Promise 知识点

可以访问 Promise 对象基础 对 Promise 进行了解,这里就不再赘述了。

Promises 承诺,你将会得到延期或长期运行任务的未来结果。承诺有两个渠道:第一个为结果,第二个为潜在的错误。要获取结果,您将回调函数作为 then 函数参数,要处理错误,您将回调函数提供为 catch 函数参数。Promise 的状态的改变是单向的,一次性的,一旦改变,状态就会凝固了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
</head>

<body>
<script type="text/javascript">
let p = new Promise((resolve, reject) => {
setTimeout(function() {
const result = Math.random();
result > 0.5 ? resolve(result) : reject('Lower than 0.5');
}, 2000);
});

p.then(result => {
console.log('success', result);
}).catch(result => {
console.log('fail', result);
});
</script>
</body>

</html>

Html 转为 Pdf

使用 html2pdf 把 Html 转为 Pdf,实际项目中可以先借助 Freemarker 模板生成 Html,然后再转换为 Pdf。

缺点: html2pdf 不支持 CSS 的 flex 和 grid 布局。

依赖

1
implementation "com.itextpdf:html2pdf:2.1.7"

转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.layout.font.FontProvider;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Html2Pdf {
public static void main(String[] args) throws IOException {
File htmlSrc = new File("/Users/Biao/Desktop/x.html");
File pdfDest = new File("/Users/Biao/Desktop/x.pdf");

// 使用中文字体解决不显示中文问题: 字体文件可以从系统中找,也可以从网上下载
FontProvider fontProvider = new FontProvider();
fontProvider.addStandardPdfFonts();
fontProvider.addFont("/Users/Biao/Desktop/Yahei.ttf");
// fontProvider.addDirectory("..."); // 添加文件夹下的所有字体

ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setCharset("UTF-8");
converterProperties.setFontProvider(fontProvider);

// HTML 中本地图片要使用 file:// 的格式,如 file:///Users/Biao/Desktop/shot.png
HtmlConverter.convertToPdf(htmlSrc, pdfDest, converterProperties);
}
}

图-最短路径-Floyd

使用 Floyd 算法求任一点到其他所有点 (任意两点) 之间的最短距离: 对于每个顶点 v,和任一顶点对 (i, j), i!=j, v!=i, v!=j,如果 A[i][j] > A[i][v]+A[v][j],则将 A[i][j] 更新为 A[i][v]+A[v][j] 的值,并且将 Prev[i][j] 改为 Prev[v][j]:

  • 距离表 (初始化为图的邻接矩阵)
  • 前驱表 (初始化为每点到其他任一点的前驱为自己)
  • 三层循环:
    • 第一层: 中间点 [A, B, C, D, E, F, G]
    • 第二层: 出发点 [A, B, C, D, E, F, G]
    • 第三层: 终结点 [A, B, C, D, E, F, G]
    • 出发点通过中间点到终结点的距离、出发点直连终结点的距离选最小值更新距离表: min(Lik+Lkj, Lij),同时更新前驱表

第一轮: 以 A 为中间节点,点 X 通过 A 到点 Y 的距离为 min(XA+AY, XY): BAA, BAB, BAC, BAD, BAE, BAF, BAG, CAA, CAB, …
第二轮: 以 B 为中间节点,…

第七轮: 以 G 为中间节点,…

1
2
3
4
5
6
7
8
9
10
11
12
// 核心: Floyd 算法计算任意 2 点之间的最短距离
final int len = distance.length;
for (int v = 0; v < len; v++) { // 第一层: 中间点
for (int i = 0; i < len; i++) { // 第二层: 出发点
for (int j = 0; j < len; j++) { // 第三层: 终结点
if (distance[i][v] + distance[v][j] < distance[i][j]) {
distance[i][j] = distance[i][v] + distance[v][j];
path[i][j] = path[v][j];
}
}
}
}

图-最短路径-Dijkstra

求图中一点到其他点的最短路径可使用 Dijkstra 算法 (使用广度优先策略):

  • 使用优先级队列实现找最小权重的点 (数组遍历也可以)
  • 前驱节点数组
  • 权重节点数组 (已访问节点)
  • 连接表的存储: Map<String, List<String>>: key 为节点名字,List 为邻接表

图-最小生成树-Kruskal

最小生成树 (Minumum Cost Spanning Tree,简称 MST) 的 2 个经典算法:

  • Prim (普里姆算法): 从顶点出发
  • Kruskal (克鲁斯卡尔算法): 从边出发

网: 带权无向图
最小生成树: 在包含 n 个顶点的连通图中,找出只有 (n-1) 条边,包含所有 n 个顶点的连通子图,也就是所谓的极小连通子图

下面图解 Prim 算法生成最小生成树的过程:

图-最小生成树-Prim

最小生成树 (Minumum Cost Spanning Tree,简称 MST) 的 2 个经典算法:

  • Prim (普里姆算法): 从顶点出发
  • Kruskal (克鲁斯卡尔算法): 从边出发

网: 带权无向图
最小生成树: 在包含 n 个顶点的连通图中,找出只有 (n-1) 条边,包含所有 n 个顶点的连通子图,也就是所谓的极小连通子图

普里姆 (Prim) 算法求最小生成树算法如如下:

  1. 设 G = (V, E) 是联通网,T = (U, D) 是最小生成树,V, U 是顶点集合,E, D 是边的集合
  2. 若从顶点 u 开始构造最小生成树,则从集合 V 中取出顶点 u 放入集合 U 中,标记顶点 u 被访问过了: visited[u] = 1
  3. 若集合 U 中顶点 ui 与集合 V-U 中的顶点 vj 之间存在边,则寻找这些边中权值最小的边,但不能构成回路,将顶点 vj 加入集合 U 中,将边 (ui, vj) 加入集合 D 中,标记 visited[vj] = 1
  4. 重复步骤 3,直到 U 与 V 相等,即所有顶点都被标记为访问过,此时 D 中有 n-1 条边

不管从哪一个顶点开始构建最小生成树,最后得到的最小生成树的边的权值加起来都相等。

下面图解 Prim 算法生成最小生成树的过程,其中:

  • 黑色节点表示未访问过节点
  • 黄色节点表示已访问过节点
  • 红色节点表示未访问过,但是将选择为访问的节点
  • 红色的边为最小生成树中的边
  • 灰色的边为不需要在判断的边,因为它的 2 个顶点都访问过了
  • 黄色的边,其有 1 个顶点被访问过了,另一个顶点未被访问