Skip to content

Snap之Matrix

何波 edited this page Jul 10, 2019 · 3 revisions

[TOC]

Martix

和css3的transform:matrix一样

<div style="transform:matrix(a,b,c,d,e,f)"></div>

<circle cx="20" cy="20" r="60" fill="#ff0000" transform="matrix(a,b,c,d,e,f)"></circle>
function Matrix(a, b, c, d, e, f) {
    if (b == null && objectToString.call(a) == "[object SVGMatrix]") {
        this.a = a.a;
        this.b = a.b;
        this.c = a.c;
        this.d = a.d;
        this.e = a.e;
        this.f = a.f;
        return;
    }
    if (a != null) {
        this.a = +a;
        this.b = +b;
        this.c = +c;
        this.d = +d;
        this.e = +e;
        this.f = +f;
    } else {
        this.a = 1;
        this.b = 0;
        this.c = 0;
        this.d = 1;
        this.e = 0;
        this.f = 0;
    }
}

Matrix.add(…)

Adds the given matrix to existing one

matrixproto.add = function (a, b, c, d, e, f) {
    if (a && a instanceof Matrix) {
        return this.add(a.a, a.b, a.c, a.d, a.e, a.f);
    }
    var aNew = a * this.a + b * this.c,
        bNew = a * this.b + b * this.d;
    this.e += e * this.a + f * this.c;
    this.f += e * this.b + f * this.d;
    this.c = c * this.a + d * this.c;
    this.d = c * this.b + d * this.d;

    this.a = aNew;
    this.b = bNew;
    return this;
};

相当于createjs中Matrix2D.prepend方法。

示例:

let svg = Snap("#svg");
let c = svg.paper.circle(50, 50, 40);
let m1 = new Snap.Matrix(1,0,0,1,20,20);
c.transform(m1.toTransformString());

if (document.addEventListener) {
    document.querySelector("#button").addEventListener("click", function() {
        m1.add(1,0,0,1,-20,-20);
        c.transform(m1.toTransformString());
    });
}

Matrix.translate(x, y)

  • x (number) horizontal offset distance
  • y (number) vertical offset distance

Translate the matrix

matrixproto.translate = function (x, y) {
    this.e += x * this.a + y * this.c;
    this.f += x * this.b + y * this.d;
    return this;
};

等同于createjs中Matrix2D.translate方法

let svg = Snap("#svg");
let c = svg.paper.rect(20, 20, 60, 60, 10).attr({
    fill: "#f00"
});

document.getElementById("button").onclick = function() {
    let m = new Snap.Matrix();
    m.translate(10, 10);
    c.transform(m);
};

Matrix.scale(x, [y], [cx], [cy])

  • x (number) amount to be scaled, with 1 resulting in no change
  • y (number) #optional amount to scale along the vertical axis. (Otherwise x applies to both axes.)
  • cx (number) #optional horizontal origin point from which to scale
  • cy (number) #optional vertical origin point from which to scale
  • Default cx, cy is the middle point of the element.

Scales the matrix

matrixproto.scale = function (x, y, cx, cy) {
    y == null && (y = x);
    (cx || cy) && this.translate(cx, cy); // 先移动原点
    this.a *= x;
    this.b *= x;
    this.c *= y;
    this.d *= y;
    (cx || cy) && this.translate(-cx, -cy); //矩阵变换完再移回原点
    return this;
};

与createjs中的Matrix2D.scale比较,多了移动scale原点的translate操作

let svg = Snap("#svg");
let c = svg.paper.circle(60, 60, 60).attr({
    fill: "#f00"
});

document.getElementById("button").onclick = function() {
    let m = new Snap.Matrix();
    let scale = Math.round((0.8 + 0.4 * Math.random()) * 100) / 100;
    m.scale(scale, scale);
    c.transform(m);
};

Matrix.skew(x, y)

  • y (number) Angle to skew along the y-axis (in degrees).
  • x (number) Angle to skew along the x-axis (in degrees).

