Laya 中缩放的实现

Laya 缩放功能的实现

在 laya 中实现滚轮对选中对象的缩放,涉及到以下两个模块:

  • 事件
  • 容器坐标

1. 事件

在 Laya 中, Event 是事件类型的集合。包含了常见的鼠标事件、键盘事件。

  • 1.1 事件的触发

      触发一个事件,需要设置一个事件接收区域.
    如若未明确指定事件,事件触发默认是冒泡模式。从子节点开始依次向父节点查询,直到找到绑定了指定事件的容器后结束。
    在对容器进行绑定时有下面两种方式:
方法一:根据事件对象获取(不推荐使用)
mySprit.on(Laya.Event.MouseDOwn, this, this.Scale); 
public Scale(e: Event)
{
    // 此种形式获取 sp, 并未明确指定绑定事件的 sp,触发事件时,需冒泡查找, 不推荐使用
    let sp = e.target as Laya.Sprite; 
}
方法二: 直接传入目标容器
mySprit.on(Laya.Event.MouseDOwn, this, this.Scale, [targetSp]); 
public Scale(sp,  e: Event)
{
    // 直接用传入的 sp 进行绑定
}
  • 1.2 事件的绑定与解绑

      只有在需要时候事件时,才绑定事件方法。用完后,需要解除绑定。这样可以避免同一个对象多次绑定的问题,
    在 laya 中,事件管理器是允许同一个对象的同一个方法事件进行多次绑定的,这样容易造成许多意向不到的 bug, 所以务必
    只在使用时进行绑定,用完立马解除绑定。注意,如鼠标移动事件、MouseUp 事件一般使用 Stage 来触发。
    如下例:
// 下面的代码为按下鼠标左键,允许拖动目标容器,松开鼠标左键后,无法拖动目标容器
this.firstSp = new Laya.Sprite();
this.firstSp.graphics.drawRect(0, 0, 100, 100, null, "#ffffff");   // 绘制图形
this.firstSp.on(Laya.Event.MOUSE_DOWN, this, this.MouseDown, [this.firstSp]); // 绑定 mouse_down 事件
this.firstSp.hitArea = this.firstSp.getGraphicBounds();       // 指定可触发事件区域
this._rootSp.addChild(this.firstSp);
Laya.stage.addChild(this._rootSp);

// 在 mouse_down 事件后才绑定事件
private MouseDown(sp:Laya.Sprite, e:Laya.Event)
{
    Laya.stage.on(Laya.Event.MOUSE_MOVE, this, this.MouseMove, [sp])
    Laya.stage.on(Laya.Event.MOUSE_UP, this, this.MouseUp, [sp])
}

private MouseMove(sp:Laya.Sprite, e:Laya.Event)
{
    sp.x += 5;
}

// 在 mouseup 事件后解除除了触发事件外的其他一切事件
private MouseUp(sp:Laya.Sprite, e:Laya.Event)
{
    Laya.stage.off(Laya.Event.MOUSE_MOVE, this, this.MouseMove)
    Laya.stage.off(Laya.Event.MOUSE_UP, this, this.MouseUp)
}
  • 1.3 可点击区域的设置
      容器要触发交互事件,必须设置可点击区域
// 将绘图区域设置为 点击区域
this.firstSp.hitArea = this.firstSp.getGraphicBounds();       // 指定可触发事件区域
// 用数值设定点击区域
this.firstSp.hitArea = new Laya.Rectangle(0, 0, 10000, 10000)

容器坐标

laya 2d 坐标系坐标单位为像素, Stage 左上角为左边原点 (0,0)。向右为 x 轴正向,向下为 Y 轴正向。所有容器创建时坐标原点默认为 (0,0), 此处需要注意:
所有容器的原点坐标都是相对于其父容器原点的坐标,是局部坐标,而非全局坐标。
因此,对象在容器中的坐标为局部坐标,转换为全局需要逐层向父容器变换,知道跟容器 Stage 为止。 同时要注意:
在移动、缩放对象时不要改变对象坐标,改变容器坐标,实现对象的改变。

// 对容器对象进行缩放
///-------------- Scale Drawing Sprite --------------------------
public static MouseWheel(currentSp: Laya.Sprite, e: Laya.Event) {
    let spriteX = 0, spriteY = 0;
    if(currentSp.parent)   // 考虑了父容器的偏差, TODO: 父容器与 stage 有偏差
    {
        spriteX = (currentSp.parent as Laya.Sprite).x
        spriteY = (currentSp.parent as Laya.Sprite).y;
    }
    this.Zoom(currentSp, e.stageX - spriteX, e.stageY - spriteY, e.delta);
}
private static Zoom(sp: Laya.Sprite, x: number, y: number, delta: number) {
    let oldScale = sp.scaleX;
    let scale = delta > 0 ? WebCad.GlobalScaleRatio : 1 / WebCad.GlobalScaleRatio;
    let newScale = sp.scaleX * scale;
    // 设置最大最小缩放范围
    newScale = newScale < 0.1 ? 0.1 : (newScale > 50 ? 50 : newScale);
    // 获取在缩放比例下,光标相对偏移位置
    sp.x = sp.x - (x - sp.x) * (newScale - oldScale) / oldScale;
    sp.y = sp.y - (y - sp.y) * (newScale - oldScale) / oldScale;
    sp.scale(newScale, newScale);
}

当有多个操作的时候,比如需要对象同时涉及到缩放,平移以及旋转时候,我们应当遵循下面这个原则:

先处理缩放,再旋转,然后平移。
这里涉及到一个矩阵组合运算的原则:
**矩阵的组合

矩阵的乘法运算不遵守交换律,这意味着它们的顺序很重要。当矩阵相乘时,在最右边的矩阵是第一个与向量相乘的,所以我们应当从右向左来读这个乘法。所以在组合时,先缩放操作,然后旋转,最后才是位移,否则它们会互相影响。比如,你先位移再缩放,位移的向量也会同样缩放。

posted on 2019-03-25 20:46  张居斜  阅读(2191)  评论(0编辑  收藏  举报

导航