Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[源码分析] canvas 实现缩放和拖拽 #13

Open
ZhenHe17 opened this issue Apr 4, 2019 · 0 comments
Open

[源码分析] canvas 实现缩放和拖拽 #13

ZhenHe17 opened this issue Apr 4, 2019 · 0 comments

Comments

@ZhenHe17
Copy link
Owner

ZhenHe17 commented Apr 4, 2019

需求

通过鼠标滚轮来缩放 canvas ,通过鼠标拖拽 canvas 。

这个需求的讨论来源于 Stack Overflow 上的问题 Zoom Canvas to Mouse Cursor ,作者使用了 SVG 的 createSVGMatrix 来记录 canvas 变换的矩阵,从而完善了缩放和拖拽的效果,这个思路引起了我的兴趣,这里对源码进行些分析。

在线例子
源码

源码分析

整体思路

  1. 通过监听 mousedown 、mousemove 、mouseup 触发拖拽的行为;通过监听 mousewheel 、触发滚轮缩放的行为
  2. 通过 canvas 的 translate 方法来移动画布;scale 方法缩放画布
  3. 实现trackTransforms,以保存每一次操作后的转换矩阵

canvas 的 translate、 scale 方法可以自由移动缩放画布,但是无法记录当前的变换状态,导致不能在绘制 canvas 时再使用 translate、 scale 方法等;并且不能获取 canvas 点击事件在矩阵变换后的位置。所以作者对每次变换进行记录,解决了变换状态丢失的问题。

实现 trackTransforms

trackTransforms 在源码中的位置:

125 | function trackTransforms(ctx){
......
178 | }

创建矩阵保存变换状态

trackTransforms 先利用 SVG 对象的 createSVGMatrix 创建了矩阵,这个矩阵保存着所有 canvas 的缩放、倾斜和移动:

var svg = document.createElementNS("http://www.w3.org/2000/svg",'svg');
var xform = svg.createSVGMatrix();

修改变换相关的方法

对 canvas 中变换相关的方法进行修改,使每次造成 canvas 变换的操作也对矩阵造成了相应的变换。这样一来就可以通过矩阵记录当前的变换状态,即:变换前的点 -> 矩阵 -> 变换后的点。矩阵的具体计算方法见MDN文档

var scale = ctx.scale;
ctx.scale = function(sx,sy){
  xform = xform.scaleNonUniform(sx,sy);
  return scale.call(ctx,sx,sy);
};
var rotate = ctx.rotate;
ctx.rotate = function(radians){
  xform = xform.rotate(radians*180/Math.PI);
  return rotate.call(ctx,radians);
};
var translate = ctx.translate;
ctx.translate = function(dx,dy){
  xform = xform.translate(dx,dy);
  return translate.call(ctx,dx,dy);
};
var transform = ctx.transform;
ctx.transform = function(a,b,c,d,e,f){
  var m2 = svg.createSVGMatrix();
  m2.a=a; m2.b=b; m2.c=c; m2.d=d; m2.e=e; m2.f=f;
  xform = xform.multiply(m2);
  return transform.call(ctx,a,b,c,d,e,f);
};
var setTransform = ctx.setTransform;
ctx.setTransform = function(a,b,c,d,e,f){
  xform.a = a;
  xform.b = b;
  xform.c = c;
  xform.d = d;
  xform.e = e;
  xform.f = f;
  return setTransform.call(ctx,a,b,c,d,e,f);
};

通过矩阵获取变换后的点的坐标

var pt  = svg.createSVGPoint();
ctx.transformedPoint = function(x,y){
  pt.x=x; pt.y=y;
  return pt.matrixTransform(xform.inverse());
}

比如作用在 canvas 上的点击事件的位置,可以通过矩阵变换对应到变换后的绘图上的位置。

其他实现此需求的JS库

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant