324 lines
6.6 KiB
JavaScript
324 lines
6.6 KiB
JavaScript
|
var matrix = require("../core/matrix");
|
||
|
|
||
|
var vector = require("../core/vector");
|
||
|
|
||
|
/**
|
||
|
* 提供变换扩展
|
||
|
* @module zrender/mixin/Transformable
|
||
|
* @author pissang (https://www.github.com/pissang)
|
||
|
*/
|
||
|
var mIdentity = matrix.identity;
|
||
|
var EPSILON = 5e-5;
|
||
|
|
||
|
function isNotAroundZero(val) {
|
||
|
return val > EPSILON || val < -EPSILON;
|
||
|
}
|
||
|
/**
|
||
|
* @alias module:zrender/mixin/Transformable
|
||
|
* @constructor
|
||
|
*/
|
||
|
|
||
|
|
||
|
var Transformable = function (opts) {
|
||
|
opts = opts || {}; // If there are no given position, rotation, scale
|
||
|
|
||
|
if (!opts.position) {
|
||
|
/**
|
||
|
* 平移
|
||
|
* @type {Array.<number>}
|
||
|
* @default [0, 0]
|
||
|
*/
|
||
|
this.position = [0, 0];
|
||
|
}
|
||
|
|
||
|
if (opts.rotation == null) {
|
||
|
/**
|
||
|
* 旋转
|
||
|
* @type {Array.<number>}
|
||
|
* @default 0
|
||
|
*/
|
||
|
this.rotation = 0;
|
||
|
}
|
||
|
|
||
|
if (!opts.scale) {
|
||
|
/**
|
||
|
* 缩放
|
||
|
* @type {Array.<number>}
|
||
|
* @default [1, 1]
|
||
|
*/
|
||
|
this.scale = [1, 1];
|
||
|
}
|
||
|
/**
|
||
|
* 旋转和缩放的原点
|
||
|
* @type {Array.<number>}
|
||
|
* @default null
|
||
|
*/
|
||
|
|
||
|
|
||
|
this.origin = this.origin || null;
|
||
|
};
|
||
|
|
||
|
var transformableProto = Transformable.prototype;
|
||
|
transformableProto.transform = null;
|
||
|
/**
|
||
|
* 判断是否需要有坐标变换
|
||
|
* 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵
|
||
|
*/
|
||
|
|
||
|
transformableProto.needLocalTransform = function () {
|
||
|
return isNotAroundZero(this.rotation) || isNotAroundZero(this.position[0]) || isNotAroundZero(this.position[1]) || isNotAroundZero(this.scale[0] - 1) || isNotAroundZero(this.scale[1] - 1);
|
||
|
};
|
||
|
|
||
|
var scaleTmp = [];
|
||
|
|
||
|
transformableProto.updateTransform = function () {
|
||
|
var parent = this.parent;
|
||
|
var parentHasTransform = parent && parent.transform;
|
||
|
var needLocalTransform = this.needLocalTransform();
|
||
|
var m = this.transform;
|
||
|
|
||
|
if (!(needLocalTransform || parentHasTransform)) {
|
||
|
m && mIdentity(m);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
m = m || matrix.create();
|
||
|
|
||
|
if (needLocalTransform) {
|
||
|
this.getLocalTransform(m);
|
||
|
} else {
|
||
|
mIdentity(m);
|
||
|
} // 应用父节点变换
|
||
|
|
||
|
|
||
|
if (parentHasTransform) {
|
||
|
if (needLocalTransform) {
|
||
|
matrix.mul(m, parent.transform, m);
|
||
|
} else {
|
||
|
matrix.copy(m, parent.transform);
|
||
|
}
|
||
|
} // 保存这个变换矩阵
|
||
|
|
||
|
|
||
|
this.transform = m;
|
||
|
var globalScaleRatio = this.globalScaleRatio;
|
||
|
|
||
|
if (globalScaleRatio != null && globalScaleRatio !== 1) {
|
||
|
this.getGlobalScale(scaleTmp);
|
||
|
var relX = scaleTmp[0] < 0 ? -1 : 1;
|
||
|
var relY = scaleTmp[1] < 0 ? -1 : 1;
|
||
|
var sx = ((scaleTmp[0] - relX) * globalScaleRatio + relX) / scaleTmp[0] || 0;
|
||
|
var sy = ((scaleTmp[1] - relY) * globalScaleRatio + relY) / scaleTmp[1] || 0;
|
||
|
m[0] *= sx;
|
||
|
m[1] *= sx;
|
||
|
m[2] *= sy;
|
||
|
m[3] *= sy;
|
||
|
}
|
||
|
|
||
|
this.invTransform = this.invTransform || matrix.create();
|
||
|
matrix.invert(this.invTransform, m);
|
||
|
};
|
||
|
|
||
|
transformableProto.getLocalTransform = function (m) {
|
||
|
return Transformable.getLocalTransform(this, m);
|
||
|
};
|
||
|
/**
|
||
|
* 将自己的transform应用到context上
|
||
|
* @param {CanvasRenderingContext2D} ctx
|
||
|
*/
|
||
|
|
||
|
|
||
|
transformableProto.setTransform = function (ctx) {
|
||
|
var m = this.transform;
|
||
|
var dpr = ctx.dpr || 1;
|
||
|
|
||
|
if (m) {
|
||
|
ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]);
|
||
|
} else {
|
||
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
transformableProto.restoreTransform = function (ctx) {
|
||
|
var dpr = ctx.dpr || 1;
|
||
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||
|
};
|
||
|
|
||
|
var tmpTransform = [];
|
||
|
var originTransform = matrix.create();
|
||
|
|
||
|
transformableProto.setLocalTransform = function (m) {
|
||
|
if (!m) {
|
||
|
// TODO return or set identity?
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var sx = m[0] * m[0] + m[1] * m[1];
|
||
|
var sy = m[2] * m[2] + m[3] * m[3];
|
||
|
var position = this.position;
|
||
|
var scale = this.scale;
|
||
|
|
||
|
if (isNotAroundZero(sx - 1)) {
|
||
|
sx = Math.sqrt(sx);
|
||
|
}
|
||
|
|
||
|
if (isNotAroundZero(sy - 1)) {
|
||
|
sy = Math.sqrt(sy);
|
||
|
}
|
||
|
|
||
|
if (m[0] < 0) {
|
||
|
sx = -sx;
|
||
|
}
|
||
|
|
||
|
if (m[3] < 0) {
|
||
|
sy = -sy;
|
||
|
}
|
||
|
|
||
|
position[0] = m[4];
|
||
|
position[1] = m[5];
|
||
|
scale[0] = sx;
|
||
|
scale[1] = sy;
|
||
|
this.rotation = Math.atan2(-m[1] / sy, m[0] / sx);
|
||
|
};
|
||
|
/**
|
||
|
* 分解`transform`矩阵到`position`, `rotation`, `scale`
|
||
|
*/
|
||
|
|
||
|
|
||
|
transformableProto.decomposeTransform = function () {
|
||
|
if (!this.transform) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var parent = this.parent;
|
||
|
var m = this.transform;
|
||
|
|
||
|
if (parent && parent.transform) {
|
||
|
// Get local transform and decompose them to position, scale, rotation
|
||
|
matrix.mul(tmpTransform, parent.invTransform, m);
|
||
|
m = tmpTransform;
|
||
|
}
|
||
|
|
||
|
var origin = this.origin;
|
||
|
|
||
|
if (origin && (origin[0] || origin[1])) {
|
||
|
originTransform[4] = origin[0];
|
||
|
originTransform[5] = origin[1];
|
||
|
matrix.mul(tmpTransform, m, originTransform);
|
||
|
tmpTransform[4] -= origin[0];
|
||
|
tmpTransform[5] -= origin[1];
|
||
|
m = tmpTransform;
|
||
|
}
|
||
|
|
||
|
this.setLocalTransform(m);
|
||
|
};
|
||
|
/**
|
||
|
* Get global scale
|
||
|
* @return {Array.<number>}
|
||
|
*/
|
||
|
|
||
|
|
||
|
transformableProto.getGlobalScale = function (out) {
|
||
|
var m = this.transform;
|
||
|
out = out || [];
|
||
|
|
||
|
if (!m) {
|
||
|
out[0] = 1;
|
||
|
out[1] = 1;
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
out[0] = Math.sqrt(m[0] * m[0] + m[1] * m[1]);
|
||
|
out[1] = Math.sqrt(m[2] * m[2] + m[3] * m[3]);
|
||
|
|
||
|
if (m[0] < 0) {
|
||
|
out[0] = -out[0];
|
||
|
}
|
||
|
|
||
|
if (m[3] < 0) {
|
||
|
out[1] = -out[1];
|
||
|
}
|
||
|
|
||
|
return out;
|
||
|
};
|
||
|
/**
|
||
|
* 变换坐标位置到 shape 的局部坐标空间
|
||
|
* @method
|
||
|
* @param {number} x
|
||
|
* @param {number} y
|
||
|
* @return {Array.<number>}
|
||
|
*/
|
||
|
|
||
|
|
||
|
transformableProto.transformCoordToLocal = function (x, y) {
|
||
|
var v2 = [x, y];
|
||
|
var invTransform = this.invTransform;
|
||
|
|
||
|
if (invTransform) {
|
||
|
vector.applyTransform(v2, v2, invTransform);
|
||
|
}
|
||
|
|
||
|
return v2;
|
||
|
};
|
||
|
/**
|
||
|
* 变换局部坐标位置到全局坐标空间
|
||
|
* @method
|
||
|
* @param {number} x
|
||
|
* @param {number} y
|
||
|
* @return {Array.<number>}
|
||
|
*/
|
||
|
|
||
|
|
||
|
transformableProto.transformCoordToGlobal = function (x, y) {
|
||
|
var v2 = [x, y];
|
||
|
var transform = this.transform;
|
||
|
|
||
|
if (transform) {
|
||
|
vector.applyTransform(v2, v2, transform);
|
||
|
}
|
||
|
|
||
|
return v2;
|
||
|
};
|
||
|
/**
|
||
|
* @static
|
||
|
* @param {Object} target
|
||
|
* @param {Array.<number>} target.origin
|
||
|
* @param {number} target.rotation
|
||
|
* @param {Array.<number>} target.position
|
||
|
* @param {Array.<number>} [m]
|
||
|
*/
|
||
|
|
||
|
|
||
|
Transformable.getLocalTransform = function (target, m) {
|
||
|
m = m || [];
|
||
|
mIdentity(m);
|
||
|
var origin = target.origin;
|
||
|
var scale = target.scale || [1, 1];
|
||
|
var rotation = target.rotation || 0;
|
||
|
var position = target.position || [0, 0];
|
||
|
|
||
|
if (origin) {
|
||
|
// Translate to origin
|
||
|
m[4] -= origin[0];
|
||
|
m[5] -= origin[1];
|
||
|
}
|
||
|
|
||
|
matrix.scale(m, m, scale);
|
||
|
|
||
|
if (rotation) {
|
||
|
matrix.rotate(m, m, rotation);
|
||
|
}
|
||
|
|
||
|
if (origin) {
|
||
|
// Translate back from origin
|
||
|
m[4] += origin[0];
|
||
|
m[5] += origin[1];
|
||
|
}
|
||
|
|
||
|
m[4] += position[0];
|
||
|
m[5] += position[1];
|
||
|
return m;
|
||
|
};
|
||
|
|
||
|
var _default = Transformable;
|
||
|
module.exports = _default;
|