一直在学习一些 地图思想,代码上始终看不懂一些矩阵的代码,今天有幸获取别人讲解的canvans矩阵的知识,算是总结一下吧
参数说明
transform(a,b, c,d,e,f)
a 水平方向的缩放 (scale X)
B 水平方向的倾斜偏移
C 竖直方向的倾斜偏移
D 竖直方向的缩放 (scale Y)
E 水平方向的移动 (translate X)
F 竖直方向的移动 (translate Y)
标准矩阵 简化矩阵 单位矩阵
a c e a 0 e 1 0 0
B d f 0 d f 0 1 0
0 0 1 0 0 1 0 0 1
矩阵映射 (x1,y1) –> (x2,y2)
X2 = x1 * a + e
Y2 = y1 * d + f
矩阵变换
a1 0 e1 a 0 e a2 0 e2
0 d1 f1 X 0 d f = 0 d2 f2
0 0 1 0 0 1 0 0 1
得出公式:
a2 = a1 * a
d2 = d1 * d
e2 = a1 * e + e1
f2 = d1 * f + f1
矩阵api
transform(a,b, c,d,e,f) 屏幕坐标—> 屏幕坐标
setTransform(a,b,c,d,e,f) 地理坐标—>屏幕坐标
如下两个函数scale、translate都屏幕坐标—> 屏幕坐标转化,是transform函数简化
scale(x,y) 对变量a、d的变化
translate(x,y) 对变量e、f的变化
举个例子
假设变换前矩阵m1
变换矩阵m
变换后矩阵m2
变换操作维transform、scale、translate
transform : m1 * m = m2
scale : m = (x,0,0,y0,0)
translate : m = (1,0,0,1,x,y)
地理坐标–>屏幕坐标
webMercator 平面坐标 单位:米
canvas 屏幕坐标 单位:pixel
//设置视图级别及视图中心
setView(center: number[] = [0,0], zoom: number = 3) {
this._center = center;
this._zoom = Math.max(3, Math.min(20, zoom));
//center为经纬度,转化为平面坐标
const origin = this._projection.project(center as any);
const bound: Bound = this._projection.bound;
//已知:matrix 转换前 坐标origin,转换后坐标 即canvas的中心 [this._canvas.width / 2, this._canvas.height / 2]
//求:转换矩阵
//解法如下:
const a = 256 * Math.pow(2, this._zoom) / (bound.xmax - bound.xmin) * bound.xscale;
const d = 256 * Math.pow(2, this._zoom) / (bound.ymax - bound.ymin) * bound.yscale;
const e = this._canvas.width / 2 - a * origin[0];
const f = this._canvas.height / 2 - d * origin[1];
this._ctx.setTransform(a , 0, 0, d, e, f);
this.redraw();
}
屏幕坐标–>屏幕坐标
交互表现为鼠标当前位置屏幕坐标不变,进行缩放,即x2 = x1, y2 = y1
其他设定:变换前矩阵(a1,0,0,d1,e1,f1)变换矩阵(a,0,0,d,e,f),变换后矩阵(a2,0,0,d2,e2,f2)
scale通过滚轮变化计算得到,且 a= d = scale,求e,f
1.将原屏幕坐标x1转成地理坐标 x0 = (x1 - e1) / a1
2.地理坐标x0转成屏幕坐标x2 a2 *x0 + e2 = x2 , e2 = x2 - a2 * x0代入1式e2 = x2 - a2 * (x1 - e1) / a1
3.已知scale = a2 / a1 故 e2 = x2 - scale * ( x1 - e1)
4.另矩阵变换a1 *e + e1 = e2
5.联立3和4 求得 e = (x2 - scale * (x1 - e1) - e1) / a1
_onWheel(event) {
event.preventDefault();
let scale = 1;
const sensitivity = 100;
const delta = event.deltaY / sensitivity;
if (delta < 0) {
if (this._zoom >= 20) return;
// 放大
scale *= delta * -2;
}
else {
// 缩小
if (this._zoom <= 3) return;
scale /= delta * 2;
}
const zoom = Math.round(Math.log(scale));
scale = Math.pow(2, zoom);
this._zoom += zoom;
//交互表现为 鼠标当前位置 屏幕坐标不变 进行缩放 即x2 = x1
//第一种方案,坐标系不变,变坐标值
//1.将原屏幕坐标 x1 转成 初始坐标 x0 = (x1 - e1) / a1 初始矩阵 (1,0,0,1,0,0)
//2.初始坐标x0 转成 现屏幕坐标x2 a2 * x0 + e2 = x2 e2 = x2 - a2 * x0 代入1式 e2 = x2 - a2 * (x1 - e1) / a1
//3.已知scale = a2 / a1 故 e2 = x2 - scale * (x1 - e1)
//4.另矩阵变换 a1 * e + e1 = e2
//5.联立3和4 求得 e = (x2 - scale * (x1 - e1) - e1) / a1
const matrix = (this._ctx as any).getTransform();
const a1 = matrix.a, e1 = matrix.e, x1 = event.x, x2 = x1; //放大到中心点 x2 = this._canvas.width / 2
const e = (x2 - scale * (x1 - e1) - e1) / a1;
const d1 = matrix.d, f1 = matrix.f, y1 = event.y, y2 = y1; //放大到中心点 y2 = this._canvas.height / 2
const f = (y2 - scale * (y1 - f1) - f1) / d1;
this._ctx.transform( scale, 0, 0, scale, e, f );
this.redraw();
}