chartx
Version:
Data Visualization Chart Library
1,909 lines (1,570 loc) • 1.88 MB
JavaScript
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function commonjsRequire () {
throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
}
function unwrapExports (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var interopRequireDefault = createCommonjsModule(function (module) {
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
"default": obj
};
}
module.exports = _interopRequireDefault;
});
unwrapExports(interopRequireDefault);
function unwrapExports$1 (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
function createCommonjsModule$1(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var classCallCheck = createCommonjsModule$1(function (module) {
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
module.exports = _classCallCheck;
module.exports["default"] = module.exports, module.exports.__esModule = true;
});
var _classCallCheck = unwrapExports$1(classCallCheck);
var createClass = createCommonjsModule$1(function (module) {
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
module.exports = _createClass;
module.exports["default"] = module.exports, module.exports.__esModule = true;
});
var _createClass = unwrapExports$1(createClass);
var assertThisInitialized = createCommonjsModule$1(function (module) {
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
module.exports = _assertThisInitialized;
module.exports["default"] = module.exports, module.exports.__esModule = true;
});
var _assertThisInitialized = unwrapExports$1(assertThisInitialized);
var setPrototypeOf = createCommonjsModule$1(function (module) {
function _setPrototypeOf(o, p) {
module.exports = _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
module.exports["default"] = module.exports, module.exports.__esModule = true;
return _setPrototypeOf(o, p);
}
module.exports = _setPrototypeOf;
module.exports["default"] = module.exports, module.exports.__esModule = true;
});
unwrapExports$1(setPrototypeOf);
var inherits = createCommonjsModule$1(function (module) {
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) setPrototypeOf(subClass, superClass);
}
module.exports = _inherits;
module.exports["default"] = module.exports, module.exports.__esModule = true;
});
var _inherits = unwrapExports$1(inherits);
var _typeof_1 = createCommonjsModule$1(function (module) {
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
module.exports = _typeof = function _typeof(obj) {
return typeof obj;
};
module.exports["default"] = module.exports, module.exports.__esModule = true;
} else {
module.exports = _typeof = function _typeof(obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
module.exports["default"] = module.exports, module.exports.__esModule = true;
}
return _typeof(obj);
}
module.exports = _typeof;
module.exports["default"] = module.exports, module.exports.__esModule = true;
});
var _typeof = unwrapExports$1(_typeof_1);
var possibleConstructorReturn = createCommonjsModule$1(function (module) {
var _typeof = _typeof_1["default"];
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
}
return assertThisInitialized(self);
}
module.exports = _possibleConstructorReturn;
module.exports["default"] = module.exports, module.exports.__esModule = true;
});
var _possibleConstructorReturn = unwrapExports$1(possibleConstructorReturn);
var getPrototypeOf = createCommonjsModule$1(function (module) {
function _getPrototypeOf(o) {
module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
module.exports["default"] = module.exports, module.exports.__esModule = true;
return _getPrototypeOf(o);
}
module.exports = _getPrototypeOf;
module.exports["default"] = module.exports, module.exports.__esModule = true;
});
var _getPrototypeOf = unwrapExports$1(getPrototypeOf);
var _ = {};
var breaker = {};
var ArrayProto = Array.prototype,
ObjProto = Object.prototype;
// Create quick reference variables for speed access to core prototypes.
var push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys;
var shallowProperty = function shallowProperty(key) {
return function (obj) {
return obj == null ? void 0 : obj[key];
};
};
var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
var getLength = shallowProperty('length');
var isArrayLike = function isArrayLike(collection) {
var length = getLength(collection);
return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
};
_.values = function (obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = new Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
_.keys = nativeKeys || function (obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) {
if (_.has(obj, key)) keys.push(key);
}
return keys;
};
_.has = function (obj, key) {
return hasOwnProperty.call(obj, key);
};
var each = _.each = _.forEach = function (obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
};
_.compact = function (array) {
return _.filter(array, _.identity);
};
_.filter = _.select = function (obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function (value, index, list) {
if (iterator.call(context, value, index, list)) results.push(value);
});
return results;
};
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function (name) {
_['is' + name] = function (obj) {
return toString.call(obj) == '[object ' + name + ']';
};
}); //if (!_.isArguments(arguments)) {
_.isArguments = function (obj) {
return !!(obj && _.has(obj, 'callee'));
}; //}
if (typeof /./ !== 'function') {
_.isFunction = function (obj) {
return typeof obj === 'function';
};
}
_.isFinite = function (obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
_.isNaN = function (obj) {
return _.isNumber(obj) && obj != +obj;
};
_.isBoolean = function (obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
_.isNull = function (obj) {
return obj === null;
};
_.isEmpty = function (obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) {
if (_.has(obj, key)) return false;
}
return true;
};
_.isElement = function (obj) {
return !!(obj && obj.nodeType === 1);
};
_.isArray = nativeIsArray || function (obj) {
return toString.call(obj) == '[object Array]';
};
_.isObject = function (obj) {
return obj === Object(obj);
};
_.identity = function (value) {
return value;
};
_.indexOf = function (array, item, isSorted) {
if (array == null) return -1;
var i = 0,
length = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) {
if (array[i] === item) return i;
}
return -1;
};
_.isWindow = function (obj) {
return obj != null && obj == obj.window;
}; // Internal implementation of a recursive `flatten` function.
var flatten = function flatten(input, shallow, output) {
if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input);
}
each(input, function (value) {
if (_.isArray(value) || _.isArguments(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
}
});
return output;
}; // Flatten out an array, either recursively (by default), or just one level.
_.flatten = function (array, shallow) {
return flatten(array, shallow, []);
};
_.every = _.all = function (obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function (value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return !!result;
}; // Return the minimum element (or element-based computation).
_.min = function (obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {
computed: Infinity,
value: Infinity
};
each(obj, function (value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {
value: value,
computed: computed
});
});
return result.value;
}; // Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function (obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {
computed: -Infinity,
value: -Infinity
};
each(obj, function (value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed > result.computed && (result = {
value: value,
computed: computed
});
});
return result.value;
}; // Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function (obj, iterator, context) {
var result;
any(obj, function (value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
}; // Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function (obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function (value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
});
return !!result;
}; // Return a version of the array that does not contain the specified value(s).
_.without = function (array) {
return _.difference(array, slice.call(arguments, 1));
}; // Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function (array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function (value) {
return !_.contains(rest, value);
});
}; // Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function (array, isSorted, iterator, context) {
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
each(initial, function (value, index) {
if (isSorted ? !index || seen[seen.length - 1] !== value : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
}
});
return results;
}; // Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function (obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function (value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
}; // Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function (obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function (value) {
return value === target;
});
}; // Convenience version of a common use case of `map`: fetching a property.
_.pluck = function (obj, key) {
return _.map(obj, function (value) {
return value[key];
});
}; // Return a random integer between min and max (inclusive).
_.random = function (min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
}; // Shuffle a collection.
_.shuffle = function (obj) {
return _.sample(obj, Infinity);
};
_.sample = function (obj, n, guard) {
if (n == null || guard) {
if (!isArrayLike(obj)) obj = _.values(obj);
return obj[_.random(obj.length - 1)];
}
var sample = isArrayLike(obj) ? _.clone(obj) : _.values(obj);
var length = getLength(sample);
n = Math.max(Math.min(n, length), 0);
var last = length - 1;
for (var index = 0; index < n; index++) {
var rand = _.random(index, last);
var temp = sample[index];
sample[index] = sample[rand];
sample[rand] = temp;
}
return sample.slice(0, n);
};
/**
*
*如果是深度extend,第一个参数就设置为true
*/
_.extend = function () {
var options,
name,
src,
copy,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
if (typeof target === "boolean") {
deep = target;
target = arguments[1] || {};
i = 2;
}
if (_typeof(target) !== "object" && !_.isFunction(target)) {
target = {};
}
if (length === i) {
target = this;
--i;
}
for (; i < length; i++) {
if ((options = arguments[i]) != null) {
for (name in options) {
src = target[name];
copy = options[name];
if (target === copy || copy === undefined) {
continue;
}
if (deep && copy && _.isObject(copy) && copy.constructor === Object) {
target[name] = _.extend(deep, src, copy);
} else {
target[name] = copy;
}
}
}
}
return target;
};
_.clone = function (obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend(true, {}, obj);
}; //********补存一些数学常用方法,暂放在这里文件下,后期多了单独成立一个类库 */
// compute euclidian modulo of m % n
// https://en.wikipedia.org/wiki/Modulo_operation
_.euclideanModulo = function (n, m) {
return (n % m + m) % m;
};
_.DEG2RAD = Math.PI / 180;
_.RAD2DEG = 180 / Math.PI;
_.degToRad = function (degrees) {
return degrees * _.DEG2RAD;
};
_.radToDeg = function (radians) {
return radians * _.RAD2DEG;
};
var Settings = {
//设备分辨率
RESOLUTION: 1,
/**
* Target frames per millisecond.
*/
TARGET_FPMS: 0.06,
/**
* If set to true WebGL will attempt make textures mimpaped by default.
* Mipmapping will only succeed if the base texture uploaded has power of two dimensions.
*/
MIPMAP_TEXTURES: true,
/**
* Default filter resolution.
*/
FILTER_RESOLUTION: 1,
// TODO: maybe change to SPRITE.BATCH_SIZE: 2000
// TODO: maybe add PARTICLE.BATCH_SIZE: 15000
/**
* The default sprite batch size.
*
* The default aims to balance desktop and mobile devices.
*/
SPRITE_BATCH_SIZE: 4096,
/**
* The prefix that denotes a URL is for a retina asset.
*/
RETINA_PREFIX: /@(.+)x/,
RENDER_OPTIONS: {
view: null,
antialias: true,
forceFXAA: false,
autoResize: false,
transparent: true,
backgroundColor: 0x000000,
clearBeforeRender: true,
preserveDrawingBuffer: false,
roundPixels: false
},
TRANSFORM_MODE: 0,
GC_MODE: 0,
GC_MAX_IDLE: 60 * 60,
GC_MAX_CHECK_COUNT: 60 * 10,
WRAP_MODE: 0,
SCALE_MODE: 0,
PRECISION: 'mediump'
};
var addOrRmoveEventHand = function addOrRmoveEventHand(domHand, ieHand) {
if (!document) return;
if (document[domHand]) {
var eventDomFn = function eventDomFn(el, type, fn) {
if (el.length) {
for (var i = 0; i < el.length; i++) {
eventDomFn(el[i], type, fn);
}
} else {
el[domHand](type, fn, false);
}
};
return eventDomFn;
} else {
var eventFn = function eventFn(el, type, fn) {
if (el.length) {
for (var i = 0; i < el.length; i++) {
eventFn(el[i], type, fn);
}
} else {
el[ieHand]("on" + type, function () {
return fn.call(el, window.event);
});
}
};
return eventFn;
}
};
var $ = {
// dom操作相关代码
query: function query(el) {
if (!el) return;
if (_.isString(el)) {
return document.getElementById(el);
}
if (el.nodeType == 1) {
//则为一个element本身
return el;
}
if (el.length) {
return el[0];
}
return null;
},
offset: function offset(el) {
if (!el) {
return {
top: 0,
left: 0
};
}
var box = el.getBoundingClientRect(),
doc = el.ownerDocument,
body = doc.body,
docElem = doc.documentElement,
// for ie
clientTop = docElem.clientTop || body.clientTop || 0,
clientLeft = docElem.clientLeft || body.clientLeft || 0,
// In Internet Explorer 7 getBoundingClientRect property is treated as physical,
// while others are logical. Make all logical, like in IE8.
zoom = 1;
if (body.getBoundingClientRect) {
var bound = body.getBoundingClientRect();
zoom = (bound.right - bound.left) / body.clientWidth;
}
if (zoom > 1) {
clientTop = 0;
clientLeft = 0;
}
var top = box.top / zoom + (window.pageYOffset || docElem && docElem.scrollTop / zoom || body.scrollTop / zoom) - clientTop,
left = box.left / zoom + (window.pageXOffset || docElem && docElem.scrollLeft / zoom || body.scrollLeft / zoom) - clientLeft;
return {
top: top,
left: left
};
},
addEvent: addOrRmoveEventHand("addEventListener", "attachEvent"),
removeEvent: addOrRmoveEventHand("removeEventListener", "detachEvent"),
pageX: function pageX(e) {
if (e.pageX) return e.pageX;else if (e.clientX) return e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);else return null;
},
pageY: function pageY(e) {
if (e.pageY) return e.pageY;else if (e.clientY) return e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);else return null;
},
/**
* 创建dom
* @param {string} id dom id 待用
* @param {string} type : dom type, such as canvas, div etc.
*/
createCanvas: function createCanvas(_width, _height, id) {
if (!document) return;
var canvas = document.createElement("canvas");
canvas.style.position = 'absolute';
canvas.style.width = _width + 'px';
canvas.style.height = _height + 'px';
canvas.style.left = 0;
canvas.style.top = 0;
canvas.setAttribute('width', _width * Settings.RESOLUTION);
canvas.setAttribute('height', _height * Settings.RESOLUTION);
canvas.setAttribute('id', id);
return canvas;
},
createView: function createView(_width, _height, id) {
var view = document.createElement("div");
view.className = "canvax-view";
view.style.cssText += "position:relative;width:100%;height:100%;";
var stageView = document.createElement("div");
stageView.style.cssText += "position:absolute;width:" + _width + "px;height:" + _height + "px;"; //用来存放一些dom元素
var domView = document.createElement("div");
domView.style.cssText += "position:absolute;width:" + _width + "px;height:" + _height + "px;";
view.appendChild(stageView);
view.appendChild(domView);
return {
view: view,
stageView: stageView,
domView: domView
};
} //dom相关代码结束
};
/**
* Canvax
*
* @author 释剑 (李涛, litao.lt@alibaba-inc.com
*/
var Utils = {
mainFrameRate: 60,
//默认主帧率
now: 0,
/*给文本检测高宽专用*/
_pixelCtx: null,
__emptyFunc: function __emptyFunc() {},
//retina 屏幕优化
_UID: 0,
//该值为向上的自增长整数值
getUID: function getUID() {
return this._UID++;
},
createId: function createId(name) {
//if end with a digit, then append an undersBase before appending
var charCode = name.charCodeAt(name.length - 1);
if (charCode >= 48 && charCode <= 57) name += "_";
return name + Utils.getUID();
},
canvasSupport: function canvasSupport() {
return !!document.createElement('canvas').getContext;
},
initElement: function initElement(canvas) {
if (!window) return;
if (window.FlashCanvas && FlashCanvas.initElement) {
FlashCanvas.initElement(canvas);
}
return canvas;
},
/**
* 按照css的顺序,返回一个[上,右,下,左]
*/
getCssOrderArr: function getCssOrderArr(r) {
var r1;
var r2;
var r3;
var r4;
if (typeof r === 'number') {
r1 = r2 = r3 = r4 = r;
} else if (r instanceof Array) {
if (r.length === 1) {
r1 = r2 = r3 = r4 = r[0];
} else if (r.length === 2) {
r1 = r3 = r[0];
r2 = r4 = r[1];
} else if (r.length === 3) {
r1 = r[0];
r2 = r4 = r[1];
r3 = r[2];
} else {
r1 = r[0];
r2 = r[1];
r3 = r[2];
r4 = r[3];
}
} else {
r1 = r2 = r3 = r4 = 0;
}
return [r1, r2, r3, r4];
},
isWebGLSupported: function isWebGLSupported() {
var contextOptions = {
stencil: true
};
try {
if (!window.WebGLRenderingContext) //不存在直接return
{
return false;
}
var canvas = document.createElement('canvas'),
gl = canvas.getContext('webgl', contextOptions) || canvas.getContext('experimental-webgl', contextOptions);
return !!(gl && gl.getContextAttributes().stencil); //还要确实检测是否支持webGL模式
} catch (e) {
return false;
}
},
checkOpt: function checkOpt(opt) {
if (!opt) {
opt = {
context: {}
};
} else {
if (!opt.context) {
opt.context = {};
}
}
return opt;
}
};
var _canvas = Utils.initElement($.createCanvas(1, 1, "_pixelCanvas"));
Utils._pixelCtx = _canvas && _canvas.getContext('2d');
/**
* Canvax
*
* @author 释剑 (李涛, litao.lt@alibaba-inc.com)
*
* canvas 上委托的事件管理
*/
var Event = function Event(evt) {
var eventType = "CanvaxEvent";
if (_.isString(evt)) {
eventType = evt;
}
if (_.isObject(evt) && evt.type) {
eventType = evt.type;
_.extend(this, evt);
}
this.target = null;
this.currentTarget = null;
this.type = eventType;
this.point = null;
var me = this;
this._stopPropagation = false; //默认不阻止事件冒泡
this.stopPropagation = function () {
me._stopPropagation = true;
if (_.isObject(evt)) {
evt._stopPropagation = true;
}
};
this._preventDefault = false; //是否组织事件冒泡
this.preventDefault = function () {
me._preventDefault = true;
if (_.isObject(evt)) {
evt._preventDefault = true;
}
};
};
/**
* Canvax
*
* @author 释剑 (李涛, litao.lt@alibaba-inc.com)
*
* canvas 上委托的事件管理
*/
var _mouseEvents = 'mousedown mouseup mouseover mousemove mouseout click dblclick wheel keydown keypress keyup';
var types = {
_types: _mouseEvents.split(/,| /),
register: function register(evts) {
if (!evts) {
return;
}
if (_.isString(evts)) {
evts = evts.split(/,| /);
}
this._types = _mouseEvents.split(/,| /).concat(evts);
},
get: function get() {
return this._types;
}
};
/**
* Canvax
*
* @author 释剑 (李涛, litao.lt@alibaba-inc.com)
*
* 事件管理类
*/
/**
* 构造函数.
* @name EventDispatcher
* @class EventDispatcher类是可调度事件的类的基类,它允许显示列表上的任何对象都是一个事件目标。
*/
var Manager = function Manager() {
//事件映射表,格式为:{type1:[listener1, listener2], type2:[listener3, listener4]}
this._eventMap = {};
};
Manager.prototype = {
/**
* 判断events里面是否有用户交互事件
*/
_setEventEnable: function _setEventEnable() {
if (this.children) return; //容器的_eventEnabled不受注册的用户交互事件影响
var hasInteractionEvent = false;
for (var t in this._eventMap) {
if (_.indexOf(types.get(), t) > -1) {
hasInteractionEvent = true;
}
}
this._eventEnabled = hasInteractionEvent;
},
/*
* 注册事件侦听器对象,以使侦听器能够接收事件通知。
*/
_addEventListener: function _addEventListener(_type, listener) {
if (typeof listener != "function") {
//listener必须是个function呐亲
return false;
}
var addResult = true;
var self = this;
var types = _type;
if (_.isString(_type)) {
types = _type.split(/,| /);
}
_.each(types, function (type) {
var map = self._eventMap[type];
if (!map) {
map = self._eventMap[type] = [];
map.push(listener); //self._eventEnabled = true;
self._setEventEnable();
return true;
}
if (_.indexOf(map, listener) == -1) {
map.push(listener); //self._eventEnabled = true;
self._setEventEnable();
return true;
}
addResult = false;
});
return addResult;
},
/**
* 删除事件侦听器。
*/
_removeEventListener: function _removeEventListener(type, listener) {
if (arguments.length == 1) return this.removeEventListenerByType(type);
var map = this._eventMap[type];
if (!map) {
return false;
}
for (var i = 0; i < map.length; i++) {
var li = map[i];
if (li === listener) {
map.splice(i, 1);
if (map.length == 0) {
delete this._eventMap[type];
this._setEventEnable(); //如果这个如果这个时候child没有任何事件侦听
/*
if(_.isEmpty(this._eventMap)){
//那么该元素不再接受事件的检测
this._eventEnabled = false;
}
*/
}
return true;
}
}
return false;
},
/**
* 删除指定类型的所有事件侦听器。
*/
_removeEventListenerByType: function _removeEventListenerByType(type) {
var map = this._eventMap[type];
if (!map) {
delete this._eventMap[type];
this._setEventEnable(); //如果这个如果这个时候child没有任何事件侦听
/*
if(_.isEmpty(this._eventMap)){
//那么该元素不再接受事件的检测
this._eventEnabled = false;
}
*/
return true;
}
return false;
},
/**
* 删除所有事件侦听器。
*/
_removeAllEventListeners: function _removeAllEventListeners() {
this._eventMap = {};
this._eventEnabled = false;
},
/**
* 派发事件,调用事件侦听器。
*/
_dispatchEvent: function _dispatchEvent(e) {
var map = this._eventMap[e.type];
if (map) {
if (!e.target) e.target = this;
if (!e.currentTarget) e.currentTarget = this;
map = map.slice();
for (var i = 0; i < map.length; i++) {
var listener = map[i];
if (typeof listener == "function") {
listener.call(this, e);
}
}
}
if (!e._stopPropagation) {
//向上冒泡
if (this.parent) {
e.currentTarget = this.parent;
this.parent._dispatchEvent(e);
}
}
return true;
},
/**
* 检查是否为指定事件类型注册了任何侦听器。
*/
_hasEventListener: function _hasEventListener(type) {
var map = this._eventMap[type];
return map != null && map.length > 0;
}
};
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
var Dispatcher = /*#__PURE__*/function (_Manager) {
_inherits(Dispatcher, _Manager);
var _super = _createSuper(Dispatcher);
function Dispatcher() {
_classCallCheck(this, Dispatcher);
return _super.call(this);
}
_createClass(Dispatcher, [{
key: "on",
value: function on(type, listener) {
this._addEventListener(type, listener);
return this;
}
}, {
key: "addEventListener",
value: function addEventListener(type, listener) {
this._addEventListener(type, listener);
return this;
}
}, {
key: "un",
value: function un(type, listener) {
this._removeEventListener(type, listener);
return this;
}
}, {
key: "removeEventListener",
value: function removeEventListener(type, listener) {
this._removeEventListener(type, listener);
return this;
}
}, {
key: "removeEventListenerByType",
value: function removeEventListenerByType(type) {
this._removeEventListenerByType(type);
return this;
}
}, {
key: "removeAllEventListeners",
value: function removeAllEventListeners() {
this._removeAllEventListeners();
return this;
} //params 要传给evt的eventhandler处理函数的参数,会被merge到Canvax event中
}, {
key: "fire",
value: function fire(eventType, params) {
//{currentTarget,point,target,type,_stopPropagation}
var e = new Event(eventType);
if (params) {
for (var p in params) {
if (p != "type") {
e[p] = params[p];
}
}
}
var me = this;
_.each(eventType.split(" "), function (eType) {
//然后,currentTarget要修正为自己
e.currentTarget = me;
me.dispatchEvent(e);
});
return this;
}
}, {
key: "dispatchEvent",
value: function dispatchEvent(evt) {
//this instanceof DisplayObjectContainer ==> this.children
//TODO: 这里import DisplayObjectContainer 的话,在displayObject里面的import EventDispatcher from "../event/EventDispatcher";
//会得到一个undefined,感觉是成了一个循环依赖的问题,所以这里换用简单的判断来判断自己是一个容易,拥有children
if (this.children && evt.point) {
var target = this.getObjectsUnderPoint(evt.point, 1)[0];
if (target) {
target.dispatchEvent(evt);
}
return;
}
if (this.context && evt.type == "mouseover") {
//记录dispatchEvent之前的心跳
var preHeartBeat = this._heartBeatNum;
var pregAlpha = this.context.$model.globalAlpha;
this._dispatchEvent(evt);
if (preHeartBeat != this._heartBeatNum) {
this._hoverClass = true;
if (this.hoverClone) {
var canvax = this.getStage().parent; //然后clone一份obj,添加到_bufferStage 中
var activShape = this.clone(true);
activShape._transform = this.getConcatenatedMatrix();
canvax._bufferStage.addChildAt(activShape, 0); //然后把自己隐藏了
//用一个临时变量_globalAlpha 来存储自己之前的alpha
this._globalAlpha = pregAlpha;
this.context.globalAlpha = 0;
}
}
return;
}
this._dispatchEvent(evt);
if (this.context && evt.type == "mouseout") {
if (this._hoverClass && this.hoverClone) {
//说明刚刚over的时候有添加样式
var canvax = this.getStage().parent;
this._hoverClass = false;
canvax._bufferStage.removeChildById(this.id);
if (this._globalAlpha) {
this.context.globalAlpha = this._globalAlpha;
delete this._globalAlpha;
}
}
}
return this;
}
}, {
key: "hasEvent",
value: function hasEvent(type) {
return this._hasEventListener(type);
}
}, {
key: "hasEventListener",
value: function hasEventListener(type) {
return this._hasEventListener(type);
}
}, {
key: "hover",
value: function hover(overFun, outFun) {
this.on("mouseover", overFun);
this.on("mouseout", outFun);
return this;
}
}, {
key: "once",
value: function once(type, listener) {
var me = this;
var onceHandle = function onceHandle() {
listener.apply(me, arguments);
this.un(type, onceHandle);
};
this.on(type, onceHandle);
return this;
}
}]);
return Dispatcher;
}(Manager);
/**
* Canvax
*
* @author 释剑 (李涛, litao.lt@alibaba-inc.com)
*
*/
var Handler = function Handler(canvax) {
var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
this.canvax = canvax;
this.curPoints = [{
x: 0,
y: 0
}]; //X,Y 的 point 集合, 在touch下面则为 touch的集合,只是这个touch被添加了对应的x,y
//当前激活的点对应的obj,在touch下可以是个数组,和上面的 curPoints 对应
this.curPointsTarget = [];
this._touching = false; //正在拖动,前提是_touching=true
this._draging = false; //当前的鼠标状态
this._cursor = "default";
this.target = this.canvax.view; //mouse体统中不需要配置drag,touch中会用到第三方的touch库,每个库的事件名称可能不一样,
//就要这里配置,默认实现的是hammerjs的,所以默认可以在项目里引入hammerjs http://hammerjs.github.io/
this.drag = {
start: "panstart",
move: "panmove",
end: "panend"
};
this._opt = opt;
_.extend(true, this, opt);
}; //这样的好处是document.compareDocumentPosition只会在定义的时候执行一次。
var contains = document && document.compareDocumentPosition ? function (parent, child) {
if (!child) {
return false;
}
return !!(parent.compareDocumentPosition(child) & 16);
} : function (parent, child) {
if (!child) {
return false;
}
return child !== child && (parent.contains ? parent.contains(child) : true);
};
Handler.prototype = {
init: function init() {
//依次添加上浏览器的自带事件侦听
var me = this;
if (this._opt.events) {
types.register(this._opt.events);
}
if (me.target) {
if (me.target.nodeType == undefined) ;
$.addEvent(me.target, "contextmenu", function (e) {
if (e && e.preventDefault) {
e.preventDefault();
} else {
window.event.returnValue = false;
}
});
_.each(types.get(), function (type) {
//不再关心浏览器环境是否 'ontouchstart' in window
//而是直接只管传给事件模块的是一个原生dom还是 jq对象 or hammer对象等
if (me.target.nodeType == 1) {
$.addEvent(me.target, type, function (e) {
me.__mouseHandler(e);
});
} else {
me.target.on(type, function (e) {
me.__libHandler(e);
});
}
});
}
},
bindEventHandle: function bindEventHandle(e, type) {
if (type == 'mouse') {
this.__mouseHandler.apply(this, [e]);
} else {
this.__libHandler.apply(this, [e]);
}
},
/*
* 原生事件系统------------------------------------------------begin
* 鼠标事件处理函数
**/
__mouseHandler: function __mouseHandler(e) {
var me = this;
var root = me.canvax;
root.updateViewOffset();
me.curPoints = [{
x: $.pageX(e) - root.viewOffset.left,
y: $.pageY(e) - root.viewOffset.top
}]; //理论上来说,这里拿到point了后,就要计算这个point对应的target来push到curPointsTarget里,
//但是因为在drag的时候其实是可以不用计算对应target的。
//所以放在了下面的me.__getcurPointsTarget( e , curMousePoint );常规mousemove中执行
var curMousePoint = me.curPoints[0];
var curMouseTarget = me.curPointsTarget[0];
if ( //这几个事件触发过来,是一定需要检测 curMouseTarget 的
_.indexOf(['mousedown', 'mouseover', 'click'], e.type) > -1 && !curMouseTarget) {
if (!curMouseTarget) {
var obj = root.getObjectsUnderPoint(curMousePoint, 1)[0];
if (obj) {
me.curPointsTarget = [obj];
}
}
curMouseTarget = me.curPointsTarget[0];
}
//mousedown的时候 如果 curMouseTarget.dragEnabled 为true。就要开始准备drag了
if (e.type == "mousedown") {
//如果curTarget 的数组为空或者第一个为false ,,,
if (curMouseTarget && curMouseTarget.dragEnabled) {
//鼠标事件已经摸到了一个
me._touching = true;
}
}
if (e.type == "mouseup" || e.type == "mouseout" && !contains(root.view, e.toElement || e.relatedTarget)) {
if (me._draging == true) {
//说明刚刚在拖动
me._dragEnd(e, curMouseTarget, 0);
curMouseTarget.fire("dragend");
}
me._draging = false;
me._touching = false;
}
if (e.type == "mouseout") {
if (!contains(root.view, e.toElement || e.relatedTarget)) {
me.__getcurPointsTarget(e, curMousePoint, true);
}
} else if (e.type == "mousemove") {
//|| e.type == "mousedown" ){
//拖动过程中就不在做其他的mouseover检测,drag优先
if (me._touching && e.type == "mousemove" && curMouseTarget) {
//说明正在拖动啊
if (!me._draging) {
//begin drag
curMouseTarget.fire("dragstart"); //有可能该child没有hover style
if (!curMouseTarget._globalAlpha) {
curMouseTarget._globalAlpha = curMouseTarget.context.$model.globalAlpha;
}
curMouseTarget.context.globalAlpha = 0; //然后克隆一个副本到activeStage
var cloneObject = me._clone2hoverStage(curMouseTarget, 0);
cloneObject.context.globalAlpha = curMouseTarget._globalAlpha;
} else {
//drag move ing
me._dragIngHander(e, curMouseTarget, 0);
}
me._draging = true;
} else {
//常规mousemove检测
//move事件中,需要不停的搜索target,这个开销挺大,
//后续可以优化,加上和帧率相当的延迟处理
me.__getcurPointsTarget(e, curMousePoint);
}
} else {
//其他的事件就直接在target上面派发事件
var child = curMouseTarget;
if (!child) {
child = root;
}
me.__dispatchEventInChilds(e, [child]);
me._cursorHander(child);
}
if (root.preventDefault || e._preventDefault) {
//阻止默认浏览器动作(W3C)
if (e && e.preventDefault) {
e.preventDefault();
} else {
window.event.returnValue = false;
}
}
},
//notInRootView 真正的mouseout,鼠标已经不在图表的节点内了
__getcurPointsTarget: function __getcurPointsTarget(e, point, notInRootView) {
var me = this;
var root = me.canvax;
var oldObj = me.curPointsTarget[0];
if (oldObj && !oldObj.context) {
oldObj = null;
}
var e = new Event(e);
if (e.type == "mousemove" && oldObj && oldObj._hoverClass && oldObj.hoverClone && oldObj.pointChkPriority && oldObj.getChildInPoint(point)) {
//小优化,鼠标move的时候。计算频率太大,所以。做此优化
//如果有target存在,而且当前元素正在hoverStage中,而且当前鼠标还在target内,就没必要取检测整个displayList了
//开发派发常规mousemove事件
e.target = e.currentTarget = oldObj;
e.point = oldObj.globalToLocal(point);
oldObj.dispatchEvent(e);
return;
}
var obj = notInRootView ? null : root.getObjectsUnderPoint(point, 1)[0];
if (oldObj && oldObj != obj || e.type == "mouseout") {
if (oldObj && oldObj.context) {
me.curPointsTarget[0] = null;
e.type = "mouseout";
e.toTarget = obj;
e.target = e.currentTarget = oldObj;
e.point = oldObj.globalToLocal(point);
oldObj.dispatchEvent(e);
}
}
if (obj && oldObj != obj) {
me.curPointsTarget[0] = obj;
e.type = "mouseover";
e.fromTarget = oldObj;
e.target = e.currentTarget = obj;
e.point = obj.globalToLocal(point);
obj.dispatchEvent(e);
}
if (e.type == "mousemove" && obj) {
e.target = e.currentTarget = oldObj;
e.point = oldObj.globalToLocal(point);
oldObj.dispatchEvent(e);
}
me._cursorHander(obj, oldObj);
},
_cursorHander: function _cursorHander(obj, oldObj) {
if (!obj && !oldObj) {
this._setCursor("default");
}
if (obj && oldObj != obj && obj.context) {
this._setCursor(obj.context.$model.cursor);
}
},
_setCursor: function _setCursor(cursor) {
if (this._cursor == cursor) {
//如果两次要设置的鼠标状态是一样的
return;
}
this.canvax.view.style.cursor = cursor;
this._cursor = cursor;
},
/*
* 原生事件系统------------------------------------------------end
*/
/*
*第三方库的事件系统------------------------------------------------begin
*触屏事件处理函数
* */
__libHandler: function __libHandler(e) {
var me = this;
var root = me.canvax;
root.updateViewOffset(); // touch 下的 curPointsTarget 从touches中来
//获取canvax坐标系统里面的坐标
me.curPoints = me.__getCanvaxPointInTouchs(e);
if (!me._draging) {
//如果在draging的话,target已经是选中了的,可以不用 检测了
me.curPointsTarget = me.__getChildInTouchs(me.curPoints);
}
if (me.curPointsTarget.length > 0) {
//drag开始
if (me.drag.start.indexOf(e.type) > -1) {
//dragstart的时候touch已经准备好了target, curPointsTarget 里面只要有一个是有效的
//就认为drags开始
_.each(me.curPointsTarget, function (child, i) {
if (child && child.dragEnabled) {
//只要有一个元素就认为正在准备drag了
me._draging = true; //有可能该child没有hover style
if (!child._globalAlpha) {
child._globalAlpha = child.context.$model.globalAlpha;
}
me._clone2hoverStage(child, i); //先把本尊给隐藏了
child.context.globalAlpha = 0;
child.fire("dragstart");
return false;
}
});
}
if (me.drag.move.indexOf(e.type) > -1) {
if (me._draging) {
_.each(me.curPointsTarget, function (child, i) {
if (child && child.dragEnabled) {
me._dragIngHander(e, child, i);
}
});
}
}
if (me.drag.end.indexOf(e.type) > -1) {
if (me._draging) {
_.each(me.curPointsTarget, function (child, i) {
if (child && child.dragEnabled) {
me._dragEnd(e, child, 0);
child.fire("dragend");
}
});
me._draging = false;
}
}
me.__dispatchEventInChilds(e, me.curPointsTarget);
} else {
//如果当前没有一个target,就把事件派发到canvax上面
me.__dispatchEventInChilds(e, [root]);
}
},
//从touchs中获取到对应touch , 在上面添加上canvax坐标系统的x,y
__getCanvaxPointInTouchs: function __getCanvaxPointInTouchs(e) {
var me = this;
var root = me.canvax;
var curTouchs = [];
_.each(e.point || e.touches, function (touch) {
curTouchs.push({
x: 'x' in touch ? touch.x : $.pageX(touch) - root.viewOffset.left,
y: 'y' in touch ? touch.y : $.pageY(touch) - root.viewOffset.top
});
});
return curTouchs;
},
__getChildInTouchs: function __getChildInTouchs(touchs) {
var me = this;
var root = me.canvax;
var touchesTarget = [];
_.each(touchs, function (touch) {
touchesTarget.push(root.getObjectsUnderPoint(touch, 1)[0]);
});
return touchesTarget;
},
/*
*第三方库的事件系统------------------------------------------------end
*/
/*
*@param {array} childs
* */
__dispatchEventInChilds: function __dispatchEventInChilds(e, childs) {
if (!childs && !("length" in childs)) {
return false;
}
var me = this;
_.each(childs, function (child, i) {
if (child) {
var ce = new Event(e); //ce.target = ce.currentTarget = child || this;
ce.stagePoint = me.curPoints[i];
ce.point = child.globalToLocal(ce.stagePoint);
child.dispatchEvent(ce);
}
});
},
//克隆一个元素到hover stage中去
_clone2hoverStage: function _clone2hoverStage(target, i) {
var me = this;
var root = me.canvax;
var _dragDuplicate = root._bufferStage.getChildById(target.id);
if (!_dragDuplicate) {
_dragDuplicate = target.clone(true);
_dragDuplicate._transform = target.getConcatenatedMatrix();
/**
*TODO: 因为后续可能会有手动添加的 元素到_bufferStage 里面来
*比如tips
*这类手动添加进来的肯定是因为需要显示在最外层的。在hover元素之上。
*所有自动添加的hover元素都默认添加在_bufferStage的最底层
**/
root._bufferStage.addChildAt(_dragDuplicate, 0);
}
_dragDuplicate.context.globalAlpha = target._globalAlpha;
target._dragPoint = target.globalToLocal(me.curPoints[i]);
return _dragDuplicate;
},
//drag 中 的处理函数
_dragIngHander: function _dragIngHander(e, target, i) {
var me = this;
var root = me.canvax;
var _point = target.globalToLocal(me.curPoints[i]); //要对应的修改本尊的位置,但是要告诉引擎不要watch这个时候的变化
target._noHeart = true;
var _moveStage = target.moveing;
target.moveing = true;
target.context.x += _point.x - target._dragPoint.x;
target.context.y += _point.y - target._dragPoint.y;
target.fire("draging");
target.moveing = _moveStage;
target._noHeart = false; //同步完毕本尊的位置
//这里只能直接修改_transform 。 不能用下面的修改x,y的方式。
var _dragDuplicate = root._bufferStage.getChildById(target.id);
_dragDuplicate._transform = target.getConcatenatedMatrix(); //worldTransform在renderer的时候计算
_dragDuplicate.worldTransform = null; //setWorldTransform都统一在render中执行,这里注释掉
//_dragDuplicate.setWorldTransform();
//直接修改的_transform不会出发心跳上报, 渲染引擎不制动这个stage需要绘制。
//所以要手动出发心跳包
_dragDuplicate.heartBeat();
},
//drag结束的处理函数
//TODO: dragend的还需要处理end的点是否还在元素上面,要恢复hover状态
_dragEnd: function _dragEnd(e, target, i) {
var me = this;
var root = me.canvax; //_dragDuplicate 复制在_bufferStage 中的副本
var _dragDuplicate = root._bufferStage.getChildById(target.id);
_dragDuplicate && _dragDuplicate.destroy();
target.context.globalAlpha = target._globalAlpha;
}
};
var event = {
Event: Event,
Dispatcher: Dispatcher,
Handler: Handler,
Manager: Manager,
types: types
};
/**
* Canvax
*
* @author 释剑 (李涛, litao.lt@alibaba-inc.com)
*
* | a | c | tx|
* | b | d | ty|
* | 0 | 0 | 1 |
*
* @class
* @memberof PIXI
*
*
* Matrix 矩阵库 用于整个系统的几何变换计算
*/
var Matrix = function Matrix(a, b, c, d, tx, ty) {
this.a = a != undefined ? a : 1;
this.b = b != undefined ? b : 0;
this.c = c != undefined ? c : 0;
this.d = d != undefined ? d : 1;
this.tx = tx != undefined ? tx : 0;
this.ty = ty != undefined ? ty : 0;
this.array = null;
};
Matrix.prototype = {
concat: function concat(mtx) {
var a = this.a;
var c = this.c;
var tx = this.tx;
this.a = a * mtx.a + this.b * mtx.c;
this.b = a * mtx.b + this.b * mtx.d;
this.c = c * mtx.a + this.d * mtx.c;
this.d = c * mtx.b + this.d * mtx.d;
this.tx = tx * mtx.a + this.ty * mtx.c + mtx.tx;
this.ty = tx * mtx.b + this.ty * mtx.d + mtx.ty;
return this;
},
concatTransform: function concatTransform(x, y, scaleX, scaleY, rotation) {
var cos = 1;
var sin = 0;
if (rotation % 360) {
var r = rotation * Math.PI / 180;
cos = Math.cos(r);
sin = Math.sin(r);
}
this.concat(new Matrix(cos * scaleX, sin * scaleX, -sin * scaleY, cos * scaleY, x, y));
return this;
},
rotate: function rotate(angle) {
//目前已经提供对顺时针逆时针两个方向旋转的支持
var cos = Math.cos(angle);
var sin = Math.sin(angle);
var a = this.a;
var c = this.c;
var tx = this.tx;
if (angle > 0) {
this.a = a * cos - this.b * sin;
this.b = a * sin + this.b * cos;
this.c = c * cos - this.d * sin;
this.d = c * sin + this.d * cos;
this.tx = tx * cos - this.ty * sin;