Comparing version 0.0.1 to 0.0.2
@@ -0,1 +1,20 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.Transform = exports.M = void 0; | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
/** | ||
@@ -9,231 +28,238 @@ * 矩阵变换计算 | ||
*/ | ||
var M = { | ||
// 矩阵 相乘计算 | ||
mm: function mm(M1, M2) { | ||
var LEN = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 4; | ||
var m = []; | ||
export const M = { | ||
// 矩阵 相乘计算 | ||
mm(M1, M2, LEN = 4) { | ||
const m = []; | ||
for (let i = 0; i < LEN; i++) { | ||
for (let j = 0; j < LEN; j++) { | ||
let ele = 0; | ||
for (let z = 0; z < LEN; z++) { | ||
ele += M1[i * LEN + z] * M2[j + LEN * z]; | ||
} | ||
m.push(ele); | ||
} | ||
for (var i = 0; i < LEN; i++) { | ||
for (var j = 0; j < LEN; j++) { | ||
var ele = 0; | ||
for (var z = 0; z < LEN; z++) { | ||
ele += M1[i * LEN + z] * M2[j + LEN * z]; | ||
} | ||
return m.map((i) => +i.toFixed(4)); | ||
}, | ||
mms(...args) { | ||
return args.reduce((prev, current) => M.mm(current, prev)); | ||
}, | ||
// 旋转 | ||
rotate(R, m) { | ||
// x轴旋转 | ||
// const RM = [ | ||
// 1, 0, 0, 0, | ||
// 0, Math.cos(R), Math.sin(R), 0, | ||
// 0, -Math.sin(R), Math.cos(R), 0, | ||
// 0, 0, 0, 1, | ||
// ] | ||
// y轴旋转 | ||
// const RM = [ | ||
// Math.cos(R), 0, -Math.sin(R), 0, | ||
// Math.sin(R), 0, Math.cos(R), 0, | ||
// 0, 0, 1, 0, | ||
// 0, 0, 0, 1, | ||
// ] | ||
m.push(ele); | ||
} | ||
} | ||
// z轴旋转 | ||
// prettier-ignore | ||
const RM = [ | ||
Math.cos(R), -Math.sin(R), 0, 0, | ||
Math.sin(R), Math.cos(R), 0, 0, | ||
0, 0, 1, 0, | ||
0, 0, 0, 1 | ||
]; | ||
return m.map(function (i) { | ||
return +i.toFixed(4); | ||
}); | ||
}, | ||
mms: function mms() { | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return M.mm(RM, m); | ||
}, | ||
// 缩放 | ||
scale(S, m) { | ||
// prettier-ignore | ||
const RM = [ | ||
S, 0, 0, 0, | ||
0, S, 0, 0, | ||
0, 0, 1, 0, | ||
0, 0, 0, 1 | ||
]; | ||
return args.reduce(function (prev, current) { | ||
return M.mm(current, prev); | ||
}); | ||
}, | ||
// 旋转 | ||
rotate: function rotate(R, m) { | ||
// x轴旋转 | ||
// const RM = [ | ||
// 1, 0, 0, 0, | ||
// 0, Math.cos(R), Math.sin(R), 0, | ||
// 0, -Math.sin(R), Math.cos(R), 0, | ||
// 0, 0, 0, 1, | ||
// ] | ||
// y轴旋转 | ||
// const RM = [ | ||
// Math.cos(R), 0, -Math.sin(R), 0, | ||
// Math.sin(R), 0, Math.cos(R), 0, | ||
// 0, 0, 1, 0, | ||
// 0, 0, 0, 1, | ||
// ] | ||
// z轴旋转 | ||
// prettier-ignore | ||
var RM = [Math.cos(R), -Math.sin(R), 0, 0, Math.sin(R), Math.cos(R), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; | ||
return M.mm(RM, m); | ||
}, | ||
// 缩放 | ||
scale: function scale(S, m) { | ||
// prettier-ignore | ||
var RM = [S, 0, 0, 0, 0, S, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; | ||
return M.mm(RM, m); | ||
}, | ||
// 位移 | ||
translate: function translate(x, y, m) { | ||
// const newM = [...m] | ||
// newM[12] += x | ||
// newM[13] += y | ||
// return newM | ||
// prettier-ignore | ||
var RM = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, 0, 1]; | ||
return M.mm(RM, m); | ||
}, | ||
// 缩放位移矫正 | ||
trans: function trans(SCALE, current, origin, m) { | ||
// 在这里,我们设置transform-origin: 0 0;然后所有的计算以图片的左上角为基准,这样方便计算位置变换后的位移 | ||
// 1. 计算出current中点相对于左上角的坐标 | ||
// 2. 根据缩放比例,计算出中点坐标的相对位移 | ||
// 3. 矩阵 -> 缩放后矩阵 -> 位移矫正 | ||
var p = { | ||
x: (current.x + current.x1) / 2 - (origin.left + m[12]), | ||
y: (current.y + current.y1) / 2 - (origin.top + m[13]) | ||
}; // 计算位移 | ||
return M.mm(RM, m); | ||
}, | ||
// 位移 | ||
translate(x, y, m) { | ||
// const newM = [...m] | ||
// newM[12] += x | ||
// newM[13] += y | ||
// return newM | ||
// prettier-ignore | ||
const RM = [ | ||
1, 0, 0, 0, | ||
0, 1, 0, 0, | ||
0, 0, 1, 0, | ||
x, y, 0, 1 | ||
]; | ||
return M.mm(RM, m); | ||
}, | ||
// 缩放位移矫正 | ||
trans(SCALE, current, origin, m) { | ||
// 在这里,我们设置transform-origin: 0 0;然后所有的计算以图片的左上角为基准,这样方便计算位置变换后的位移 | ||
// 1. 计算出current中点相对于左上角的坐标 | ||
// 2. 根据缩放比例,计算出中点坐标的相对位移 | ||
// 3. 矩阵 -> 缩放后矩阵 -> 位移矫正 | ||
const p = { | ||
x: (current.x + current.x1) / 2 - (origin.left + m[12]), | ||
y: (current.y + current.y1) / 2 - (origin.top + m[13]), | ||
}; | ||
// 计算位移 | ||
const newP = { | ||
x: -p.x * (SCALE - 1), | ||
y: -p.y * (SCALE - 1), | ||
}; | ||
return M.translate(newP.x, newP.y, m); | ||
}, | ||
var newP = { | ||
x: -p.x * (SCALE - 1), | ||
y: -p.y * (SCALE - 1) | ||
}; | ||
return M.translate(newP.x, newP.y, m); | ||
} | ||
}; | ||
exports.M = M; | ||
export class Transform { | ||
state = { | ||
var Transform = /*#__PURE__*/function () { | ||
function Transform(m) { | ||
_classCallCheck(this, Transform); | ||
_defineProperty(this, "state", { | ||
scale: 1, | ||
rotate: 0, | ||
x: 0, | ||
y: 0 | ||
}); | ||
// prettier-ignore | ||
this.m = m || [// 初始状态 | ||
1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; | ||
} | ||
_createClass(Transform, [{ | ||
key: "snap", | ||
value: function snap() { | ||
return _objectSpread({}, this.state); | ||
} | ||
}, { | ||
key: "reset", | ||
value: function reset() { | ||
this.state = { | ||
scale: 1, | ||
rotate: 0, | ||
x: 0, | ||
y: 0, | ||
}; | ||
constructor(m) { | ||
// prettier-ignore | ||
this.m = m || [ | ||
// 初始状态 | ||
1, 0, 0, 0, | ||
0, 1, 0, 0, | ||
0, 0, 1, 0, | ||
0, 0, 0, 1, | ||
] | ||
y: 0 | ||
}; | ||
} | ||
}, { | ||
key: "toM", | ||
value: function toM() { | ||
var initM = this.m; | ||
var state = this.state; // const m = M.translate(state.x/state.scale, state.y/state.scale, M.rotate(state.rotate, M.scale(state.scale, initM))) | ||
// 先位移,再放在,再旋转 | ||
snap() { | ||
return { | ||
...this.state, | ||
}; | ||
var m = M.rotate(state.rotate, M.scale(state.scale, M.translate(state.x, state.y, initM))); | ||
return m; | ||
} | ||
reset() { | ||
this.state = { | ||
scale: 1, | ||
rotate: 0, | ||
x: 0, | ||
y: 0, | ||
}; | ||
}, { | ||
key: "toCss", | ||
value: function toCss() { | ||
return ";transform: matrix3D(".concat(this.toM().join(','), ");"); | ||
} | ||
}, { | ||
key: "translate", | ||
value: function translate(x, y, snap) { | ||
if (snap) { | ||
this.state.x = x + snap.x; | ||
this.state.y = y + snap.y; | ||
} else { | ||
this.state.x += x; | ||
this.state.y += y; | ||
} | ||
toM() { | ||
const initM = this.m; | ||
const state = this.state; | ||
// const m = M.translate(state.x/state.scale, state.y/state.scale, M.rotate(state.rotate, M.scale(state.scale, initM))) | ||
// 先位移,再放在,再旋转 | ||
const m = M.rotate(state.rotate, M.scale(state.scale, M.translate(state.x, state.y, initM))); | ||
return m; | ||
return this.toM(); | ||
} | ||
toCss() { | ||
return `;transform: matrix3D(${this.toM().join(',')});`; | ||
}, { | ||
key: "getTranslate", | ||
value: function getTranslate() { | ||
return [this.state.x, this.state.y]; | ||
} | ||
translate(x, y, snap) { | ||
if (snap) { | ||
this.state.x = x + snap.x; | ||
this.state.y = y + snap.y; | ||
} else { | ||
this.state.x += x; | ||
this.state.y += y; | ||
} | ||
return this.toM(); | ||
}, { | ||
key: "rotate", | ||
value: function rotate(R, origin) { | ||
var baseR = this.state.rotate; | ||
this.state.rotate = baseR + R; | ||
this.fixRotate(origin, baseR, R); | ||
return this.toM(); | ||
} | ||
}, { | ||
key: "fixRotate", | ||
value: function fixRotate(origin, baseR, rotate) { | ||
// 在这里,我们设置transform-origin: 0 0;然后所有的计算以图片的左上角为基准,这样方便计算位置变换后的位移 | ||
// 1. 计算出center中点相对于左上角的坐标 | ||
// 2. 根据缩放比例,计算出中点坐标的相对位移 | ||
// 3. 矩阵 -> 缩放后矩阵 -> 位移矫正 | ||
var _this$state = this.state, | ||
TX = _this$state.x, | ||
TY = _this$state.y; | ||
var p = { | ||
x: origin.width / 2 + origin.left - (origin.left + TX), | ||
y: origin.height / 2 + origin.top - (origin.top + TY) | ||
}; | ||
var len = Math.sqrt(Math.pow(p.x, 2) + Math.pow(p.y, 2)); | ||
var ang = Math.atan(p.y / p.x) + rotate; | ||
getTranslate() { | ||
return [this.state.x, this.state.y]; | ||
} | ||
if (baseR % (Math.PI * 2) >= Math.PI) { | ||
ang += Math.PI; | ||
} | ||
rotate(R, origin) { | ||
const baseR = this.state.rotate; | ||
this.state.rotate = baseR + R; | ||
this.fixRotate(origin, baseR, R); | ||
return this.toM(); | ||
} | ||
var X = Math.sin(ang) * len; | ||
var Y = Math.cos(ang) * len; // 因为每次修改都是90°,所以x,y的坐标需要交换 | ||
fixRotate(origin, baseR, rotate) { | ||
// 在这里,我们设置transform-origin: 0 0;然后所有的计算以图片的左上角为基准,这样方便计算位置变换后的位移 | ||
// 1. 计算出center中点相对于左上角的坐标 | ||
// 2. 根据缩放比例,计算出中点坐标的相对位移 | ||
// 3. 矩阵 -> 缩放后矩阵 -> 位移矫正 | ||
const { x: TX, y: TY } = this.state; | ||
const p = { | ||
x: origin.width / 2 + origin.left - (origin.left + TX), | ||
y: origin.height / 2 + origin.top - (origin.top + TY), | ||
}; | ||
var _ref = [Y, X]; | ||
X = _ref[0]; | ||
Y = _ref[1]; | ||
// 计算位移 | ||
var newX = p.x - X; | ||
var newY = -(p.y - Y); // 为什么y要取负数呢? | ||
const len = Math.sqrt(Math.pow(p.x, 2) + Math.pow(p.y, 2)); | ||
let ang = Math.atan(p.y / p.x) + rotate; | ||
if (baseR % (Math.PI * 2) >= Math.PI) { | ||
ang += Math.PI; | ||
} | ||
return this.translate(newY, newX); | ||
} | ||
}, { | ||
key: "scale", | ||
value: function scale(S, center, origin, snap) { | ||
if (snap) { | ||
this.state.scale = snap.scale * S; | ||
this.fixScale(center, origin, S - 1, snap); | ||
} else { | ||
this.state.scale *= S; // 根据变化的缩放比例进行位置校正 | ||
let X = Math.sin(ang) * len; | ||
let Y = Math.cos(ang) * len; | ||
this.fixScale(center, origin, S - 1); | ||
} | ||
// 因为每次修改都是90°,所以x,y的坐标需要交换 | ||
[X, Y] = [Y, X]; | ||
// 计算位移 | ||
const newX = p.x - X; | ||
const newY = -(p.y - Y); // 为什么y要取负数呢? | ||
return this.translate(newY, newX); | ||
return this.toM(); | ||
} | ||
}, { | ||
key: "fixScale", | ||
value: function fixScale(center, origin, scale, snap) { | ||
// 在这里,我们设置transform-origin: 0 0;然后所有的计算以图片的左上角为基准,这样方便计算位置变换后的位移 | ||
// 1. 计算出center中点相对于左上角的坐标 | ||
// 2. 根据缩放比例,计算出中点坐标的相对位移 | ||
// 3. 矩阵 -> 缩放后矩阵 -> 位移矫正 | ||
var _ref2 = snap || this.state, | ||
TX = _ref2.x, | ||
TY = _ref2.y; | ||
scale(S, center, origin, snap) { | ||
if (snap) { | ||
this.state.scale = snap.scale * S; | ||
this.fixScale(center, origin, S - 1, snap); | ||
} else { | ||
this.state.scale *= S; | ||
// 根据变化的缩放比例进行位置校正 | ||
this.fixScale(center, origin, S - 1); | ||
} | ||
return this.toM(); | ||
} | ||
var p = { | ||
x: (center.x + center.x1) / 2 - (origin.left + TX), | ||
y: (center.y + center.y1) / 2 - (origin.top + TY) | ||
}; // 计算位移 | ||
fixScale(center, origin, scale, snap) { | ||
// 在这里,我们设置transform-origin: 0 0;然后所有的计算以图片的左上角为基准,这样方便计算位置变换后的位移 | ||
// 1. 计算出center中点相对于左上角的坐标 | ||
// 2. 根据缩放比例,计算出中点坐标的相对位移 | ||
// 3. 矩阵 -> 缩放后矩阵 -> 位移矫正 | ||
const { x: TX, y: TY } = snap || this.state; | ||
const p = { | ||
x: (center.x + center.x1) / 2 - (origin.left + TX), | ||
y: (center.y + center.y1) / 2 - (origin.top + TY), | ||
}; | ||
// 计算位移 | ||
const newX = -p.x * scale; | ||
const newY = -p.y * scale; | ||
return this.translate(newX, newY, snap); | ||
var newX = -p.x * scale; | ||
var newY = -p.y * scale; | ||
return this.translate(newX, newY, snap); | ||
} | ||
getScale() { | ||
return this.state.scale; | ||
}, { | ||
key: "getScale", | ||
value: function getScale() { | ||
return this.state.scale; | ||
} | ||
} | ||
}]); | ||
return Transform; | ||
}(); | ||
/** | ||
@@ -245,1 +271,4 @@ * 始终相对左上角进行变换 | ||
*/ | ||
exports.Transform = Transform; |
@@ -1,21 +0,50 @@ | ||
const isIos = window.navigator.userAgent.match(/iphone|ios|ipad/i); | ||
const isPhone = window.navigator.userAgent.match(/phone|android|ios/i); | ||
const isPC = !window.navigator.userAgent.match(/phone|android|ios|pad/i); | ||
const [touchstart, touchmove, touchend, touchleave] = isPhone | ||
? ['touchstart', 'touchmove', 'touchend', 'touchcancel'] | ||
: ['mousedown', 'mousemove', 'mouseup', 'mouseleave']; | ||
const IOS_SYSTEM_SWIPE_WIDTH = 45; // ios系统返回触发宽度 | ||
"use strict"; | ||
export { | ||
touchstart, touchmove, touchend, touchleave, isIos, isPhone, isPC, IOS_SYSTEM_SWIPE_WIDTH, | ||
}; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.touchstart = exports.touchmove = exports.touchleave = exports.touchend = exports.isPhone = exports.isPC = exports.isIos = exports.TOUCH_DIRECTION = exports.TOUCH_ACTION = exports.IOS_SYSTEM_SWIPE_WIDTH = void 0; | ||
export const TOUCH_DIRECTION = { | ||
leftToRight: 'lr', | ||
topToBottom: 'ud', | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
var isIos = window.navigator.userAgent.match(/iphone|ios|ipad/i); | ||
exports.isIos = isIos; | ||
var isPhone = window.navigator.userAgent.match(/phone|android|ios/i); | ||
exports.isPhone = isPhone; | ||
var isPC = !window.navigator.userAgent.match(/phone|android|ios|pad/i); | ||
exports.isPC = isPC; | ||
var _ref = isPhone ? ['touchstart', 'touchmove', 'touchend', 'touchcancel'] : ['mousedown', 'mousemove', 'mouseup', 'mouseleave'], | ||
_ref2 = _slicedToArray(_ref, 4), | ||
touchstart = _ref2[0], | ||
touchmove = _ref2[1], | ||
touchend = _ref2[2], | ||
touchleave = _ref2[3]; | ||
exports.touchleave = touchleave; | ||
exports.touchend = touchend; | ||
exports.touchmove = touchmove; | ||
exports.touchstart = touchstart; | ||
var IOS_SYSTEM_SWIPE_WIDTH = 45; // ios系统返回触发宽度 | ||
exports.IOS_SYSTEM_SWIPE_WIDTH = IOS_SYSTEM_SWIPE_WIDTH; | ||
var TOUCH_DIRECTION = { | ||
leftToRight: 'lr', | ||
topToBottom: 'ud' | ||
}; | ||
export const TOUCH_ACTION = { | ||
swipe: 'swipe', | ||
pinch: 'pinch', | ||
} | ||
exports.TOUCH_DIRECTION = TOUCH_DIRECTION; | ||
var TOUCH_ACTION = { | ||
swipe: 'swipe', | ||
pinch: 'pinch' | ||
}; | ||
exports.TOUCH_ACTION = TOUCH_ACTION; |
1034
lib/core.js
@@ -1,37 +0,72 @@ | ||
/* eslint-disable max-len */ | ||
/* eslint-disable no-multi-assign */ | ||
/* eslint-disable no-restricted-properties */ | ||
import Event from './Event'; | ||
"use strict"; | ||
import { | ||
touchstart, | ||
touchmove, | ||
touchend, | ||
touchleave, | ||
isIos, | ||
isPhone, | ||
IOS_SYSTEM_SWIPE_WIDTH, | ||
TOUCH_DIRECTION, | ||
TOUCH_ACTION, | ||
} from './config'; | ||
import util from './util'; | ||
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports["default"] = void 0; | ||
var _Event2 = _interopRequireDefault(require("./Event")); | ||
var _config = require("./config"); | ||
var _util = _interopRequireDefault(require("./util")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } | ||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } | ||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } | ||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } | ||
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } | ||
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
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 _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
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; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
// touches changeTouches targetTouches 的区别 | ||
// https://stackoverflow.com/questions/7056026/variation-of-e-touches-e-targettouches-and-e-changedtouches | ||
function distance(x, y, x1, y1) { | ||
return Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2), 2); | ||
return Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2), 2); | ||
} | ||
function getFinger(e) { | ||
return e.touches ? e.touches.length : 1; | ||
return e.touches ? e.touches.length : 1; | ||
} | ||
function prevent(options, event) { | ||
if (event.cancelable) { | ||
options.preventDefault && event.preventDefault(); | ||
options.stopPropagation && event.stopPropagation(); | ||
} | ||
if (event.cancelable) { | ||
options.preventDefault && event.preventDefault(); | ||
options.stopPropagation && event.stopPropagation(); | ||
} | ||
} | ||
/* | ||
@@ -50,452 +85,573 @@ * touch core 提供回调 | ||
*/ | ||
export default class Touch extends Event { | ||
$dom = document.body; | ||
constructor($dom, options = {}) { | ||
super(); | ||
this.$dom = $dom; | ||
this.options = { | ||
finger: 1, // 默认为单指 | ||
forceResetTime: 500, // 强制reset时长 | ||
preventDefault: true, // 阻止默认事件 | ||
stopPropagation: false, // 是否冒泡 | ||
litenLeaveEvent: true, // 是否监听leave事件 | ||
iosSystemSwipe: true, | ||
XYWeight: 1, | ||
ingoreTouchElement: 'ingoreTouchElement', // 忽略touch事件的元素 | ||
canScrollElement: 'canScrollElement', // 可滚动元素 | ||
listenerOptions: {}, // addEventListener options 处理capture/passive等 | ||
activeClassName: '', // 触摸过程中为$dom添加的class | ||
effectElements: [], // 受影响的元素,会在其dom上添加activeClassName | ||
...options, | ||
}; | ||
this.init(); | ||
this.addEvents(); | ||
} | ||
// 初始化 | ||
init = () => { | ||
this.inited = true; | ||
this.state = { | ||
start: { x: 0, y: 0 }, // 滑动开始 | ||
move: { x: 0, y: 0 }, // 滑动中 | ||
moveCache: { x: 0, y: 0 }, // 位移缓存 | ||
change: { x: 0, y: 0 }, // x,y位移变化 | ||
end: { x: 0, y: 0 }, // 滑动结束 | ||
V: { x: 0, y: 0 }, // 位移速度 | ||
VCache: [], | ||
overAllV: { x: 0, y: 0 }, // 位移速度 | ||
scale: 1, // 缩放比例 | ||
scalePer: 1, // 缩放变化 | ||
scaleV: 0, // 缩放速度 | ||
rotate: 0, // 旋转角度 | ||
rotatePer: 0, // 旋转变化 | ||
rotateV: 0, // 旋转速度 | ||
finger: 0, // 触摸指个数 | ||
nativeFingers: 0, // 当前dom上的触摸点个数 | ||
enable: false, | ||
swipeX: true, | ||
swipeY: true, | ||
freeze: false, | ||
direction: '', // lr, ud | ||
isMove: false, | ||
action: '', // swipe pinch | ||
}; | ||
}; | ||
var Touch = /*#__PURE__*/function (_Event) { | ||
_inherits(Touch, _Event); | ||
/** | ||
* 在effectElements切换className | ||
* @param {boolean} [addClass=true] | ||
* @memberof Touch | ||
*/ | ||
toggleActiveClass(addClass = true) { | ||
const { effectElements = [], activeClassName } = this.options; | ||
if (activeClassName) { | ||
[this.$dom, ...effectElements].forEach((dom) => { | ||
dom instanceof window.HTMLElement && dom.classList.toggle(activeClassName, addClass); | ||
}); | ||
} | ||
} | ||
var _super = _createSuper(Touch); | ||
enable() { | ||
this._isDisable = false; | ||
} | ||
function Touch($dom) { | ||
var _this; | ||
/** | ||
* 阻止任何触摸滑动默认事件,阻止滑动冒泡事件 | ||
* @memberof Touch | ||
*/ | ||
disable() { | ||
this._isDisable = true; | ||
} | ||
var _options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
destroy() { | ||
this.isDestroy = true; | ||
this.removeEvents(); | ||
} | ||
_classCallCheck(this, Touch); | ||
isAddEvent = false; | ||
_this = _super.call(this); | ||
getListenerOptions() { | ||
return { | ||
passive: false, | ||
...this.options.listenerOptions, | ||
}; | ||
} | ||
_defineProperty(_assertThisInitialized(_this), "$dom", document.body); | ||
addEvents() { | ||
if (this.isDestroy || this.isAddEvent) return; | ||
this.isAddEvent = true; | ||
this.$dom.addEventListener(touchstart, this.start, this.getListenerOptions()); | ||
this.$dom.addEventListener(touchmove, this.move, this.getListenerOptions()); | ||
this.$dom.addEventListener(touchend, this.end, this.getListenerOptions()); | ||
!isPhone && | ||
this.options.litenLeaveEvent && | ||
this.$dom.addEventListener(touchleave, this.end, { passive: false }); | ||
} | ||
_defineProperty(_assertThisInitialized(_this), "init", function () { | ||
_this.inited = true; | ||
_this.state = { | ||
start: { | ||
x: 0, | ||
y: 0 | ||
}, | ||
// 滑动开始 | ||
move: { | ||
x: 0, | ||
y: 0 | ||
}, | ||
// 滑动中 | ||
moveCache: { | ||
x: 0, | ||
y: 0 | ||
}, | ||
// 位移缓存 | ||
change: { | ||
x: 0, | ||
y: 0 | ||
}, | ||
// x,y位移变化 | ||
end: { | ||
x: 0, | ||
y: 0 | ||
}, | ||
// 滑动结束 | ||
V: { | ||
x: 0, | ||
y: 0 | ||
}, | ||
// 位移速度 | ||
VCache: [], | ||
overAllV: { | ||
x: 0, | ||
y: 0 | ||
}, | ||
// 位移速度 | ||
scale: 1, | ||
// 缩放比例 | ||
scalePer: 1, | ||
// 缩放变化 | ||
scaleV: 0, | ||
// 缩放速度 | ||
rotate: 0, | ||
// 旋转角度 | ||
rotatePer: 0, | ||
// 旋转变化 | ||
rotateV: 0, | ||
// 旋转速度 | ||
finger: 0, | ||
// 触摸指个数 | ||
nativeFingers: 0, | ||
// 当前dom上的触摸点个数 | ||
enable: false, | ||
swipeX: true, | ||
swipeY: true, | ||
freeze: false, | ||
direction: '', | ||
// lr, ud | ||
isMove: false, | ||
action: '' // swipe pinch | ||
removeEvents() { | ||
this.$dom.removeEventListener(touchstart, this.start); | ||
this.$dom.removeEventListener(touchmove, this.move); | ||
this.$dom.removeEventListener(touchend, this.end); | ||
!isPhone && this.options.litenLeaveEvent && this.$dom.removeEventListener(touchleave, this.end); | ||
this.isAddEvent = false; | ||
} | ||
}; | ||
}); | ||
getPosition(e, index = 0) { | ||
let x = isPhone ? e.touches[index].clientX : e.clientX; | ||
let y = isPhone ? e.touches[index].clientY : e.clientY; | ||
// 使用相对位置 | ||
if (this.options.useRelativePos) { | ||
const { left, top } = this.$dom.getBoundingClientRect(); | ||
x -= left; | ||
y -= top; | ||
} | ||
return { | ||
[`x${index || ''}`]: x, | ||
[`y${index || ''}`]: y, | ||
}; | ||
} | ||
_defineProperty(_assertThisInitialized(_this), "isAddEvent", false); | ||
/** | ||
* 因为有些元素可能是可以滚动的,所以只能在滚动到边界的时候才能触发touch事件监听 | ||
* @param {*} currentTarget | ||
* @returns | ||
* @memberof Touch | ||
*/ | ||
setTouchLimit(currentTarget) { | ||
const { options, state } = this; | ||
if (options.canScrollElement) { | ||
const scrollElement = util.parent(currentTarget, this.$dom, (d) => | ||
d.classList.contains(options.canScrollElement) | ||
); | ||
if (scrollElement) { | ||
const { clientHeight, scrollHeight, scrollTop } = scrollElement; | ||
_defineProperty(_assertThisInitialized(_this), "start", function (event) { | ||
var _assertThisInitialize = _assertThisInitialized(_this), | ||
options = _assertThisInitialize.options, | ||
state = _assertThisInitialize.state; | ||
if (scrollHeight != clientHeight) { | ||
if (scrollTop == 0) { | ||
// 顶部 | ||
state.directionLimit = { | ||
direction: TOUCH_DIRECTION.topToBottom, | ||
only: 1, | ||
scrollElement, | ||
}; | ||
} else if (Math.ceil(scrollTop) >= scrollHeight - clientHeight) { | ||
// 底部 | ||
state.directionLimit = { | ||
direction: TOUCH_DIRECTION.topToBottom, | ||
only: -1, | ||
scrollElement, | ||
}; | ||
} else { | ||
state.directionLimit = { | ||
direction: TOUCH_DIRECTION.topToBottom, | ||
only: 0, // 不限定滚动方向 | ||
scrollElement, | ||
}; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
_this.removePcEvents && _this.removePcEvents(); | ||
if (_this._isDisable) return; | ||
prevent(options, event); | ||
/** | ||
* 判断当前元素是否是可以忽略滑动监听 | ||
* @param {*} target | ||
* @returns | ||
* @memberof Touch | ||
*/ | ||
checkIsTouchIgnore(currentTarget) { | ||
let { ingoreTouchElement } = this.options; | ||
if (!Array.isArray(ingoreTouchElement)) { | ||
ingoreTouchElement = [ingoreTouchElement]; | ||
_this.trigger('nativeStart', event, state); // 0到2 保持稳定 | ||
// 2到1 被销毁 | ||
// 2 -> 3 只有一个有用 | ||
state.nativeFingers = getFinger(event); | ||
_this.trigger('finger:change', state.nativeFingers, state.finger); | ||
if (!state.freeze && !state.isMove) { | ||
if (_this.checkIsTouchIgnore(event.target)) return; // 某些元素在内部滚动到尽头时,可以触发滚动,并且需要锁定滚动方向 | ||
_this.setTouchLimit(event.target); // 处理ios系统返回 | ||
if (_config.isIos && options.iosSystemSwipe && options.finger === 1 && (event.touches[0].clientX < _config.IOS_SYSTEM_SWIPE_WIDTH || event.touches[0].clientX > window.innerWidth - _config.IOS_SYSTEM_SWIPE_WIDTH)) { | ||
return; | ||
} | ||
const isIngnore = | ||
ingoreTouchElement.length && | ||
util.parent( | ||
currentTarget, | ||
this.$dom, | ||
(d) => | ||
ingoreTouchElement.some((item) => d.classList.contains(item)) || | ||
getComputedStyle(d).overflowX === 'scroll' | ||
); | ||
state.finger = getFinger(event); | ||
state.enable = true; | ||
state.start = state.move = state.moveCache = _objectSpread(_objectSpread({}, _this.getPosition(event)), {}, { | ||
time: Date.now() | ||
}); | ||
// options.checkIsTouchIgnore 也可以校验是否可以滑动 | ||
return isIngnore || (this.options.checkIsTouchIgnore && this.options.checkIsTouchIgnore(event)); | ||
} | ||
_this.toggleActiveClass(); // 如果是双指,记录双指位置 | ||
/** | ||
* 判断滑动方向是否受限 | ||
* @param {String} direction | ||
* @param {Bumber} change | ||
* @returns | ||
* @memberof Touch | ||
*/ | ||
checkTouchLimit(direction, change) { | ||
const { state } = this; | ||
const { directionLimit } = state; | ||
// 判断方向滑动方向是否一致 左右 上下 是否一致 | ||
if (directionLimit && directionLimit.direction === direction) { | ||
// 如果<=0 表示,滑动动向相反 | ||
if (change * directionLimit.only <= 0) { | ||
return true; | ||
} | ||
if (options.finger == 2 && getFinger(event) >= options.finger) { | ||
state.start = state.move = state.moveCache = _objectSpread(_objectSpread({}, _this.getPosition(event, 1)), state.start); | ||
} | ||
return this.options.checkTouchLimit && this.options.checkTouchLimit(direction, change); | ||
} | ||
_this.trigger('start', state, event); | ||
start = (event) => { | ||
const { options, state } = this; | ||
this.removePcEvents && this.removePcEvents(); | ||
if (this._isDisable) return; | ||
_this.trigger('startv2', { | ||
state: state, | ||
event: event | ||
}); | ||
} | ||
}); | ||
prevent(options, event); | ||
this.trigger('nativeStart', event, state); | ||
_defineProperty(_assertThisInitialized(_this), "move", function (event) { | ||
if (_this._isDisable) { | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
return; | ||
} | ||
// 0到2 保持稳定 | ||
// 2到1 被销毁 | ||
// 2 -> 3 只有一个有用 | ||
state.nativeFingers = getFinger(event); | ||
this.trigger('finger:change', state.nativeFingers, state.finger); | ||
if (!state.freeze && !state.isMove) { | ||
if (this.checkIsTouchIgnore(event.target)) return; | ||
// 某些元素在内部滚动到尽头时,可以触发滚动,并且需要锁定滚动方向 | ||
var _assertThisInitialize2 = _assertThisInitialized(_this), | ||
state = _assertThisInitialize2.state, | ||
options = _assertThisInitialize2.options; | ||
this.setTouchLimit(event.target); | ||
prevent(options, event); // 如果想自己控制preventDefault 可以监听nativeChange事件,处理event | ||
// 处理ios系统返回 | ||
if ( | ||
isIos && | ||
options.iosSystemSwipe && | ||
options.finger === 1 && | ||
(event.touches[0].clientX < IOS_SYSTEM_SWIPE_WIDTH || | ||
event.touches[0].clientX > window.innerWidth - IOS_SYSTEM_SWIPE_WIDTH) | ||
) { | ||
return; | ||
} | ||
_this.trigger('nativeChange', event); | ||
state.finger = getFinger(event); | ||
state.enable = true; | ||
state.start = state.move = state.moveCache = { | ||
...this.getPosition(event), | ||
time: Date.now(), | ||
}; | ||
this.toggleActiveClass(); | ||
// 如果是双指,记录双指位置 | ||
if (options.finger == 2 && getFinger(event) >= options.finger) { | ||
state.start = state.move = state.moveCache = { | ||
...this.getPosition(event, 1), | ||
...state.start, | ||
}; | ||
} | ||
this.trigger('start', state, event); | ||
this.trigger('startv2', { state, event }); | ||
} | ||
}; | ||
var start = state.start, | ||
moveCache = state.moveCache, | ||
swipeX = state.swipeX, | ||
swipeY = state.swipeY; | ||
move = (event) => { | ||
if (this._isDisable) { | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
if (state.touchLimit) { | ||
return; | ||
} // 同时只能响应一种手势,滑动 还是 缩放 | ||
if (state.enable && !state.freeze) { | ||
var move = _objectSpread(_objectSpread({}, _this.getPosition(event)), {}, { | ||
time: Date.now() | ||
}); | ||
var T = (move.time - moveCache.time) * 1000; | ||
if (!state.swipeY || swipeX && Math.abs(move.x - start.x) * options.XYWeight > Math.abs(move.y - start.y)) { | ||
// canScrollElement在可以滚动的区间,不trigger滚动事件,但在touchend后要对state做重置 | ||
if (state.swipeY && _this.checkTouchLimit(_config.TOUCH_DIRECTION.leftToRight, move.x - start.x)) { | ||
state.touchLimit = true; | ||
return; | ||
} | ||
state.direction = _config.TOUCH_DIRECTION.leftToRight; | ||
state.isMove = true; | ||
state.swipeY = false; | ||
} | ||
const { state, options } = this; | ||
prevent(options, event); | ||
// 如果想自己控制preventDefault 可以监听nativeChange事件,处理event | ||
this.trigger('nativeChange', event); | ||
if (!state.swipeX || swipeY && Math.abs(move.x - start.x) * options.XYWeight < Math.abs(move.y - start.y) // 注释<=逻辑,否则pc端点击之后,就无法左右滑动了 | ||
) { | ||
if (state.swipeX && _this.checkTouchLimit(_config.TOUCH_DIRECTION.topToBottom, move.y - start.y)) { | ||
state.touchLimit = true; | ||
return; | ||
} | ||
const { start, moveCache, swipeX, swipeY } = state; | ||
state.isMove = true; | ||
state.swipeX = false; | ||
state.direction = _config.TOUCH_DIRECTION.topToBottom; | ||
} // 开始滚动前 | ||
if (state.touchLimit) { | ||
return; | ||
!state.isMove && _this.trigger('beforeChange', state, event); | ||
if (options.finger === 2 && getFinger(event) === options.finger && state.action !== _config.TOUCH_ACTION.swipe) { | ||
state.action = _config.TOUCH_ACTION.pinch; | ||
move = _objectSpread(_objectSpread({}, _this.getPosition(event, 1)), move); | ||
state.move = move; | ||
state.change = { | ||
x: move.x - start.x, | ||
y: move.y - start.y, | ||
x1: move.x1 - start.x1, | ||
y1: move.y1 - start.y1 | ||
}; // 缩放比例 | ||
state.scale = distance(move.x, move.y, move.x1, move.y1) / distance(start.x, start.y, start.x1, start.y1); | ||
state.scalePer = distance(move.x, move.y, move.x1, move.y1) / distance(moveCache.x, moveCache.y, moveCache.x1, moveCache.y1); | ||
state.scaleV = state.scalePer / T; // 旋转角度 | ||
state.rotate = Math.atan2(move.y - move.y1, move.x - move.x1) - Math.atan2(start.y - start.y1, start.x - start.x1); | ||
state.rotatePer = Math.atan2(move.y - move.y1, move.x - move.x1) - Math.atan2(moveCache.y - moveCache.y1, moveCache.x - moveCache.x1); | ||
state.rotateV = state.rotatePer / T; // 计算滑动速度 | ||
state.V = { | ||
x: (move.x - moveCache.x) / T, | ||
y: (move.y - moveCache.y) / T | ||
}; | ||
state.VCache.push(state.V); | ||
if (state.VCache.length > 4) { | ||
state.VCache.shift(); | ||
} // state.rotate *= 180 / Math.PI | ||
_this.trigger('change', state.change, state, event); | ||
_this.trigger('changev2', { | ||
state: state, | ||
event: event | ||
}); | ||
state.moveCache = _objectSpread({}, state.move); | ||
} | ||
// 同时只能响应一种手势,滑动 还是 缩放 | ||
if (state.enable && !state.freeze) { | ||
let move = { | ||
...this.getPosition(event), | ||
time: Date.now(), | ||
}; | ||
const T = (move.time - moveCache.time) * 1000; | ||
if (getFinger(event) === 1) { | ||
state.action = _config.TOUCH_ACTION.swipe; | ||
state.move = move; | ||
state.change = { | ||
x: move.x - start.x, | ||
y: move.y - start.y | ||
}; // 计算滑动速度 | ||
if ( | ||
!state.swipeY || | ||
(swipeX && Math.abs(move.x - start.x) * options.XYWeight > Math.abs(move.y - start.y)) | ||
) { | ||
// canScrollElement在可以滚动的区间,不trigger滚动事件,但在touchend后要对state做重置 | ||
if (state.swipeY && this.checkTouchLimit(TOUCH_DIRECTION.leftToRight, move.x - start.x)) { | ||
state.touchLimit = true; | ||
return; | ||
} | ||
state.direction = TOUCH_DIRECTION.leftToRight; | ||
state.isMove = true; | ||
state.swipeY = false; | ||
} | ||
state.V = { | ||
x: (move.x - moveCache.x) / T, | ||
y: (move.y - moveCache.y) / T | ||
}; // 保留最近4个瞬时速度,以供消费 | ||
if ( | ||
!state.swipeX || | ||
(swipeY && Math.abs(move.x - start.x) * options.XYWeight < Math.abs(move.y - start.y)) // 注释<=逻辑,否则pc端点击之后,就无法左右滑动了 | ||
) { | ||
if (state.swipeX && this.checkTouchLimit(TOUCH_DIRECTION.topToBottom, move.y - start.y)) { | ||
state.touchLimit = true; | ||
return; | ||
} | ||
state.isMove = true; | ||
state.swipeX = false; | ||
state.direction = TOUCH_DIRECTION.topToBottom; | ||
} | ||
state.VCache.push(state.V); | ||
// 开始滚动前 | ||
!state.isMove && this.trigger('beforeChange', state, event); | ||
if (state.VCache.length > 4) { | ||
state.VCache.shift(); | ||
} // 需要提供最后一次有效滑动方向 | ||
// 以及滑动速度 | ||
// 滑动 | ||
// todo,trigger传递的state,应该是不可变state,如果传递引用的话,会导致一些问题 | ||
if (options.finger === 2 && getFinger(event) === options.finger && state.action !== TOUCH_ACTION.swipe) { | ||
state.action = TOUCH_ACTION.pinch; | ||
move = { | ||
...this.getPosition(event, 1), | ||
...move, | ||
}; | ||
state.move = move; | ||
state.change = { | ||
x: move.x - start.x, | ||
y: move.y - start.y, | ||
x1: move.x1 - start.x1, | ||
y1: move.y1 - start.y1, | ||
}; | ||
// 缩放比例 | ||
state.scale = | ||
distance(move.x, move.y, move.x1, move.y1) / distance(start.x, start.y, start.x1, start.y1); | ||
state.scalePer = | ||
distance(move.x, move.y, move.x1, move.y1) / | ||
distance(moveCache.x, moveCache.y, moveCache.x1, moveCache.y1); | ||
state.scaleV = state.scalePer / T; | ||
// 旋转角度 | ||
state.rotate = | ||
Math.atan2(move.y - move.y1, move.x - move.x1) - Math.atan2(start.y - start.y1, start.x - start.x1); | ||
state.rotatePer = | ||
Math.atan2(move.y - move.y1, move.x - move.x1) - | ||
Math.atan2(moveCache.y - moveCache.y1, moveCache.x - moveCache.x1); | ||
state.rotateV = state.rotatePer / T; | ||
// 计算滑动速度 | ||
state.V = { | ||
x: (move.x - moveCache.x) / T, | ||
y: (move.y - moveCache.y) / T, | ||
}; | ||
state.VCache.push(state.V); | ||
if (state.VCache.length > 4) { | ||
state.VCache.shift(); | ||
} | ||
// state.rotate *= 180 / Math.PI | ||
this.trigger('change', state.change, state, event); | ||
this.trigger('changev2', { state, event }); | ||
state.moveCache = { ...state.move }; | ||
} | ||
if (getFinger(event) === 1) { | ||
state.action = TOUCH_ACTION.swipe; | ||
state.move = move; | ||
state.change = { | ||
x: move.x - start.x, | ||
y: move.y - start.y, | ||
}; | ||
_this.trigger('change', state.change, state, event); | ||
// 计算滑动速度 | ||
state.V = { | ||
x: (move.x - moveCache.x) / T, | ||
y: (move.y - moveCache.y) / T, | ||
}; | ||
// 保留最近4个瞬时速度,以供消费 | ||
state.VCache.push(state.V); | ||
if (state.VCache.length > 4) { | ||
state.VCache.shift(); | ||
} | ||
// 需要提供最后一次有效滑动方向 | ||
// 以及滑动速度 | ||
// 滑动 | ||
_this.trigger('changev2', { | ||
state: state, | ||
event: event | ||
}); | ||
// todo,trigger传递的state,应该是不可变state,如果传递引用的话,会导致一些问题 | ||
this.trigger('change', state.change, state, event); | ||
this.trigger('changev2', { state, event }); | ||
state.moveCache = state.move; | ||
} | ||
state.moveCache = state.move; | ||
} | ||
}; | ||
} | ||
}); | ||
end = (event) => { | ||
if (this._isDisable) return false; | ||
_defineProperty(_assertThisInitialized(_this), "end", function (event) { | ||
if (_this._isDisable) return false; | ||
const { state, options } = this; | ||
if (state.isMove) { | ||
this.removePcEvents = this.ingorePcClick(); | ||
var _assertThisInitialize3 = _assertThisInitialized(_this), | ||
state = _assertThisInitialize3.state, | ||
options = _assertThisInitialize3.options; | ||
if (state.isMove) { | ||
_this.removePcEvents = _this.ingorePcClick(); | ||
} // 触发 | ||
state.nativeFingers = getFinger(event); | ||
_this.trigger('finger:change', state.nativeFingers, state.finger); | ||
_this.trigger('nativeEnd', event); // 只有当前手指个数小于预期个数才有触发touch end的必要 | ||
// 这么做的意义在哪里?监听事件可能会被重复触发? | ||
if (state.nativeFingers < options.finger || !_config.isPhone) { | ||
_this.touchend(event); | ||
} | ||
return false; | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "touchend", function (event) { | ||
var _assertThisInitialize4 = _assertThisInitialized(_this), | ||
state = _assertThisInitialize4.state; | ||
var isDone = false; | ||
var timeout; | ||
var done = function done() { | ||
if (!isDone) { | ||
clearTimeout(timeout); | ||
isDone = true; | ||
var scale = _this.state.scale; | ||
_this.init(); | ||
_this.state.scale = scale; | ||
_this.toggleActiveClass(false); | ||
} | ||
// 触发 | ||
state.nativeFingers = getFinger(event); | ||
this.trigger('finger:change', state.nativeFingers, state.finger); | ||
this.trigger('nativeEnd', event); | ||
// 只有当前手指个数小于预期个数才有触发touch end的必要 | ||
// 这么做的意义在哪里?监听事件可能会被重复触发? | ||
if (state.nativeFingers < options.finger || !isPhone) { | ||
this.touchend(event); | ||
} | ||
return false; | ||
}; | ||
}; // 避免安卓手机,滑动穿透到body上去 | ||
// 根据经验主义,如果滚动穿透到body,点击元素,就会把滚动移交给当前元素 | ||
touchend = (event) => { | ||
const { state } = this; | ||
let isDone = false; | ||
let timeout; | ||
const done = () => { | ||
if (!isDone) { | ||
clearTimeout(timeout); | ||
isDone = true; | ||
const { scale } = this.state; | ||
this.init(); | ||
this.state.scale = scale; | ||
this.toggleActiveClass(false); | ||
} | ||
}; | ||
// 避免安卓手机,滑动穿透到body上去 | ||
// 根据经验主义,如果滚动穿透到body,点击元素,就会把滚动移交给当前元素 | ||
if (state.directionLimit && state.directionLimit.scrollElement) { | ||
state.directionLimit.scrollElement.click(); | ||
if (state.directionLimit && state.directionLimit.scrollElement) { | ||
state.directionLimit.scrollElement.click(); | ||
} // 如果是无效滑动依然会在一定时间后执行done,但是没有在touchstart的时候clear定时器 | ||
// timeout = setTimeout(done, options.forceResetTime) // 可能由于种种原因导致应该执行done,但是没有执行,这里用来重置 | ||
if (state.enable && !state.freeze) { | ||
state.freeze = true; // 冻结,不响应事件 | ||
_this.trigger('end', state.change, done, state, event); // 触发onEnd,等待初始化 | ||
_this.trigger('endv2', { | ||
done: done, | ||
state: state, | ||
event: event | ||
}); // 如果没有移动就立即终结 | ||
if (!state.direction || state.touchLimit) { | ||
done(); | ||
} | ||
// 如果是无效滑动依然会在一定时间后执行done,但是没有在touchstart的时候clear定时器 | ||
// timeout = setTimeout(done, options.forceResetTime) // 可能由于种种原因导致应该执行done,但是没有执行,这里用来重置 | ||
if (state.enable && !state.freeze) { | ||
state.freeze = true; // 冻结,不响应事件 | ||
} | ||
}); | ||
this.trigger('end', state.change, done, state, event); // 触发onEnd,等待初始化 | ||
this.trigger('endv2', { done, state, event }); | ||
// 如果没有移动就立即终结 | ||
if (!state.direction || state.touchLimit) { | ||
done(); | ||
_this.$dom = $dom; | ||
_this.options = _objectSpread({ | ||
finger: 1, | ||
// 默认为单指 | ||
forceResetTime: 500, | ||
// 强制reset时长 | ||
preventDefault: true, | ||
// 阻止默认事件 | ||
stopPropagation: false, | ||
// 是否冒泡 | ||
litenLeaveEvent: true, | ||
// 是否监听leave事件 | ||
iosSystemSwipe: true, | ||
XYWeight: 1, | ||
ingoreTouchElement: 'ingoreTouchElement', | ||
// 忽略touch事件的元素 | ||
canScrollElement: 'canScrollElement', | ||
// 可滚动元素 | ||
listenerOptions: {}, | ||
// addEventListener options 处理capture/passive等 | ||
activeClassName: '', | ||
// 触摸过程中为$dom添加的class | ||
effectElements: [] | ||
}, _options); | ||
_this.init(); | ||
_this.addEvents(); | ||
return _this; | ||
} // 初始化 | ||
_createClass(Touch, [{ | ||
key: "toggleActiveClass", | ||
value: | ||
/** | ||
* 在effectElements切换className | ||
* @param {boolean} [addClass=true] | ||
* @memberof Touch | ||
*/ | ||
function toggleActiveClass() { | ||
var addClass = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; | ||
var _this$options = this.options, | ||
_this$options$effectE = _this$options.effectElements, | ||
effectElements = _this$options$effectE === void 0 ? [] : _this$options$effectE, | ||
activeClassName = _this$options.activeClassName; | ||
if (activeClassName) { | ||
[this.$dom].concat(_toConsumableArray(effectElements)).forEach(function (dom) { | ||
dom instanceof window.HTMLElement && dom.classList.toggle(activeClassName, addClass); | ||
}); | ||
} | ||
} | ||
}, { | ||
key: "enable", | ||
value: function enable() { | ||
this._isDisable = false; | ||
} | ||
/** | ||
* 阻止任何触摸滑动默认事件,阻止滑动冒泡事件 | ||
* @memberof Touch | ||
*/ | ||
}, { | ||
key: "disable", | ||
value: function disable() { | ||
this._isDisable = true; | ||
} | ||
}, { | ||
key: "destroy", | ||
value: function destroy() { | ||
this.isDestroy = true; | ||
this.removeEvents(); | ||
} | ||
}, { | ||
key: "getListenerOptions", | ||
value: function getListenerOptions() { | ||
return _objectSpread({ | ||
passive: false | ||
}, this.options.listenerOptions); | ||
} | ||
}, { | ||
key: "addEvents", | ||
value: function addEvents() { | ||
if (this.isDestroy || this.isAddEvent) return; | ||
this.isAddEvent = true; | ||
this.$dom.addEventListener(_config.touchstart, this.start, this.getListenerOptions()); | ||
this.$dom.addEventListener(_config.touchmove, this.move, this.getListenerOptions()); | ||
this.$dom.addEventListener(_config.touchend, this.end, this.getListenerOptions()); | ||
!_config.isPhone && this.options.litenLeaveEvent && this.$dom.addEventListener(_config.touchleave, this.end, { | ||
passive: false | ||
}); | ||
} | ||
}, { | ||
key: "removeEvents", | ||
value: function removeEvents() { | ||
this.$dom.removeEventListener(_config.touchstart, this.start); | ||
this.$dom.removeEventListener(_config.touchmove, this.move); | ||
this.$dom.removeEventListener(_config.touchend, this.end); | ||
!_config.isPhone && this.options.litenLeaveEvent && this.$dom.removeEventListener(_config.touchleave, this.end); | ||
this.isAddEvent = false; | ||
} | ||
}, { | ||
key: "getPosition", | ||
value: function getPosition(e) { | ||
var _ref; | ||
var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
var x = _config.isPhone ? e.touches[index].clientX : e.clientX; | ||
var y = _config.isPhone ? e.touches[index].clientY : e.clientY; // 使用相对位置 | ||
if (this.options.useRelativePos) { | ||
var _this$$dom$getBoundin = this.$dom.getBoundingClientRect(), | ||
left = _this$$dom$getBoundin.left, | ||
top = _this$$dom$getBoundin.top; | ||
x -= left; | ||
y -= top; | ||
} | ||
return _ref = {}, _defineProperty(_ref, "x".concat(index || ''), x), _defineProperty(_ref, "y".concat(index || ''), y), _ref; | ||
} | ||
/** | ||
* 因为有些元素可能是可以滚动的,所以只能在滚动到边界的时候才能触发touch事件监听 | ||
* @param {*} currentTarget | ||
* @returns | ||
* @memberof Touch | ||
*/ | ||
}, { | ||
key: "setTouchLimit", | ||
value: function setTouchLimit(currentTarget) { | ||
var options = this.options, | ||
state = this.state; | ||
if (options.canScrollElement) { | ||
var scrollElement = _util["default"].parent(currentTarget, this.$dom, function (d) { | ||
return d.classList.contains(options.canScrollElement); | ||
}); | ||
if (scrollElement) { | ||
var clientHeight = scrollElement.clientHeight, | ||
scrollHeight = scrollElement.scrollHeight, | ||
scrollTop = scrollElement.scrollTop; | ||
if (scrollHeight != clientHeight) { | ||
if (scrollTop == 0) { | ||
// 顶部 | ||
state.directionLimit = { | ||
direction: _config.TOUCH_DIRECTION.topToBottom, | ||
only: 1, | ||
scrollElement: scrollElement | ||
}; | ||
} else if (Math.ceil(scrollTop) >= scrollHeight - clientHeight) { | ||
// 底部 | ||
state.directionLimit = { | ||
direction: _config.TOUCH_DIRECTION.topToBottom, | ||
only: -1, | ||
scrollElement: scrollElement | ||
}; | ||
} else { | ||
state.directionLimit = { | ||
direction: _config.TOUCH_DIRECTION.topToBottom, | ||
only: 0, | ||
// 不限定滚动方向 | ||
scrollElement: scrollElement | ||
}; | ||
} | ||
} | ||
} | ||
}; | ||
} | ||
} | ||
/** | ||
* 判断当前元素是否是可以忽略滑动监听 | ||
* @param {*} target | ||
* @returns | ||
* @memberof Touch | ||
*/ | ||
}, { | ||
key: "checkIsTouchIgnore", | ||
value: function checkIsTouchIgnore(currentTarget) { | ||
var ingoreTouchElement = this.options.ingoreTouchElement; | ||
if (!Array.isArray(ingoreTouchElement)) { | ||
ingoreTouchElement = [ingoreTouchElement]; | ||
} | ||
var isIngnore = ingoreTouchElement.length && _util["default"].parent(currentTarget, this.$dom, function (d) { | ||
return ingoreTouchElement.some(function (item) { | ||
return d.classList.contains(item); | ||
}) || getComputedStyle(d).overflowX === 'scroll'; | ||
}); // options.checkIsTouchIgnore 也可以校验是否可以滑动 | ||
return isIngnore || this.options.checkIsTouchIgnore && this.options.checkIsTouchIgnore(event); | ||
} | ||
/** | ||
* 判断滑动方向是否受限 | ||
* @param {String} direction | ||
* @param {Bumber} change | ||
* @returns | ||
* @memberof Touch | ||
*/ | ||
}, { | ||
key: "checkTouchLimit", | ||
value: function checkTouchLimit(direction, change) { | ||
var state = this.state; | ||
var directionLimit = state.directionLimit; // 判断方向滑动方向是否一致 左右 上下 是否一致 | ||
if (directionLimit && directionLimit.direction === direction) { | ||
// 如果<=0 表示,滑动动向相反 | ||
if (change * directionLimit.only <= 0) { | ||
return true; | ||
} | ||
} | ||
return this.options.checkTouchLimit && this.options.checkTouchLimit(direction, change); | ||
} | ||
}, { | ||
key: "ingorePcClick", | ||
value: | ||
/** | ||
* https://stackoverflow.com/questions/8643739/cancel-click-event-in-the-mouseup-event-handler/8927598 | ||
@@ -506,23 +662,27 @@ * 解决pc上mouseup事件结束后,click事件会触发的问题 | ||
*/ | ||
ingorePcClick() { | ||
if (isPhone) return; | ||
function ingorePcClick() { | ||
if (_config.isPhone) return; | ||
var remove; | ||
let remove; | ||
const handleClick = (e) => { | ||
e.stopPropagation(); | ||
remove(); | ||
}; | ||
var handleClick = function handleClick(e) { | ||
e.stopPropagation(); | ||
remove(); | ||
}; | ||
const option = { | ||
capture: true, | ||
}; | ||
var option = { | ||
capture: true | ||
}; | ||
remove = () => { | ||
window.removeEventListener('click', handleClick, option); | ||
}; | ||
remove = function remove() { | ||
window.removeEventListener('click', handleClick, option); | ||
}; | ||
window.addEventListener('click', handleClick, option); | ||
window.addEventListener('click', handleClick, option); | ||
return remove; | ||
} | ||
}]); | ||
return remove; | ||
} | ||
} | ||
return Touch; | ||
}(_Event2["default"]); | ||
exports["default"] = Touch; |
@@ -1,36 +0,77 @@ | ||
export default class Event { | ||
_cache = {}; | ||
"use strict"; | ||
on(name, callback) { | ||
const { _cache } = this; | ||
_cache[name] = _cache[name] || []; | ||
if (callback) { | ||
_cache[name].push(callback); | ||
} else { | ||
_cache[name].forEach((cb) => cb()); | ||
} | ||
return this; | ||
} | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports["default"] = void 0; | ||
off(name, callback) { | ||
const { _cache } = this; | ||
if (!name) { | ||
this._cache = {}; | ||
return this; | ||
} | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
if (name && callback) { | ||
_cache[name] = (_cache[name] || []).filter((cb) => cb !== callback); | ||
return this; | ||
} | ||
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); } } | ||
_cache[name] = []; | ||
return this; | ||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
var Event = /*#__PURE__*/function () { | ||
function Event() { | ||
_classCallCheck(this, Event); | ||
_defineProperty(this, "_cache", {}); | ||
} | ||
_createClass(Event, [{ | ||
key: "on", | ||
value: function on(name, callback) { | ||
var _cache = this._cache; | ||
_cache[name] = _cache[name] || []; | ||
if (callback) { | ||
_cache[name].push(callback); | ||
} else { | ||
_cache[name].forEach(function (cb) { | ||
return cb(); | ||
}); | ||
} | ||
return this; | ||
} | ||
}, { | ||
key: "off", | ||
value: function off(name, callback) { | ||
var _cache = this._cache; | ||
trigger(name, ...args) { | ||
const { _cache } = this; | ||
(_cache[name] || []).forEach((cb) => cb(...args)); | ||
if (!name) { | ||
this._cache = {}; | ||
return this; | ||
} | ||
if (name && callback) { | ||
_cache[name] = (_cache[name] || []).filter(function (cb) { | ||
return cb !== callback; | ||
}); | ||
return this; | ||
} | ||
_cache[name] = []; | ||
return this; | ||
} | ||
} | ||
}, { | ||
key: "trigger", | ||
value: function trigger(name) { | ||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | ||
args[_key - 1] = arguments[_key]; | ||
} | ||
var _cache = this._cache; | ||
(_cache[name] || []).forEach(function (cb) { | ||
return cb.apply(void 0, args); | ||
}); | ||
return this; | ||
} | ||
}]); | ||
return Event; | ||
}(); | ||
exports["default"] = Event; |
178
lib/M.js
@@ -0,1 +1,8 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports["default"] = void 0; | ||
/** | ||
@@ -6,96 +13,89 @@ * 矩阵变换计算 | ||
*/ | ||
const M = { | ||
// 矩阵 相乘计算 | ||
mm(M1, M2, LEN = 4) { | ||
const m = []; | ||
for (let i = 0; i < LEN; i++) { | ||
for (let j = 0; j < LEN; j++) { | ||
let ele = 0; | ||
for (let z = 0; z < LEN; z++) { | ||
ele += M1[i * LEN + z] * M2[j + LEN * z]; | ||
} | ||
m.push(ele); | ||
} | ||
var M = { | ||
// 矩阵 相乘计算 | ||
mm: function mm(M1, M2) { | ||
var LEN = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 4; | ||
var m = []; | ||
for (var i = 0; i < LEN; i++) { | ||
for (var j = 0; j < LEN; j++) { | ||
var ele = 0; | ||
for (var z = 0; z < LEN; z++) { | ||
ele += M1[i * LEN + z] * M2[j + LEN * z]; | ||
} | ||
return m.map((i) => +i.toFixed(4)); | ||
}, | ||
mms(...args) { | ||
return args.reduce((prev, current) => M.mm(current, prev)); | ||
}, | ||
// 旋转 | ||
rotate(R, m) { | ||
// x轴旋转 | ||
// const RM = [ | ||
// 1, 0, 0, 0, | ||
// 0, Math.cos(R), Math.sin(R), 0, | ||
// 0, -Math.sin(R), Math.cos(R), 0, | ||
// 0, 0, 0, 1, | ||
// ] | ||
// y轴旋转 | ||
// const RM = [ | ||
// Math.cos(R), 0, -Math.sin(R), 0, | ||
// Math.sin(R), 0, Math.cos(R), 0, | ||
// 0, 0, 1, 0, | ||
// 0, 0, 0, 1, | ||
// ] | ||
m.push(ele); | ||
} | ||
} | ||
// z轴旋转 | ||
// prettier-ignore | ||
const RM = [ | ||
Math.cos(R), -Math.sin(R), 0, 0, | ||
Math.sin(R), Math.cos(R), 0, 0, | ||
0, 0, 1, 0, | ||
0, 0, 0, 1 | ||
]; | ||
return m.map(function (i) { | ||
return +i.toFixed(4); | ||
}); | ||
}, | ||
mms: function mms() { | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
return M.mm(RM, m); | ||
}, | ||
// 缩放 | ||
scale(S, m) { | ||
// prettier-ignore | ||
const RM = [ | ||
S, 0, 0, 0, | ||
0, S, 0, 0, | ||
0, 0, 1, 0, | ||
0, 0, 0, 1 | ||
]; | ||
return args.reduce(function (prev, current) { | ||
return M.mm(current, prev); | ||
}); | ||
}, | ||
// 旋转 | ||
rotate: function rotate(R, m) { | ||
// x轴旋转 | ||
// const RM = [ | ||
// 1, 0, 0, 0, | ||
// 0, Math.cos(R), Math.sin(R), 0, | ||
// 0, -Math.sin(R), Math.cos(R), 0, | ||
// 0, 0, 0, 1, | ||
// ] | ||
// y轴旋转 | ||
// const RM = [ | ||
// Math.cos(R), 0, -Math.sin(R), 0, | ||
// Math.sin(R), 0, Math.cos(R), 0, | ||
// 0, 0, 1, 0, | ||
// 0, 0, 0, 1, | ||
// ] | ||
// z轴旋转 | ||
// prettier-ignore | ||
var RM = [Math.cos(R), -Math.sin(R), 0, 0, Math.sin(R), Math.cos(R), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; | ||
return M.mm(RM, m); | ||
}, | ||
// 缩放 | ||
scale: function scale(S, m) { | ||
// prettier-ignore | ||
var RM = [S, 0, 0, 0, 0, S, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; | ||
return M.mm(RM, m); | ||
}, | ||
// 位移 | ||
translate: function translate(x, y, m) { | ||
// const newM = [...m] | ||
// newM[12] += x | ||
// newM[13] += y | ||
// return newM | ||
// prettier-ignore | ||
var RM = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x / m[0], y / m[0], 0, 1]; | ||
return M.mm(RM, m); | ||
}, | ||
// 缩放位移矫正 | ||
trans: function trans(SCALE, current, origin, m) { | ||
// 在这里,我们设置transform-origin: 0 0;然后所有的计算以图片的左上角为基准,这样方便计算位置变换后的位移 | ||
// 1. 计算出current中点相对于左上角的坐标 | ||
// 2. 根据缩放比例,计算出中点坐标的相对位移 | ||
// 3. 矩阵 -> 缩放后矩阵 -> 位移矫正 | ||
var p = { | ||
x: (current.x + current.x1) / 2 - (origin.left + m[12]), | ||
y: (current.y + current.y1) / 2 - (origin.top + m[13]) | ||
}; // 计算位移 | ||
return M.mm(RM, m); | ||
}, | ||
// 位移 | ||
translate(x, y, m) { | ||
// const newM = [...m] | ||
// newM[12] += x | ||
// newM[13] += y | ||
// return newM | ||
// prettier-ignore | ||
const RM = [ | ||
1, 0, 0, 0, | ||
0, 1, 0, 0, | ||
0, 0, 1, 0, | ||
x / m[0], y / m[0], 0, 1 | ||
]; | ||
return M.mm(RM, m); | ||
}, | ||
// 缩放位移矫正 | ||
trans(SCALE, current, origin, m) { | ||
// 在这里,我们设置transform-origin: 0 0;然后所有的计算以图片的左上角为基准,这样方便计算位置变换后的位移 | ||
// 1. 计算出current中点相对于左上角的坐标 | ||
// 2. 根据缩放比例,计算出中点坐标的相对位移 | ||
// 3. 矩阵 -> 缩放后矩阵 -> 位移矫正 | ||
const p = { | ||
x: (current.x + current.x1) / 2 - (origin.left + m[12]), | ||
y: (current.y + current.y1) / 2 - (origin.top + m[13]), | ||
}; | ||
// 计算位移 | ||
const newP = { | ||
x: -p.x * (SCALE - 1), | ||
y: -p.y * (SCALE - 1), | ||
}; | ||
return M.translate(newP.x, newP.y, m); | ||
}, | ||
var newP = { | ||
x: -p.x * (SCALE - 1), | ||
y: -p.y * (SCALE - 1) | ||
}; | ||
return M.translate(newP.x, newP.y, m); | ||
} | ||
}; | ||
export default M; | ||
var _default = M; | ||
exports["default"] = _default; |
811
lib/Pinch.js
@@ -1,8 +0,58 @@ | ||
import TouchEvent from './core'; | ||
import Event from './Event'; | ||
import { TOUCH_DIRECTION, isPC } from './config'; | ||
import util from './util'; | ||
import Tap from './Tap'; | ||
import { Transform } from './BetterM'; | ||
"use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports["default"] = void 0; | ||
var _core = _interopRequireDefault(require("./core")); | ||
var _Event2 = _interopRequireDefault(require("./Event")); | ||
var _config = require("./config"); | ||
var _util = _interopRequireDefault(require("./util")); | ||
var _Tap = _interopRequireDefault(require("./Tap")); | ||
var _BetterM = require("./BetterM"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } | ||
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } | ||
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } | ||
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } | ||
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
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 _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
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; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
/** | ||
@@ -16,197 +66,305 @@ * 获取X,Y轴上的变更,对于没有scale == 1的元素,需要对滑动变更进行锁定 | ||
function getChangeXY(change, state) { | ||
if (state.scale == 1) { | ||
if (state.direction === TOUCH_DIRECTION.leftToRight) { | ||
return [change.x, 0]; | ||
} | ||
if (state.direction === TOUCH_DIRECTION.topToBottom) { | ||
return [0, change.y]; | ||
} | ||
if (state.scale == 1) { | ||
if (state.direction === _config.TOUCH_DIRECTION.leftToRight) { | ||
return [change.x, 0]; | ||
} | ||
return [change.x, change.y]; | ||
if (state.direction === _config.TOUCH_DIRECTION.topToBottom) { | ||
return [0, change.y]; | ||
} | ||
} | ||
return [change.x, change.y]; | ||
} | ||
export default class Pinch extends Event { | ||
// 被缩放对象是否需要加载 | ||
isLoad = false; // 是否可以立即开启 | ||
var Pinch = /*#__PURE__*/function (_Event) { | ||
_inherits(Pinch, _Event); | ||
constructor( | ||
$eventDom, // 事件监听元素 | ||
$transformDom = $eventDom, // 被缩放元素 | ||
/** @type {{maxScale: number, minScale: number, canPinch: boolean, toggleScale: number, $borderCheckDom: HTMLElement, disableBorderCheck: boolean }} */ | ||
option = {} | ||
) { | ||
super(); | ||
const { | ||
$borderCheckDom = $eventDom, // 边界校验元素 | ||
canPinch = true, // 是否可缩放 | ||
maxScale = 1, | ||
minScale = 1, | ||
} = option; | ||
this.option = option; | ||
this.canPinch = canPinch; | ||
this.maxScale = maxScale; | ||
this.minScale = minScale; | ||
$transformDom.style.cssText += ';transform-origin: left top; will-change: transform;'; | ||
var _super = _createSuper(Pinch); | ||
this.$eventDom = $eventDom; | ||
this.$transformDom = $transformDom; | ||
this.$borderCheckDom = $borderCheckDom; | ||
// 被缩放对象是否需要加载 | ||
// 是否可以立即开启 | ||
function Pinch(_$eventDom) { | ||
var _this; | ||
// 需要处理是否是图片,是否需要加载的问题 | ||
if ($transformDom.tagName.toLowerCase() === 'img') { | ||
this.updateInfo($transformDom); | ||
$transformDom.$$pinch = this; | ||
} else { | ||
this.isLoad = true; | ||
} | ||
this.origin = util.getOrigin(this.$transformDom); | ||
// 未加载,允许左右移动,不允许缩放图片 | ||
this.baseOrigin = util.getOrigin(this.$borderCheckDom); | ||
this.addEvents(); | ||
var _$transformDom = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _$eventDom; | ||
// 矩阵变换逻辑在此 | ||
this.trans = new Transform(); | ||
var option = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
// 页面resize时,重置baseinfo | ||
if (isPC) { | ||
window.addEventListener('resize', this.setBaseInfo); | ||
this.removeResize = window.removeEventListener('resize', this.setBaseInfo); | ||
} | ||
_classCallCheck(this, Pinch); | ||
_this = _super.call(this); | ||
_defineProperty(_assertThisInitialized(_this), "isLoad", false); | ||
_defineProperty(_assertThisInitialized(_this), "setBaseInfo", function () { | ||
var $img = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.$transformDom; | ||
_this.origin = _util["default"].getOrigin(_this.$transformDom); | ||
_this.baseOrigin = _util["default"].getOrigin(_this.$borderCheckDom); | ||
_this.naturalWidth = $img.naturalWidth; | ||
_this.maxScale = Math.max(_this.naturalWidth / _this.origin.width, _this.maxScale); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "updateInfo", function () { | ||
var $img = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.$transformDom; | ||
var load = function load() { | ||
$img.removeEventListener('load', load); | ||
if (_this.isDestroy) return; // 图片加载成功,不代表一定可以获取到图片的rect信息, | ||
// 因此这里做个延迟判断,足以覆盖大多数情况,从严谨的角度来讲,可以直接根据img.naturalWidth/Height算出来 | ||
_this.timeout = setTimeout(function () { | ||
_this.setBaseInfo(); | ||
_this.isLoad = true; | ||
}, 100); | ||
}; | ||
$img.addEventListener('load', load); | ||
$img.src = $img.src || $img.originSrc; | ||
if ($img.complete || $img.width || $img.height) { | ||
load(); | ||
} | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "timeout", null); | ||
_defineProperty(_assertThisInitialized(_this), "animation", function () { | ||
var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.4; | ||
var fn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; | ||
_this.touch.state.scale = _this.trans.getScale(); | ||
var _assertThisInitialize = _assertThisInitialized(_this), | ||
$transformDom = _assertThisInitialize.$transformDom, | ||
$eventDom = _assertThisInitialize.$eventDom; // 也可以使用js动画来控制 | ||
_util["default"].transition([$transformDom], time); | ||
$transformDom.clientHeight; | ||
$transformDom.style.cssText += _this.trans.toCss(); | ||
clearTimeout(_this.timeout); | ||
var animationEnd = function animationEnd() { | ||
clearTimeout(_this.timeout); | ||
_util["default"].transition([$transformDom, $eventDom]); | ||
fn && fn(); | ||
}; | ||
_this.animationEnd = animationEnd; | ||
_this.timeout = setTimeout(animationEnd, time * 1000); | ||
return _assertThisInitialized(_this); | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "toggle", function (center) { | ||
if (!_this.isLoad) { | ||
return; | ||
} | ||
var _assertThisInitialize2 = _assertThisInitialized(_this), | ||
minScale = _assertThisInitialize2.minScale, | ||
origin = _assertThisInitialize2.origin; | ||
var initS = _this.trans.getScale(); | ||
if (initS > minScale) { | ||
// 此处可选择恢复初始状态或者缩放回原始比例 | ||
// this.trans.scale(minScale / initS, center, origin) | ||
_this.trans.reset(); | ||
} else { | ||
var _assertThisInitialize3 = _assertThisInitialized(_this), | ||
maxScale = _assertThisInitialize3.maxScale; | ||
var _this$option$toggleSc = _this.option.toggleScale, | ||
toggleScale = _this$option$toggleSc === void 0 ? 1.5 : _this$option$toggleSc; // 双击默认比例 | ||
var dbClickScale = Math.min(maxScale, toggleScale); | ||
_this.trans.scale(dbClickScale / initS, center, origin); | ||
} | ||
_this.borderCheck(_this.origin, _this.baseOrigin); | ||
_this.animation(); | ||
}); | ||
var _option$$borderCheckD = option.$borderCheckDom, | ||
$borderCheckDom = _option$$borderCheckD === void 0 ? _$eventDom : _option$$borderCheckD, | ||
_option$canPinch = option.canPinch, | ||
canPinch = _option$canPinch === void 0 ? true : _option$canPinch, | ||
_option$maxScale = option.maxScale, | ||
_maxScale = _option$maxScale === void 0 ? 1 : _option$maxScale, | ||
_option$minScale = option.minScale, | ||
_minScale = _option$minScale === void 0 ? 1 : _option$minScale; | ||
_this.option = option; | ||
_this.canPinch = canPinch; | ||
_this.maxScale = _maxScale; | ||
_this.minScale = _minScale; | ||
_$transformDom.style.cssText += ';transform-origin: left top; will-change: transform;'; | ||
_this.$eventDom = _$eventDom; | ||
_this.$transformDom = _$transformDom; | ||
_this.$borderCheckDom = $borderCheckDom; // 需要处理是否是图片,是否需要加载的问题 | ||
if (_$transformDom.tagName.toLowerCase() === 'img') { | ||
_this.updateInfo(_$transformDom); | ||
_$transformDom.$$pinch = _assertThisInitialized(_this); | ||
} else { | ||
_this.isLoad = true; | ||
} | ||
setBaseInfo = ($img = this.$transformDom) => { | ||
this.origin = util.getOrigin(this.$transformDom); | ||
this.baseOrigin = util.getOrigin(this.$borderCheckDom); | ||
this.naturalWidth = $img.naturalWidth; | ||
this.maxScale = Math.max(this.naturalWidth / this.origin.width, this.maxScale); | ||
}; | ||
_this.origin = _util["default"].getOrigin(_this.$transformDom); // 未加载,允许左右移动,不允许缩放图片 | ||
// 获取图片信息 | ||
updateInfo = ($img = this.$transformDom) => { | ||
const load = () => { | ||
$img.removeEventListener('load', load); | ||
if (this.isDestroy) return; | ||
// 图片加载成功,不代表一定可以获取到图片的rect信息, | ||
// 因此这里做个延迟判断,足以覆盖大多数情况,从严谨的角度来讲,可以直接根据img.naturalWidth/Height算出来 | ||
this.timeout = setTimeout(() => { | ||
this.setBaseInfo(); | ||
this.isLoad = true; | ||
}, 100); | ||
}; | ||
$img.addEventListener('load', load); | ||
$img.src = $img.src || $img.originSrc; | ||
if ($img.complete || $img.width || $img.height) { | ||
load(); | ||
_this.baseOrigin = _util["default"].getOrigin(_this.$borderCheckDom); | ||
_this.addEvents(); // 矩阵变换逻辑在此 | ||
_this.trans = new _BetterM.Transform(); // 页面resize时,重置baseinfo | ||
if (_config.isPC) { | ||
window.addEventListener('resize', _this.setBaseInfo); | ||
_this.removeResize = window.removeEventListener('resize', _this.setBaseInfo); | ||
} | ||
return _this; | ||
} | ||
_createClass(Pinch, [{ | ||
key: "addEvents", | ||
value: function addEvents() { | ||
var _this2 = this; | ||
var baseM = []; | ||
this.touch = new _core["default"](this.$eventDom, { | ||
finger: 2, | ||
preventDefault: true, | ||
XYWeight: 2 // x 相对于 y 的权重比例 | ||
}) // onStart之前执行 | ||
.on('nativeStart', function () { | ||
_this2.trigger('nativeStart'); // 立即终结动画 | ||
_this2.animationEnd && _this2.animationEnd(); | ||
}).on('startv2', function () { | ||
_this2.trigger('start'); | ||
baseM = _this2.trans.snap(); | ||
}).on('changev2', function (_ref) { | ||
var state = _ref.state; | ||
var change = state.change; | ||
if (state.finger >= 2) { | ||
if (_this2.isLoad && _this2.canPinch) { | ||
_this2.trans.scale(state.scale, state.move, _this2.origin, baseM); | ||
} | ||
} else { | ||
var _getChangeXY = getChangeXY(change, state), | ||
_getChangeXY2 = _slicedToArray(_getChangeXY, 2), | ||
changeX = _getChangeXY2[0], | ||
changeY = _getChangeXY2[1]; // 单指拖拽 | ||
_this2.trans.translate(changeX, changeY, baseM); | ||
var result = _this2.borderCheck(_this2.origin, _this2.baseOrigin); // if (state.finger == 1 && result.isOver) { | ||
// baseM = this.trans.snap() | ||
// // = result.m; | ||
// } | ||
// // 如果元素可以内部滚动,那么不应当可以随意触发change事件 | ||
// // 应当在用户有意识的触发change事件时,才触发。避免弹窗关闭的过于敏感 | ||
// // 目前的应用场景主要在图片预览组件 | ||
if (state.finger == 1 && result.isBigger && !result.isOver) { | ||
state.dontTriggerChange = true; | ||
} | ||
if (result.isOver && !state.dontTriggerChange) { | ||
_this2.trigger('change', result, state); // 触发变化 | ||
} | ||
} | ||
}; | ||
addEvents() { | ||
let baseM = []; | ||
this.touch = new TouchEvent(this.$eventDom, { | ||
finger: 2, | ||
preventDefault: true, | ||
XYWeight: 2, // x 相对于 y 的权重比例 | ||
}) | ||
// onStart之前执行 | ||
.on('nativeStart', () => { | ||
this.trigger('nativeStart'); | ||
// 立即终结动画 | ||
this.animationEnd && this.animationEnd(); | ||
}) | ||
.on('startv2', () => { | ||
this.trigger('start'); | ||
baseM = this.trans.snap(); | ||
}) | ||
.on('changev2', ({ state }) => { | ||
const { change } = state; | ||
if (state.finger >= 2) { | ||
if (this.isLoad && this.canPinch) { | ||
this.trans.scale(state.scale, state.move, this.origin, baseM); | ||
} | ||
} else { | ||
const [changeX, changeY] = getChangeXY(change, state); | ||
// 单指拖拽 | ||
this.trans.translate(changeX, changeY, baseM); | ||
const result = this.borderCheck(this.origin, this.baseOrigin); | ||
// if (state.finger == 1 && result.isOver) { | ||
// baseM = this.trans.snap() | ||
// // = result.m; | ||
// } | ||
_util["default"].RAF(function () { | ||
// 触摸点大于2 | ||
_this2.$transformDom.style.cssText += _this2.trans.toCss(); | ||
}); | ||
}).on('endv2', function (_ref2) { | ||
var done = _ref2.done, | ||
state = _ref2.state; | ||
var change = state.change; | ||
// // 如果元素可以内部滚动,那么不应当可以随意触发change事件 | ||
// // 应当在用户有意识的触发change事件时,才触发。避免弹窗关闭的过于敏感 | ||
// // 目前的应用场景主要在图片预览组件 | ||
if (state.finger == 1 && result.isBigger && !result.isOver) { | ||
state.dontTriggerChange = true; | ||
} | ||
_util["default"].RAF(function () { | ||
done(); | ||
if (!state.isMove) return; | ||
if (result.isOver && !state.dontTriggerChange) { | ||
this.trigger('change', result, state); // 触发变化 | ||
} | ||
} | ||
util.RAF(() => { | ||
// 触摸点大于2 | ||
this.$transformDom.style.cssText += this.trans.toCss(); | ||
}); | ||
}) | ||
.on('endv2', ({ done, state }) => { | ||
const { change } = state; | ||
util.RAF(() => { | ||
done(); | ||
if (!state.isMove) return; | ||
if (state.finger >= 2) { | ||
if (this.isLoad && this.canPinch && state.isMove) { | ||
// 最大缩放比例校验 | ||
if (this.trans.getScale() > this.maxScale) { | ||
const PER = this.maxScale / this.trans.getScale(); | ||
this.trans.scale(PER, state.move, this.origin); | ||
} | ||
} else { | ||
return; | ||
} | ||
} else { | ||
const K = 20000; // 滑动系数 | ||
const speed = state.VCache.reduce( | ||
(prev, current) => { | ||
prev.x += current.x; | ||
prev.y += current.y; | ||
return prev; | ||
}, | ||
{ x: 0, y: 0 } | ||
); | ||
let [finalVX, finalVY] = state.VCache.length | ||
? [(speed.x * K) / state.VCache.length, (speed.y * K) / state.VCache.length] | ||
: [0, 0]; | ||
if (state.finger >= 2) { | ||
if (_this2.isLoad && _this2.canPinch && state.isMove) { | ||
// 最大缩放比例校验 | ||
if (_this2.trans.getScale() > _this2.maxScale) { | ||
var PER = _this2.maxScale / _this2.trans.getScale(); | ||
// 避免抖动 | ||
if (Math.abs(finalVX) < 20 && Math.abs(finalVY) < 20) { | ||
finalVX = 0; | ||
finalVY = 0; | ||
} | ||
const [changeX, changeY] = getChangeXY( | ||
{ | ||
x: change.x + finalVX, | ||
y: change.y + finalVY, | ||
}, | ||
state | ||
); | ||
// 单指拖拽 | ||
this.trans.translate(changeX, changeY, baseM); | ||
} | ||
_this2.trans.scale(PER, state.move, _this2.origin); | ||
} | ||
} else { | ||
return; | ||
} | ||
} else { | ||
var K = 20000; // 滑动系数 | ||
// 边界校验 | ||
const result = this.borderCheck(this.origin, this.baseOrigin, { minScale: this.minScale }); | ||
const ANIMATION_TIME = state.nativeFingers >= state.finger ? 0 : 0.4; | ||
// 只有移动才有触发动画的必要【溢出,或者缩小了】 | ||
this.animation(ANIMATION_TIME); | ||
var speed = state.VCache.reduce(function (prev, current) { | ||
prev.x += current.x; | ||
prev.y += current.y; | ||
return prev; | ||
}, { | ||
x: 0, | ||
y: 0 | ||
}); | ||
// 单指触发才有回调的意义 | ||
if (state.finger == 1 && result.isOver && !state.dontTriggerChange) { | ||
this.trigger('end', result, state, this); | ||
} | ||
}); | ||
}); | ||
this.canPinch && this.enablePinch(); | ||
var _ref3 = state.VCache.length ? [speed.x * K / state.VCache.length, speed.y * K / state.VCache.length] : [0, 0], | ||
_ref4 = _slicedToArray(_ref3, 2), | ||
finalVX = _ref4[0], | ||
finalVY = _ref4[1]; // 避免抖动 | ||
if (Math.abs(finalVX) < 20 && Math.abs(finalVY) < 20) { | ||
finalVX = 0; | ||
finalVY = 0; | ||
} | ||
var _getChangeXY3 = getChangeXY({ | ||
x: change.x + finalVX, | ||
y: change.y + finalVY | ||
}, state), | ||
_getChangeXY4 = _slicedToArray(_getChangeXY3, 2), | ||
changeX = _getChangeXY4[0], | ||
changeY = _getChangeXY4[1]; // 单指拖拽 | ||
_this2.trans.translate(changeX, changeY, baseM); | ||
} // 边界校验 | ||
var result = _this2.borderCheck(_this2.origin, _this2.baseOrigin, { | ||
minScale: _this2.minScale | ||
}); | ||
var ANIMATION_TIME = state.nativeFingers >= state.finger ? 0 : 0.4; // 只有移动才有触发动画的必要【溢出,或者缩小了】 | ||
_this2.animation(ANIMATION_TIME); // 单指触发才有回调的意义 | ||
if (state.finger == 1 && result.isOver && !state.dontTriggerChange) { | ||
_this2.trigger('end', result, state, _this2); | ||
} | ||
}); | ||
}); | ||
this.canPinch && this.enablePinch(); | ||
} | ||
/** | ||
@@ -220,123 +378,125 @@ * 边界校验 | ||
*/ | ||
borderCheck(o = {}, b = {}, { minScale = 1, maxScale = Infinity } = {}) { | ||
if (this.option.disableBorderCheck) { | ||
return { | ||
x: 0, // X轴溢出距离 | ||
y: 0, // Y轴溢出距离 | ||
isOver: false, // 是否溢出 | ||
isBigger: false, // 被缩放对象是否大于边界尺寸 | ||
}; | ||
} | ||
let S = this.trans.getScale(); | ||
// 处理旋转带来的宽高交换 | ||
// let rotate = this.trans.state.rotate | ||
// if (Math.floor(Math.PI / rotate + 1) % 2 == 0) { | ||
}, { | ||
key: "borderCheck", | ||
value: function borderCheck() { | ||
var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
// } | ||
var _ref5 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, | ||
_ref5$minScale = _ref5.minScale, | ||
minScale = _ref5$minScale === void 0 ? 1 : _ref5$minScale, | ||
_ref5$maxScale = _ref5.maxScale, | ||
maxScale = _ref5$maxScale === void 0 ? Infinity : _ref5$maxScale; | ||
// 如果缩放倍数小于1,还原到1 | ||
const defaultCenter = { | ||
x: o.width / 2, | ||
x1: o.width / 2, | ||
y: o.height / 2, | ||
y1: o.height / 2, | ||
if (this.option.disableBorderCheck) { | ||
return { | ||
x: 0, | ||
// X轴溢出距离 | ||
y: 0, | ||
// Y轴溢出距离 | ||
isOver: false, | ||
// 是否溢出 | ||
isBigger: false // 被缩放对象是否大于边界尺寸 | ||
}; | ||
if (S < minScale) { | ||
this.trans.scale(minScale / S, defaultCenter, o); | ||
} else if (S > maxScale) { | ||
this.trans.scale(maxScale / S, defaultCenter, o); | ||
} | ||
} | ||
S = this.trans.getScale(); | ||
var S = this.trans.getScale(); // 处理旋转带来的宽高交换 | ||
// let rotate = this.trans.state.rotate | ||
// if (Math.floor(Math.PI / rotate + 1) % 2 == 0) { | ||
// } | ||
// 如果缩放倍数小于1,还原到1 | ||
let maxTLR = 0; | ||
let minTLR = 0; | ||
let maxTUD = 0; | ||
let minTUD = 0; | ||
var defaultCenter = { | ||
x: o.width / 2, | ||
x1: o.width / 2, | ||
y: o.height / 2, | ||
y1: o.height / 2 | ||
}; | ||
// 缩放后大于基础校验宽度 | ||
if (o.width * S > b.width) { | ||
maxTLR = b.left - o.left; // 因为transform-origin为left | ||
minTLR = maxTLR - (o.width * S - b.width); // 宽度差 | ||
} else { | ||
maxTLR = minTLR = ((1 - S) * o.width) / 2; // 反则就以缩放元素的中心缩放 | ||
} | ||
if (S < minScale) { | ||
this.trans.scale(minScale / S, defaultCenter, o); | ||
} else if (S > maxScale) { | ||
this.trans.scale(maxScale / S, defaultCenter, o); | ||
} | ||
if (o.height * S > b.height) { | ||
maxTUD = b.top - o.top; | ||
minTUD = maxTUD - (o.height * S - b.height); | ||
} else { | ||
maxTUD = minTUD = ((1 - S) * o.height) / 2; // 反则就以缩放元素的中心缩放 | ||
} | ||
S = this.trans.getScale(); | ||
var maxTLR = 0; | ||
var minTLR = 0; | ||
var maxTUD = 0; | ||
var minTUD = 0; // 缩放后大于基础校验宽度 | ||
// 获取最大,最小位移 | ||
const [x, y] = this.trans.getTranslate(); | ||
const newX = Math.max(minTLR, Math.min(maxTLR, x)); | ||
const newY = Math.max(minTUD, Math.min(maxTUD, y)); | ||
if (o.width * S > b.width) { | ||
maxTLR = b.left - o.left; // 因为transform-origin为left | ||
// newM为校正后的矩阵, | ||
const X = newX - x; | ||
const Y = newY - y; | ||
minTLR = maxTLR - (o.width * S - b.width); // 宽度差 | ||
} else { | ||
maxTLR = minTLR = (1 - S) * o.width / 2; // 反则就以缩放元素的中心缩放 | ||
} | ||
this.trans.translate(X, Y); | ||
return { | ||
x: -X, // X轴溢出距离 | ||
y: -Y, // Y轴溢出距离 | ||
isOver: X !== 0 || Y !== 0, // 是否溢出 | ||
isBigger: o.width * S > b.width || o.height * S > b.height, // 被缩放对象是否大于边界尺寸 | ||
}; | ||
} | ||
if (o.height * S > b.height) { | ||
maxTUD = b.top - o.top; | ||
minTUD = maxTUD - (o.height * S - b.height); | ||
} else { | ||
maxTUD = minTUD = (1 - S) * o.height / 2; // 反则就以缩放元素的中心缩放 | ||
} // 获取最大,最小位移 | ||
disablePinch() { | ||
this.canPinch = false; | ||
this.tap && this.tap.destroy(); | ||
} | ||
enablePinch() { | ||
this.canPinch = true; | ||
this.tap && this.tap.destroy(); | ||
// 双击放大 | ||
this.tap = new Tap(this.$eventDom, 600) | ||
.on(2, (event) => { | ||
const t = event.changedTouches ? event.changedTouches[0] : event; | ||
const center = { | ||
x: t.clientX, | ||
y: t.clientY, | ||
x1: t.clientX, | ||
y1: t.clientY, | ||
}; | ||
this.toggle(center); | ||
}) | ||
.on(1, () => { | ||
this.trans.getScale() == 1 && this.trigger('close'); | ||
}); | ||
var _this$trans$getTransl = this.trans.getTranslate(), | ||
_this$trans$getTransl2 = _slicedToArray(_this$trans$getTransl, 2), | ||
x = _this$trans$getTransl2[0], | ||
y = _this$trans$getTransl2[1]; | ||
var newX = Math.max(minTLR, Math.min(maxTLR, x)); | ||
var newY = Math.max(minTUD, Math.min(maxTUD, y)); // newM为校正后的矩阵, | ||
var X = newX - x; | ||
var Y = newY - y; | ||
this.trans.translate(X, Y); | ||
return { | ||
x: -X, | ||
// X轴溢出距离 | ||
y: -Y, | ||
// Y轴溢出距离 | ||
isOver: X !== 0 || Y !== 0, | ||
// 是否溢出 | ||
isBigger: o.width * S > b.width || o.height * S > b.height // 被缩放对象是否大于边界尺寸 | ||
}; | ||
} | ||
}, { | ||
key: "disablePinch", | ||
value: function disablePinch() { | ||
this.canPinch = false; | ||
this.tap && this.tap.destroy(); | ||
} | ||
}, { | ||
key: "enablePinch", | ||
value: function enablePinch() { | ||
var _this3 = this; | ||
timeout = null; | ||
this.canPinch = true; | ||
this.tap && this.tap.destroy(); // 双击放大 | ||
// 执行动画 | ||
animation = (time = 0.4, fn = () => {}) => { | ||
this.touch.state.scale = this.trans.getScale(); | ||
const { $transformDom, $eventDom } = this; | ||
// 也可以使用js动画来控制 | ||
util.transition([$transformDom], time); | ||
$transformDom.clientHeight; | ||
$transformDom.style.cssText += this.trans.toCss(); | ||
clearTimeout(this.timeout); | ||
const animationEnd = () => { | ||
clearTimeout(this.timeout); | ||
util.transition([$transformDom, $eventDom]); | ||
fn && fn(); | ||
this.tap = new _Tap["default"](this.$eventDom, 600).on(2, function (event) { | ||
var t = event.changedTouches ? event.changedTouches[0] : event; | ||
var center = { | ||
x: t.clientX, | ||
y: t.clientY, | ||
x1: t.clientX, | ||
y1: t.clientY | ||
}; | ||
this.animationEnd = animationEnd; | ||
this.timeout = setTimeout(animationEnd, time * 1000); | ||
return this; | ||
}; | ||
rotate(R) { | ||
this.trans.rotate(R, this.origin); | ||
this.animation(); | ||
_this3.toggle(center); | ||
}).on(1, function () { | ||
_this3.trans.getScale() == 1 && _this3.trigger('close'); | ||
}); | ||
} | ||
}, { | ||
key: "rotate", | ||
value: function rotate(R) { | ||
this.trans.rotate(R, this.origin); | ||
this.animation(); | ||
} | ||
/** | ||
@@ -347,47 +507,50 @@ * 放大缩小切换 | ||
*/ | ||
toggle = (center) => { | ||
if (!this.isLoad) { | ||
return; | ||
} | ||
const { minScale, origin } = this; | ||
const initS = this.trans.getScale(); | ||
if (initS > minScale) { | ||
// 此处可选择恢复初始状态或者缩放回原始比例 | ||
// this.trans.scale(minScale / initS, center, origin) | ||
this.trans.reset(); | ||
} else { | ||
const { maxScale } = this; | ||
const { toggleScale = 1.5 } = this.option; // 双击默认比例 | ||
const dbClickScale = Math.min(maxScale, toggleScale); | ||
this.trans.scale(dbClickScale / initS, center, origin); | ||
} | ||
this.borderCheck(this.origin, this.baseOrigin); | ||
this.animation(); | ||
}; | ||
// 销毁 | ||
destroy() { | ||
[this.tap, this.touch].filter((i) => i).forEach((i) => i.destroy()); | ||
this.isDestroy = true; | ||
if (this.$transformDom) { | ||
this.$transformDom.style.cssText += ';transform: none;'; | ||
} | ||
}, { | ||
key: "destroy", | ||
value: // 销毁 | ||
function destroy() { | ||
[this.tap, this.touch].filter(function (i) { | ||
return i; | ||
}).forEach(function (i) { | ||
return i.destroy(); | ||
}); | ||
this.isDestroy = true; | ||
if (this.removeResize) { | ||
this.removeResize(); | ||
} | ||
if (this.$transformDom) { | ||
this.$transformDom.style.cssText += ';transform: none;'; | ||
} | ||
return this; | ||
} | ||
if (this.removeResize) { | ||
this.removeResize(); | ||
} | ||
// 开启 | ||
enable() { | ||
[this.tap, this.touch].filter((i) => i).forEach((i) => i.enable()); | ||
return this; | ||
return this; | ||
} // 开启 | ||
}, { | ||
key: "enable", | ||
value: function enable() { | ||
[this.tap, this.touch].filter(function (i) { | ||
return i; | ||
}).forEach(function (i) { | ||
return i.enable(); | ||
}); | ||
return this; | ||
} | ||
}, { | ||
key: "disable", | ||
value: function disable() { | ||
[this.tap, this.touch].filter(function (i) { | ||
return i; | ||
}).forEach(function (i) { | ||
return i.disable(); | ||
}); | ||
return this; | ||
} | ||
}]); | ||
disable() { | ||
[this.tap, this.touch].filter((i) => i).forEach((i) => i.disable()); | ||
return this; | ||
} | ||
} | ||
return Pinch; | ||
}(_Event2["default"]); | ||
exports["default"] = Pinch; |
298
lib/Tap.js
@@ -1,9 +0,53 @@ | ||
import Event from './Event'; | ||
"use strict"; | ||
import { touchstart, touchmove, touchend } from './config'; | ||
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports["default"] = void 0; | ||
var _Event2 = _interopRequireDefault(require("./Event")); | ||
var _config = require("./config"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } | ||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } | ||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } | ||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } | ||
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 } }); Object.defineProperty(subClass, "prototype", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); } | ||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } | ||
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 _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } | ||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } | ||
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; } } | ||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
function getTouch(event) { | ||
return event.touches ? event.touches[0] : event; | ||
return event.touches ? event.touches[0] : event; | ||
} | ||
/** | ||
@@ -15,121 +59,167 @@ * 监听双击/点击/长按事件 | ||
*/ | ||
class Tap extends Event { | ||
constructor($ele, TIME = 300, longTime = TIME) { | ||
super(); | ||
this.$ele = $ele; | ||
this.TIME = TIME; | ||
this.longTime = Math.max(longTime, TIME); | ||
this.reset(); | ||
this._addEvents(); | ||
} | ||
reset() { | ||
this.state = { | ||
clickRecord: [], | ||
recording: false, | ||
first: false, | ||
}; | ||
} | ||
_touchstart = (event) => { | ||
clearTimeout(this._timeout); | ||
const { state } = this; | ||
const touch = getTouch(event); | ||
state.clickRecord.push({ | ||
start: Date.now(), | ||
x: touch.clientX, | ||
y: touch.clientY, | ||
}); | ||
state.recording = true; | ||
}; | ||
var Tap = /*#__PURE__*/function (_Event) { | ||
_inherits(Tap, _Event); | ||
_touchmove = () => { | ||
this.state.recording = false; | ||
}; | ||
var _super = _createSuper(Tap); | ||
_touchend = (event) => { | ||
const { state, TIME } = this; | ||
// 得到连续点击次数,判断有没有比当前连续点击次数更大的事件监听,如果有就setTimeout,没有立即执行 | ||
// 在下次touchstart的时候,清除所有setTimeout,因为按理说你已经执行了 | ||
if (state.recording) { | ||
const lastItem = state.clickRecord[state.clickRecord.length - 1]; | ||
const endTime = Date.now(); | ||
lastItem.end = endTime; | ||
// 处理长按事件 | ||
if (lastItem.end - lastItem.start >= this.longTime) { | ||
this.trigger('long', event); | ||
this.reset(); | ||
return; | ||
} | ||
function Tap($ele) { | ||
var _this; | ||
const clickedTimes = state.clickRecord.length; | ||
var _TIME = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 300; | ||
// 判断点击位置是否稳定 | ||
if (state.clickRecord.length > 1) { | ||
const isPositionStable = state.clickRecord.slice(0, -1).every((item, index) => { | ||
const next = state.clickRecord[index + 1]; | ||
return Math.abs(item.x - next.x) < 10 && Math.abs(item.y - next.y) < 10; | ||
}); | ||
if (!isPositionStable) { | ||
this.reset(); | ||
return; | ||
} | ||
} | ||
var longTime = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _TIME; | ||
// 有一个问题,如果click dbclick,那么dbclick也会触发两次click | ||
// 这不是我们想要的,因此我们拿到监听click事件做次数 | ||
// 过滤long也就是非数字key | ||
const keys = Object.keys(this._cache) | ||
.filter((i) => /\d+/.test(i)) | ||
.map((i) => +i); | ||
const maxListenClickNums = Math.max(...keys); | ||
_classCallCheck(this, Tap); | ||
if (clickedTimes === maxListenClickNums) { | ||
this.trigger(clickedTimes, event); | ||
this.reset(); | ||
} else { | ||
this._timeout = setTimeout(() => { | ||
this.trigger(clickedTimes, event); | ||
this.reset(); | ||
}, TIME); | ||
} | ||
_this = _super.call(this); | ||
_defineProperty(_assertThisInitialized(_this), "_touchstart", function (event) { | ||
clearTimeout(_this._timeout); | ||
var _assertThisInitialize = _assertThisInitialized(_this), | ||
state = _assertThisInitialize.state; | ||
var touch = getTouch(event); | ||
state.clickRecord.push({ | ||
start: Date.now(), | ||
x: touch.clientX, | ||
y: touch.clientY | ||
}); | ||
state.recording = true; | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "_touchmove", function () { | ||
_this.state.recording = false; | ||
}); | ||
_defineProperty(_assertThisInitialized(_this), "_touchend", function (event) { | ||
var _assertThisInitialize2 = _assertThisInitialized(_this), | ||
state = _assertThisInitialize2.state, | ||
TIME = _assertThisInitialize2.TIME; // 得到连续点击次数,判断有没有比当前连续点击次数更大的事件监听,如果有就setTimeout,没有立即执行 | ||
// 在下次touchstart的时候,清除所有setTimeout,因为按理说你已经执行了 | ||
if (state.recording) { | ||
var lastItem = state.clickRecord[state.clickRecord.length - 1]; | ||
var endTime = Date.now(); | ||
lastItem.end = endTime; // 处理长按事件 | ||
if (lastItem.end - lastItem.start >= _this.longTime) { | ||
_this.trigger('long', event); | ||
_this.reset(); | ||
return; | ||
} | ||
var clickedTimes = state.clickRecord.length; // 判断点击位置是否稳定 | ||
if (state.clickRecord.length > 1) { | ||
var isPositionStable = state.clickRecord.slice(0, -1).every(function (item, index) { | ||
var next = state.clickRecord[index + 1]; | ||
return Math.abs(item.x - next.x) < 10 && Math.abs(item.y - next.y) < 10; | ||
}); | ||
if (!isPositionStable) { | ||
_this.reset(); | ||
return; | ||
} | ||
} // 有一个问题,如果click dbclick,那么dbclick也会触发两次click | ||
// 这不是我们想要的,因此我们拿到监听click事件做次数 | ||
// 过滤long也就是非数字key | ||
var keys = Object.keys(_this._cache).filter(function (i) { | ||
return /\d+/.test(i); | ||
}).map(function (i) { | ||
return +i; | ||
}); | ||
var maxListenClickNums = Math.max.apply(Math, _toConsumableArray(keys)); | ||
if (clickedTimes === maxListenClickNums) { | ||
_this.trigger(clickedTimes, event); | ||
_this.reset(); | ||
} else { | ||
this.reset(); | ||
_this._timeout = setTimeout(function () { | ||
_this.trigger(clickedTimes, event); | ||
_this.reset(); | ||
}, TIME); | ||
} | ||
}; | ||
} else { | ||
_this.reset(); | ||
} | ||
}); | ||
_addEvents() { | ||
if (this.isDestroy) return; | ||
const { $ele } = this; | ||
$ele.addEventListener(touchstart, this._touchstart); | ||
$ele.addEventListener(touchmove, this._touchmove); | ||
$ele.addEventListener(touchend, this._touchend); | ||
} | ||
_this.$ele = $ele; | ||
_this.TIME = _TIME; | ||
_this.longTime = Math.max(longTime, _TIME); | ||
_removeEvents() { | ||
const { $ele } = this; | ||
$ele.removeEventListener(touchstart, this._touchstart); | ||
$ele.removeEventListener(touchmove, this._touchmove); | ||
$ele.removeEventListener(touchend, this._touchend); | ||
_this.reset(); | ||
_this._addEvents(); | ||
return _this; | ||
} | ||
_createClass(Tap, [{ | ||
key: "reset", | ||
value: function reset() { | ||
this.state = { | ||
clickRecord: [], | ||
recording: false, | ||
first: false | ||
}; | ||
} | ||
}, { | ||
key: "_addEvents", | ||
value: function _addEvents() { | ||
if (this.isDestroy) return; | ||
var $ele = this.$ele; | ||
$ele.addEventListener(_config.touchstart, this._touchstart); | ||
$ele.addEventListener(_config.touchmove, this._touchmove); | ||
$ele.addEventListener(_config.touchend, this._touchend); | ||
} | ||
}, { | ||
key: "_removeEvents", | ||
value: function _removeEvents() { | ||
var $ele = this.$ele; | ||
$ele.removeEventListener(_config.touchstart, this._touchstart); | ||
$ele.removeEventListener(_config.touchmove, this._touchmove); | ||
$ele.removeEventListener(_config.touchend, this._touchend); | ||
} | ||
}, { | ||
key: "enable", | ||
value: function enable() { | ||
if (this._isDisable) { | ||
this._isDisable = false; | ||
enable() { | ||
if (this._isDisable) { | ||
this._isDisable = false; | ||
this._addEvents(); | ||
} | ||
this._addEvents(); | ||
} | ||
} | ||
}, { | ||
key: "disable", | ||
value: function disable() { | ||
this._isDisable = true; | ||
disable() { | ||
this._isDisable = true; | ||
this._removeEvents(); | ||
this._removeEvents(); | ||
} | ||
}, { | ||
key: "destroy", | ||
value: function destroy() { | ||
this.isDestroy = true; | ||
this.off(); | ||
destroy() { | ||
this.isDestroy = true; | ||
this.off(); | ||
this._removeEvents(); | ||
this._removeEvents(); | ||
} | ||
} | ||
}]); | ||
export default Tap; | ||
return Tap; | ||
}(_Event2["default"]); | ||
var _default = Tap; | ||
exports["default"] = _default; |
349
lib/util.js
@@ -0,144 +1,227 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports["default"] = void 0; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
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); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } | ||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
// 小工具 | ||
export default { | ||
classname(...anys) { | ||
const classes = []; | ||
const getType = (any) => | ||
Object.prototype.toString | ||
.call(any) | ||
.match(/([^\s]+)]$/)[1] | ||
.toLowerCase(); | ||
const handle = (any) => { | ||
switch (getType(any)) { | ||
case 'string': | ||
case 'number': | ||
classes.push(any); | ||
break; | ||
case 'array': | ||
any.forEach(handle); | ||
break; | ||
case 'object': | ||
Object.keys(any).forEach((key) => { | ||
any[key] && classes.push(key); | ||
}); | ||
break; | ||
default: | ||
break; | ||
} | ||
}; | ||
var _default = { | ||
classname: function classname() { | ||
var classes = []; | ||
anys.forEach(handle); | ||
var getType = function getType(any) { | ||
return Object.prototype.toString.call(any).match(/([^\s]+)]$/)[1].toLowerCase(); | ||
}; | ||
return classes.join(' '); | ||
}, | ||
/** | ||
* 获取指定的父级元素 | ||
* @param {*} child | ||
* @param {*} [root=document.body] | ||
* @param {*} fn | ||
* @returns {HTMLElement} | ||
*/ | ||
parent(child, root = document.body, fn) { | ||
let parent = child; | ||
while (parent && parent !== root) { | ||
const result = parent instanceof HTMLElement && fn(parent); | ||
if (result) { | ||
return parent; | ||
} | ||
parent = parent.parentElement; | ||
} | ||
return null; | ||
}, | ||
style($dom, styles) { | ||
$dom.style.cssText += styles; | ||
}, | ||
Timeout: class { | ||
queue = []; | ||
var handle = function handle(any) { | ||
switch (getType(any)) { | ||
case 'string': | ||
case 'number': | ||
classes.push(any); | ||
break; | ||
add(fn, time) { | ||
if (time > 0) { | ||
let t; | ||
const item = (exec = true) => { | ||
clearTimeout(t); | ||
exec && fn(); | ||
}; | ||
t = setTimeout(() => { | ||
fn(); | ||
this.queue = this.queue.filter((i) => i !== item); | ||
}, time); | ||
this.queue.push(item); | ||
} else { | ||
fn(); | ||
} | ||
return this; | ||
} | ||
case 'array': | ||
any.forEach(handle); | ||
break; | ||
clear() { | ||
this.queue.forEach((i) => i(false)); | ||
this.queue = []; | ||
return this; | ||
} | ||
case 'object': | ||
Object.keys(any).forEach(function (key) { | ||
any[key] && classes.push(key); | ||
}); | ||
break; | ||
done() { | ||
this.queue.forEach((i) => i()); | ||
this.queue = []; | ||
return this; | ||
} | ||
}, | ||
setTimeout(fn = () => {}, time = 0) { | ||
default: | ||
break; | ||
} | ||
}; | ||
for (var _len = arguments.length, anys = new Array(_len), _key = 0; _key < _len; _key++) { | ||
anys[_key] = arguments[_key]; | ||
} | ||
anys.forEach(handle); | ||
return classes.join(' '); | ||
}, | ||
/** | ||
* 获取指定的父级元素 | ||
* @param {*} child | ||
* @param {*} [root=document.body] | ||
* @param {*} fn | ||
* @returns {HTMLElement} | ||
*/ | ||
parent: function parent(child) { | ||
var root = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : document.body; | ||
var fn = arguments.length > 2 ? arguments[2] : undefined; | ||
var parent = child; | ||
while (parent && parent !== root) { | ||
var result = parent instanceof HTMLElement && fn(parent); | ||
if (result) { | ||
return parent; | ||
} | ||
parent = parent.parentElement; | ||
} | ||
return null; | ||
}, | ||
style: function style($dom, styles) { | ||
$dom.style.cssText += styles; | ||
}, | ||
Timeout: /*#__PURE__*/function () { | ||
function Timeout() { | ||
_classCallCheck(this, Timeout); | ||
_defineProperty(this, "queue", []); | ||
} | ||
_createClass(Timeout, [{ | ||
key: "add", | ||
value: function add(fn, time) { | ||
var _this = this; | ||
if (time > 0) { | ||
return setTimeout(fn, time); | ||
var t; | ||
var item = function item() { | ||
var exec = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; | ||
clearTimeout(t); | ||
exec && fn(); | ||
}; | ||
t = setTimeout(function () { | ||
fn(); | ||
_this.queue = _this.queue.filter(function (i) { | ||
return i !== item; | ||
}); | ||
}, time); | ||
this.queue.push(item); | ||
} else { | ||
fn(); | ||
} | ||
return fn(); | ||
}, | ||
// requestAnimationFrame 提升性能 | ||
RAF(...fns) { | ||
fns.forEach((fn) => (window.requestAnimationFrame ? requestAnimationFrame(fn) : fn())); | ||
}, | ||
// 数组对比 | ||
equalArr(arr1 = [], arr2 = []) { | ||
return arr1.length == arr2.length && arr1.every((i, index) => arr2[index] == i); | ||
}, | ||
position(ele) { | ||
const eleRect = ele.getBoundingClientRect(); | ||
const pRect = ele.parentElement.getBoundingClientRect(); | ||
return { | ||
left: eleRect.left - pRect.left, | ||
top: eleRect.top - pRect.top, | ||
}; | ||
}, | ||
getOrigin($dom) { | ||
const { left, right, top, bottom, width, height } = $dom.getBoundingClientRect(); | ||
return { | ||
left, | ||
right, | ||
top, | ||
bottom, | ||
width, | ||
height, | ||
}; | ||
}, | ||
transition($doms = [], time = 0) { | ||
const transition = time > 0 ? `${time}s cubic-bezier(0.25, 0.8, 0.25, 1)` : 'none'; | ||
$doms.forEach((ele) => { | ||
ele.style.cssText += `; | ||
-webkit-transition: ${transition}; | ||
transition: ${transition}; | ||
`; | ||
return this; | ||
} | ||
}, { | ||
key: "clear", | ||
value: function clear() { | ||
this.queue.forEach(function (i) { | ||
return i(false); | ||
}); | ||
}, | ||
getParent(child, parent) { | ||
// parent 可以是dom/class str/id str | ||
let $p = child; | ||
let match = false; | ||
while ($p !== document.body) { | ||
if ($p === parent) { | ||
match = true; | ||
break; | ||
} else { | ||
$p = $p.parentElement; | ||
} | ||
} | ||
return match ? $p : null; | ||
}, | ||
this.queue = []; | ||
return this; | ||
} | ||
}, { | ||
key: "done", | ||
value: function done() { | ||
this.queue.forEach(function (i) { | ||
return i(); | ||
}); | ||
this.queue = []; | ||
return this; | ||
} | ||
}]); | ||
return Timeout; | ||
}(), | ||
setTimeout: function (_setTimeout) { | ||
function setTimeout() { | ||
return _setTimeout.apply(this, arguments); | ||
} | ||
setTimeout.toString = function () { | ||
return _setTimeout.toString(); | ||
}; | ||
return setTimeout; | ||
}(function () { | ||
var fn = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {}; | ||
var time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
if (time > 0) { | ||
return setTimeout(fn, time); | ||
} | ||
return fn(); | ||
}), | ||
// requestAnimationFrame 提升性能 | ||
RAF: function RAF() { | ||
for (var _len2 = arguments.length, fns = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
fns[_key2] = arguments[_key2]; | ||
} | ||
fns.forEach(function (fn) { | ||
return window.requestAnimationFrame ? requestAnimationFrame(fn) : fn(); | ||
}); | ||
}, | ||
// 数组对比 | ||
equalArr: function equalArr() { | ||
var arr1 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | ||
var arr2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; | ||
return arr1.length == arr2.length && arr1.every(function (i, index) { | ||
return arr2[index] == i; | ||
}); | ||
}, | ||
position: function position(ele) { | ||
var eleRect = ele.getBoundingClientRect(); | ||
var pRect = ele.parentElement.getBoundingClientRect(); | ||
return { | ||
left: eleRect.left - pRect.left, | ||
top: eleRect.top - pRect.top | ||
}; | ||
}, | ||
getOrigin: function getOrigin($dom) { | ||
var _$dom$getBoundingClie = $dom.getBoundingClientRect(), | ||
left = _$dom$getBoundingClie.left, | ||
right = _$dom$getBoundingClie.right, | ||
top = _$dom$getBoundingClie.top, | ||
bottom = _$dom$getBoundingClie.bottom, | ||
width = _$dom$getBoundingClie.width, | ||
height = _$dom$getBoundingClie.height; | ||
return { | ||
left: left, | ||
right: right, | ||
top: top, | ||
bottom: bottom, | ||
width: width, | ||
height: height | ||
}; | ||
}, | ||
transition: function transition() { | ||
var $doms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | ||
var time = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; | ||
var transition = time > 0 ? "".concat(time, "s cubic-bezier(0.25, 0.8, 0.25, 1)") : 'none'; | ||
$doms.forEach(function (ele) { | ||
ele.style.cssText += ";\n -webkit-transition: ".concat(transition, ";\n transition: ").concat(transition, ";\n "); | ||
}); | ||
}, | ||
getParent: function getParent(child, parent) { | ||
// parent 可以是dom/class str/id str | ||
var $p = child; | ||
var match = false; | ||
while ($p !== document.body) { | ||
if ($p === parent) { | ||
match = true; | ||
break; | ||
} else { | ||
$p = $p.parentElement; | ||
} | ||
} | ||
return match ? $p : null; | ||
} | ||
}; | ||
exports["default"] = _default; |
{ | ||
"name": "a-touch", | ||
"version": "0.0.1", | ||
"version": "0.0.2", | ||
"description": "a touch for web", | ||
@@ -14,3 +14,12 @@ "main": "index.js", | ||
"author": "lingyan", | ||
"license": "ISC" | ||
"license": "ISC", | ||
"devDependencies": { | ||
"@babel/core": "^7.17.10", | ||
"@babel/preset-env": "^7.17.10", | ||
"eslint": "^8.14.0", | ||
"gulp": "^4.0.2", | ||
"gulp-babel": "^8.0.0", | ||
"husky": "^7.0.4", | ||
"prettier": "^2.6.2" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
133178
20
3244
7
1