@tarojs/components
Version:
480 lines (474 loc) • 16.2 kB
JavaScript
import { r as registerInstance, h, g as getElement, H as Host, c as createEvent } from './index-ab3c86da.js';
import { i as isElement } from './index-a00a7418.js';
const areaCss = "taro-movable-area-core{width:10px;height:10px;display:block;position:relative}";
const MovableArea = class {
constructor(hostRef) {
registerInstance(this, hostRef);
/** 子元素集合 */
this.views = [];
/** 手势缩放 */
this.scaleLength = 0;
this.viewsChanged = () => {
this.views = [];
const elements = this.element.querySelectorAll('taro-movable-view-core');
Array.from(elements).forEach((element) => {
this.views.push(element);
});
this.updateArea();
};
this.handleTouchStart = (e) => {
const touches = e.touches;
if (!touches || touches.length <= 1) {
return;
}
const gap = {
width: touches[1].pageX - touches[0].pageX,
height: touches[1].pageY - touches[0].pageY
};
this.scaleLength = Math.sqrt(gap.width * gap.width + gap.height * gap.height);
if (this.scaleArea) {
return;
}
const find = (target, views) => {
const loop = (e, t) => {
if (!(e = e.parentNode)) {
return false;
}
return (!isElement(e) || e !== document.body) && (e === t || e === t.element || e.element === t || loop(e, t));
};
for (let i = 0; i < views.length; i++) {
const view = views[i];
if (target === view["element"] || loop(target, view)) {
return view;
}
}
};
const touch1 = find(touches[0].target, this.views);
const touch2 = find(touches[1].target, this.views);
this.scaleTarget = touch1 && touch1 === touch2 ? touch1 : undefined;
};
this.handleTouchMove = (e) => {
const touches = e.touches;
if (!touches || touches.length <= 1) {
return;
}
e.preventDefault();
const gap = {
width: touches[1].pageX - touches[0].pageX,
height: touches[1].pageY - touches[0].pageY
};
if (this.scaleLength > 0) {
this.updateScale(Math.sqrt(gap.width * gap.width + gap.height * gap.height) / this.scaleLength);
}
};
this.handleTouchEnd = (e) => {
var _a, _b;
if ((e.touches && e.touches.length) || !e.changedTouches) {
return;
}
this.scaleLength = 0;
if (this.scaleArea) {
this.views.forEach((element) => {
var _a;
(_a = element["endScale"]) === null || _a === void 0 ? void 0 : _a.call(element);
});
}
else {
(_b = (_a = this.scaleTarget) === null || _a === void 0 ? void 0 : _a["endScale"]) === null || _b === void 0 ? void 0 : _b.call(_a);
}
this.scaleTarget = undefined;
};
this.updateScale = (scale) => {
var _a, _b;
if (!scale || scale === 1) {
return;
}
if (this.scaleArea) {
this.views.forEach((element) => {
var _a;
(_a = element["setScale"]) === null || _a === void 0 ? void 0 : _a.call(element, scale);
});
}
else {
(_b = (_a = this.scaleTarget) === null || _a === void 0 ? void 0 : _a["setScale"]) === null || _b === void 0 ? void 0 : _b.call(_a, scale);
}
};
this.updateArea = () => {
const computedStyle = window.getComputedStyle(this.element);
const clientRect = this.element.getBoundingClientRect();
const horizontal = ["Left", "Right"].map((e) => {
return parseFloat(computedStyle["border" + e + "Width"]) + parseFloat(computedStyle["padding" + e]);
});
const vertical = ["Top", "Bottom"].map((e) => {
return parseFloat(computedStyle["border" + e + "Width"]) + parseFloat(computedStyle["padding" + e]);
});
this.views.forEach((element) => {
var _a;
(_a = element["setParent"]) === null || _a === void 0 ? void 0 : _a.call(element, {
element: this.element,
area: {
height: clientRect.height - vertical[0] - vertical[1],
width: clientRect.width - horizontal[0] - horizontal[1]
}
});
});
};
this.scaleArea = undefined;
}
connectedCallback() {
this.observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
var _a, _b;
if (mutation.attributeName === "class" || mutation.attributeName === "style") {
const offsetWidth = this.element.offsetWidth;
const offsetHeight = this.element.offsetHeight;
if (offsetWidth !== ((_a = this.offset) === null || _a === void 0 ? void 0 : _a.width) || offsetHeight !== ((_b = this.offset) === null || _b === void 0 ? void 0 : _b.height)) {
this.updateArea();
}
this.offset = {
width: offsetWidth,
height: offsetHeight
};
}
});
});
this.observer.observe(this.element, {
attributes: true
});
}
disconnectedCallback() {
var _a;
(_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
}
componentDidLoad() {
this.viewsChanged();
}
render() {
return (h(Host, { onTouchStart: this.handleTouchStart, onTouchMove: this.handleTouchMove, onTouchEnd: this.handleTouchEnd }));
}
get element() { return getElement(this); }
};
MovableArea.style = areaCss;
const viewCss = "taro-movable-view-core{width:10px;height:10px;display:inline-block;position:absolute;top:0;left:0}";
const MovableView = class {
constructor(hostRef) {
registerInstance(this, hostRef);
this.onChange = createEvent(this, "change", 7);
this.onScale = createEvent(this, "scale", 7);
this.onHTouchMove = createEvent(this, "htouchmove", 7);
this.onVTouchMove = createEvent(this, "vtouchmove", 7);
/** 当前水平偏移 */
this.translateX = 0;
/** 当前垂直偏移 */
this.translateY = 0;
/** touch-start 原点 */
this.origin = { x: 0, y: 0 };
/** 父容器大小 */
this.area = { width: 0, height: 0 };
/** 原始缩放倍数 */
this.originScale = 1;
/** 当前缩放倍数 */
this.currentScale = 1;
/** 宽度 */
this.width = 0;
/** 高度 */
this.height = 0;
/** 移动边界 */
this.minX = 0;
this.minY = 0;
this.maxX = 0;
this.maxY = 0;
/** 移动基础位置 */
this.baseX = 0;
this.baseY = 0;
/** 偏移量 */
this.offset = { x: 0, y: 0 };
this.scaleOffset = { x: 0, y: 0 };
this.getLimitXY = (x, y) => {
let outOfBounds = false;
x > this.maxX ? (x = this.maxX, outOfBounds = true) : x < this.minX && (x = this.minX, outOfBounds = true);
y > this.maxY ? (y = this.maxY, outOfBounds = true) : y < this.minY && (y = this.minY, outOfBounds = true);
return { x, y, outOfBounds };
};
this.animationTo = (x, y, scale, source, noEmitChange, emitScale, callback) => {
if (this.animation) {
this.setTransform(x, y, scale, source, noEmitChange, emitScale);
callback === null || callback === void 0 ? void 0 : callback();
}
else {
this.setTransform(x, y, scale, source, noEmitChange, emitScale);
}
};
this.setTransform = (x, y, scale, source, noEmitChange, emitScale) => {
x = Number(x.toFixed(1));
y = Number(y.toFixed(1));
scale = Number((scale !== null && scale !== void 0 ? scale : this.currentScale).toFixed(3));
if (!this.outOfBounds) {
const limit = this.getLimitXY(x, y);
x = limit.x;
y = limit.y;
}
const subtract = (e, t) => {
return +((1e3 * e - 1e3 * t) / 1e3).toFixed(1);
};
const realX = subtract(x, this.scaleOffset.x);
const realY = subtract(y, this.scaleOffset.y);
if (this.translateX !== x || this.translateY !== y) {
!noEmitChange && this.onChange.emit({
x: realX,
y: realY,
source
});
}
if (scale !== this.currentScale) {
emitScale && this.onScale.emit({
scale,
x: realX,
y: realY
});
}
const transform = `translateX(${x}px) translateY(${y}px) translateZ(0px) scale(${scale})`;
this.element.style.transform = transform;
this.element.style.webkitTransform = transform;
this.translateX = x;
this.translateY = y;
this.currentScale = scale;
};
this.updateOffset = () => {
const offset = (element, parent) => {
if (element === parent || !element.offsetParent) {
return { left: 0, top: 0 };
}
const current = offset(element.offsetParent, parent);
return {
left: element.offsetLeft + current.left,
top: element.offsetTop + current.top
};
};
if (!this.parent) {
return;
}
const current = offset(this.element, this.parent);
this.offset.x = current.left;
this.offset.y = current.top;
};
this.updateScaleOffset = (scale = this.currentScale) => {
const rect = this.element.getBoundingClientRect();
this.height = rect.height / this.currentScale;
this.width = rect.width / this.currentScale;
this.scaleOffset.x = (this.width * scale - this.width) / 2;
this.scaleOffset.y = (this.height * scale - this.height) / 2;
};
this.updateBoundary = () => {
const x1 = 0 - this.offset.x + this.scaleOffset.x;
const x2 = this.area.width - this.width - this.offset.x - this.scaleOffset.x;
this.minX = Math.min(x1, x2);
this.maxX = Math.max(x1, x2);
const y1 = 0 - this.offset.y + this.scaleOffset.y;
const y2 = this.area.height - this.height - this.offset.y - this.scaleOffset.y;
this.minY = Math.min(y1, y2);
this.maxY = Math.max(y1, y2);
};
this.updateScale = (scale, animation, animationCallback) => {
if (!this.scale) {
return;
}
const target = this.adjustScale(scale);
this.updateScaleOffset(target);
this.updateBoundary();
const { x, y } = this.getLimitXY(this.translateX, this.translateY);
if (animation) {
this.animationTo(x, y, target, "", true, true, animationCallback);
}
else if (!this.updating) {
this.updating = true;
requestAnimationFrame(() => {
this.setTransform(x, y, target, "", true, true);
this.updating = false;
});
}
};
this.setOriginScale = (scale) => {
this.originScale = scale;
};
this.adjustScale = (scale) => {
return Math.min(10, this.scaleMax, Math.max(.5, this.scaleMin, scale));
};
this.handleTouchStart = (e) => {
const touches = e.touches;
if (this.disabled || touches.length > 1 || !this.element) {
return;
}
const touch = touches[0];
this.touching = true;
this.firstMoveFireEvent = false;
this.origin.x = touch.screenX;
this.origin.y = touch.screenY;
this.baseX = this.translateX;
this.baseY = this.translateY;
this.element.style.willChange = "transform";
};
this.handleTouchMove = (e) => {
const touches = e.touches;
if (this.disabled || !this.element || this.scaling || !this.touching || touches.length > 1) {
return;
}
if (this.direction !== "horizontal") {
e.preventDefault();
}
const touch = touches[0];
const x = touch.screenX - this.origin.x;
const y = touch.screenY - this.origin.y;
this.setTransform(this.xMove ? (x + this.baseX) : 0, this.yMove ? (y + this.baseY) : 0);
if (!this.firstMoveFireEvent) {
this.firstMoveFireEvent = true;
const onTouchMove = Math.abs(x) > Math.abs(y) ? this.onHTouchMove : this.onVTouchMove;
onTouchMove.emit({
originalEvent: e,
bubbles: false,
capturePhase: false,
composed: true,
extraFields: {
touches: e.touches || {},
changedTouches: e.changedTouches || {}
}
});
}
};
this.handleTouchEnd = (e) => {
const touch = e.changedTouches[0];
if (this.disabled || !this.touching || !touch) {
return;
}
this.touching = false;
const x = touch.screenX - this.origin.x;
const y = touch.screenY - this.origin.y;
this.setTransform(this.xMove ? (x + this.baseX) : 0, this.yMove ? (y + this.baseY) : 0);
};
this.x = 0;
this.y = 0;
this.direction = "none";
this.outOfBounds = false;
this.inertia = false;
this.friction = 2;
this.damping = 20;
this.disabled = false;
this.scale = false;
this.scaleMin = .5;
this.scaleMax = 10;
this.scaleValue = 1;
this.animation = true;
}
watchX(newValue) {
this.setTransform(parseFloat(`${newValue || 0}`), this.translateY);
}
watchY(newValue) {
this.setTransform(this.translateX, parseFloat(`${newValue || 0}`));
}
watchScaleMinOrMax() {
if (!this.scale)
return false;
this.updateScale(this.currentScale, true);
this.setOriginScale(this.currentScale);
}
watchScaleValue(scale) {
if (!this.scale) {
return false;
}
this.updateScale(scale, true);
this.setOriginScale(scale);
return scale;
}
/**
* 设置父节点
*/
async setParent({ element, area }) {
const scale = this.scale ? this.scaleValue : 1;
this.area = area;
this.parent = element;
this.updateOffset();
this.updateScaleOffset(scale);
this.updateBoundary();
this.setTransform(Number(this.x) + this.scaleOffset.x, Number(this.y) + this.scaleOffset.y, scale, "", true);
this.setOriginScale(scale);
}
/**
* 结束缩放
*/
async endScale() {
this.scaling = false;
this.setOriginScale(this.currentScale);
}
/**
* 更新缩放
*/
async setScale(scale) {
if (!this.scale) {
return;
}
this.scaling = true;
this.updateScale(scale * this.originScale);
}
connectedCallback() {
this.observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
const name = mutation.attributeName;
if (name && ["class", "style"].includes(name)) {
const oldValue = mutation.oldValue;
const newValue = mutation.target.getAttribute(name);
if (oldValue === newValue) {
return;
}
const filter = (input) => {
return input === null || input === void 0 ? void 0 : input.split(";").filter((item) => {
return !["transform", "will-change"].find((key) => {
return item.trim().startsWith(key);
});
}).join(";");
};
if (name === "style" && filter(newValue) === filter(oldValue)) {
return;
}
this.updateOffset();
this.updateScaleOffset();
this.updateBoundary();
this.setTransform(this.translateX, this.translateY);
}
});
});
this.observer.observe(this.element, {
attributes: true,
attributeOldValue: true
});
}
disconnectedCallback() {
var _a;
(_a = this.observer) === null || _a === void 0 ? void 0 : _a.disconnect();
}
componentDidLoad() {
this.element.style.transformOrigin = "center";
this.xMove = ["horizontal", "all"].includes(this.direction);
this.yMove = ["vertical", "all"].includes(this.direction);
if (this.friction <= 0) {
this.friction = 2;
}
if (this.x || this.y) {
const x = parseFloat(`${this.x || 0}`);
const y = parseFloat(`${this.y || 0}`);
this.setTransform(x, y);
}
}
render() {
return (h(Host, { onTouchStart: this.handleTouchStart, onTouchMove: this.handleTouchMove, onTouchEnd: this.handleTouchEnd }));
}
get element() { return getElement(this); }
static get watchers() { return {
"x": ["watchX"],
"y": ["watchY"],
"scaleMin": ["watchScaleMinOrMax"],
"scaleMax": ["watchScaleMinOrMax"],
"scaleValue": ["watchScaleValue"]
}; }
};
MovableView.style = viewCss;
export { MovableArea as taro_movable_area_core, MovableView as taro_movable_view_core };