Skews the matrix

matrixproto.skew = function (x, y) {
    x = x || 0;
    y = y || 0;
    x = Snap.rad(x);
    y = Snap.rad(y);
    var c = math.tan(x).toFixed(9);
    var b = math.tan(y).toFixed(9);
    return this.add(1, b, c, 1, 0, 0);
};

Matrix.invert()

反转矩阵,使它执行相反的变换。

matrixproto.invert = function () {
    var me = this,
        x = me.a * me.d - me.b * me.c;
    return new Matrix(me.d / x, -me.b / x, -me.c / x, me.a / x, (me.c * me.f - me.d * me.e) / x, (me.b * me.e - me.a * me.f) / x);
};

等同于createjs中Matrix2D.invert方法

Matrix.clone()

Returns a copy of the matrix

matrixproto.clone = function () {
    return new Matrix(this.a, this.b, this.c, this.d, this.e, this.f);
};

Matrix.split()

将矩阵分解成基本变换,这个函数牛!

matrixproto.split = function () {
    var out = {};
    // translation
    out.dx = this.e;
    out.dy = this.f;

    // scale and shear
    var row = [[this.a, this.b], [this.c, this.d]];
    out.scalex = math.sqrt(norm(row[0]));
    normalize(row[0]);

    out.shear = row[0][0] * row[1][0] + row[0][1] * row[1][1];
    row[1] = [row[1][0] - row[0][0] * out.shear, row[1][1] - row[0][1] * out.shear];

    out.scaley = math.sqrt(norm(row[1]));
    normalize(row[1]);
    out.shear /= out.scaley;

    if (this.determinant() < 0) {
        out.scalex = -out.scalex;
    }

    // rotation
    var sin = row[0][1],
        cos = row[1][1];
    if (cos < 0) {
        out.rotate = Snap.deg(math.acos(cos));
        if (sin < 0) {
            out.rotate = 360 - out.rotate;
        }
    } else {
        out.rotate = Snap.deg(math.asin(sin));
    }

    out.isSimple = !+out.shear.toFixed(9) && (out.scalex.toFixed(9) == out.scaley.toFixed(9) || !out.rotate);
    out.isSuperSimple = !+out.shear.toFixed(9) && out.scalex.toFixed(9) == out.scaley.toFixed(9) && !out.rotate;
    out.noRotation = !+out.shear.toFixed(9) && !out.rotate;
    return out;
};

示例:

let m = new Snap.Matrix(1,2,3,4,5,6);
console.dir(m.split())

输出:

Object: {
    dx: 5
    dy: 6
    isSimple: false
    isSuperSimple: false
    noRotation: false
    rotate: 116.56505117707798
    scalex: -2.23606797749979
    scaley: 0.8944271909999159
    shear: 5.5
}

Matrix.toTransformString()

返回给定矩阵的使用transform表示的字符串。

matrixproto.toTransformString = function (shorter) {
    var s = shorter || this.split();
    if (!+s.shear.toFixed(9)) {
        s.scalex = +s.scalex.toFixed(4);
        s.scaley = +s.scaley.toFixed(4);
        s.rotate = +s.rotate.toFixed(4);
        return  (s.dx || s.dy ? "t" + [+s.dx.toFixed(4), +s.dy.toFixed(4)] : E) +
                (s.rotate ? "r" + [+s.rotate.toFixed(4), 0, 0] : E) +
                (s.scalex != 1 || s.scaley != 1 ? "s" + [s.scalex, s.scaley, 0, 0] : E);
    } else {
        return "m" + [this.get(0), this.get(1), this.get(2), this.get(3), this.get(4), this.get(5)];
    }
};

示例:

let m1 = new Snap.Matrix(1,2,3,4,5,6)
let m2 = new Snap.Matrix(2,0,0,1,20,20)
console.log(m1.toTransformString()) // m1,2,3,4,5,6
console.log(m2.toTransformString()) // t20,20s2,1,0,0

Clone this wiki locally