235 lines
8.8 KiB
JavaScript
235 lines
8.8 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.RNSVGElements = exports.PointerTypeMapping = void 0;
|
|
exports.calculateViewScale = calculateViewScale;
|
|
exports.degToRad = exports.coneToDeviation = void 0;
|
|
exports.isPointerInBounds = isPointerInBounds;
|
|
exports.isRNSVGElement = isRNSVGElement;
|
|
exports.isRNSVGNode = isRNSVGNode;
|
|
exports.tryExtractStylusData = tryExtractStylusData;
|
|
var _PointerType = require("../PointerType");
|
|
function isPointerInBounds(view, {
|
|
x,
|
|
y
|
|
}) {
|
|
const rect = view.getBoundingClientRect();
|
|
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom;
|
|
}
|
|
const PointerTypeMapping = exports.PointerTypeMapping = new Map([['mouse', _PointerType.PointerType.MOUSE], ['touch', _PointerType.PointerType.TOUCH], ['pen', _PointerType.PointerType.STYLUS], ['none', _PointerType.PointerType.OTHER]]);
|
|
const degToRad = degrees => degrees * Math.PI / 180;
|
|
exports.degToRad = degToRad;
|
|
const coneToDeviation = degrees => Math.cos(degToRad(degrees / 2));
|
|
exports.coneToDeviation = coneToDeviation;
|
|
function calculateViewScale(view) {
|
|
const styles = getComputedStyle(view);
|
|
const resultScales = {
|
|
scaleX: 1,
|
|
scaleY: 1
|
|
};
|
|
|
|
// Get scales from scale property
|
|
if (styles.scale !== undefined && styles.scale !== 'none') {
|
|
const scales = styles.scale.split(' ');
|
|
if (scales[0]) {
|
|
resultScales.scaleX = parseFloat(scales[0]);
|
|
}
|
|
resultScales.scaleY = scales[1] ? parseFloat(scales[1]) : parseFloat(scales[0]);
|
|
}
|
|
|
|
// Get scales from transform property
|
|
const matrixElements = new RegExp(/matrix\((.+)\)/).exec(styles.transform)?.[1];
|
|
if (matrixElements) {
|
|
const matrixElementsArray = matrixElements.split(', ');
|
|
resultScales.scaleX *= parseFloat(matrixElementsArray[0]);
|
|
resultScales.scaleY *= parseFloat(matrixElementsArray[3]);
|
|
}
|
|
return resultScales;
|
|
}
|
|
function tryExtractStylusData(event) {
|
|
const pointerType = PointerTypeMapping.get(event.pointerType);
|
|
if (pointerType !== _PointerType.PointerType.STYLUS) {
|
|
return;
|
|
}
|
|
|
|
// @ts-ignore This property exists (https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent#instance_properties)
|
|
const eventAzimuthAngle = event.azimuthAngle;
|
|
// @ts-ignore This property exists (https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent#instance_properties)
|
|
const eventAltitudeAngle = event.altitudeAngle;
|
|
if (event.tiltX === 0 && event.tiltY === 0) {
|
|
// If we are in this branch, it means that either tilt properties are not supported and we have to calculate them from altitude and azimuth angles,
|
|
// or stylus is perpendicular to the screen and we can use altitude / azimuth instead of tilt
|
|
|
|
// If azimuth and altitude are undefined in this branch, it means that we are either perpendicular to the screen,
|
|
// or that none of the position sets is supported. In that case, we can treat stylus as perpendicular
|
|
if (eventAzimuthAngle === undefined || eventAltitudeAngle === undefined) {
|
|
return {
|
|
tiltX: 0,
|
|
tiltY: 0,
|
|
azimuthAngle: Math.PI / 2,
|
|
altitudeAngle: Math.PI / 2,
|
|
pressure: event.pressure
|
|
};
|
|
}
|
|
const {
|
|
tiltX,
|
|
tiltY
|
|
} = spherical2tilt(eventAltitudeAngle, eventAzimuthAngle);
|
|
return {
|
|
tiltX,
|
|
tiltY,
|
|
azimuthAngle: eventAzimuthAngle,
|
|
altitudeAngle: eventAltitudeAngle,
|
|
pressure: event.pressure
|
|
};
|
|
}
|
|
const {
|
|
altitudeAngle,
|
|
azimuthAngle
|
|
} = tilt2spherical(event.tiltX, event.tiltY);
|
|
return {
|
|
tiltX: event.tiltX,
|
|
tiltY: event.tiltY,
|
|
azimuthAngle,
|
|
altitudeAngle,
|
|
pressure: event.pressure
|
|
};
|
|
}
|
|
|
|
// `altitudeAngle` and `azimuthAngle` are experimental properties, which are not supported on Firefox and Safari.
|
|
// Given that, we use `tilt` properties and algorithm that converts one value to another.
|
|
//
|
|
// Source: https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
|
|
function tilt2spherical(tiltX, tiltY) {
|
|
const tiltXrad = tiltX * Math.PI / 180;
|
|
const tiltYrad = tiltY * Math.PI / 180;
|
|
|
|
// calculate azimuth angle
|
|
let azimuthAngle = 0;
|
|
if (tiltX === 0) {
|
|
if (tiltY > 0) {
|
|
azimuthAngle = Math.PI / 2;
|
|
} else if (tiltY < 0) {
|
|
azimuthAngle = 3 * Math.PI / 2;
|
|
}
|
|
} else if (tiltY === 0) {
|
|
if (tiltX < 0) {
|
|
azimuthAngle = Math.PI;
|
|
}
|
|
} else if (Math.abs(tiltX) === 90 || Math.abs(tiltY) === 90) {
|
|
// not enough information to calculate azimuth
|
|
azimuthAngle = 0;
|
|
} else {
|
|
// Non-boundary case: neither tiltX nor tiltY is equal to 0 or +-90
|
|
const tanX = Math.tan(tiltXrad);
|
|
const tanY = Math.tan(tiltYrad);
|
|
azimuthAngle = Math.atan2(tanY, tanX);
|
|
if (azimuthAngle < 0) {
|
|
azimuthAngle += 2 * Math.PI;
|
|
}
|
|
}
|
|
|
|
// calculate altitude angle
|
|
let altitudeAngle = 0;
|
|
if (Math.abs(tiltX) === 90 || Math.abs(tiltY) === 90) {
|
|
altitudeAngle = 0;
|
|
} else if (tiltX === 0) {
|
|
altitudeAngle = Math.PI / 2 - Math.abs(tiltYrad);
|
|
} else if (tiltY === 0) {
|
|
altitudeAngle = Math.PI / 2 - Math.abs(tiltXrad);
|
|
} else {
|
|
// Non-boundary case: neither tiltX nor tiltY is equal to 0 or +-90
|
|
altitudeAngle = Math.atan(1.0 / Math.sqrt(Math.pow(Math.tan(tiltXrad), 2) + Math.pow(Math.tan(tiltYrad), 2)));
|
|
}
|
|
return {
|
|
altitudeAngle: altitudeAngle,
|
|
azimuthAngle: azimuthAngle
|
|
};
|
|
}
|
|
|
|
// If we are on a platform that doesn't support `tiltX` and `tiltY`, we have to calculate them from `altitude` and `azimuth` angles.
|
|
//
|
|
// Source: https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
|
|
function spherical2tilt(altitudeAngle, azimuthAngle) {
|
|
const radToDeg = 180 / Math.PI;
|
|
let tiltXrad = 0;
|
|
let tiltYrad = 0;
|
|
if (altitudeAngle === 0) {
|
|
// the pen is in the X-Y plane
|
|
if (azimuthAngle === 0 || azimuthAngle === 2 * Math.PI) {
|
|
// pen is on positive X axis
|
|
tiltXrad = Math.PI / 2;
|
|
}
|
|
if (azimuthAngle === Math.PI / 2) {
|
|
// pen is on positive Y axis
|
|
tiltYrad = Math.PI / 2;
|
|
}
|
|
if (azimuthAngle === Math.PI) {
|
|
// pen is on negative X axis
|
|
tiltXrad = -Math.PI / 2;
|
|
}
|
|
if (azimuthAngle === 3 * Math.PI / 2) {
|
|
// pen is on negative Y axis
|
|
tiltYrad = -Math.PI / 2;
|
|
}
|
|
if (azimuthAngle > 0 && azimuthAngle < Math.PI / 2) {
|
|
tiltXrad = Math.PI / 2;
|
|
tiltYrad = Math.PI / 2;
|
|
}
|
|
if (azimuthAngle > Math.PI / 2 && azimuthAngle < Math.PI) {
|
|
tiltXrad = -Math.PI / 2;
|
|
tiltYrad = Math.PI / 2;
|
|
}
|
|
if (azimuthAngle > Math.PI && azimuthAngle < 3 * Math.PI / 2) {
|
|
tiltXrad = -Math.PI / 2;
|
|
tiltYrad = -Math.PI / 2;
|
|
}
|
|
if (azimuthAngle > 3 * Math.PI / 2 && azimuthAngle < 2 * Math.PI) {
|
|
tiltXrad = Math.PI / 2;
|
|
tiltYrad = -Math.PI / 2;
|
|
}
|
|
}
|
|
if (altitudeAngle !== 0) {
|
|
const tanAlt = Math.tan(altitudeAngle);
|
|
tiltXrad = Math.atan(Math.cos(azimuthAngle) / tanAlt);
|
|
tiltYrad = Math.atan(Math.sin(azimuthAngle) / tanAlt);
|
|
}
|
|
const tiltX = Math.round(tiltXrad * radToDeg);
|
|
const tiltY = Math.round(tiltYrad * radToDeg);
|
|
return {
|
|
tiltX,
|
|
tiltY
|
|
};
|
|
}
|
|
const RNSVGElements = exports.RNSVGElements = new Set(['Circle', 'ClipPath', 'Ellipse', 'ForeignObject', 'G', 'Image', 'Line', 'Marker', 'Mask', 'Path', 'Pattern', 'Polygon', 'Polyline', 'Rect', 'Svg', 'Symbol', 'TSpan', 'Text', 'TextPath', 'Use']);
|
|
|
|
// This function helps us determine whether given node is SVGElement or not. In our implementation of
|
|
// findNodeHandle, we can encounter such element in 2 forms - SVG tag or ref to SVG Element. Since Gesture Handler
|
|
// does not depend on SVG, we use our simplified SVGRef type that has `elementRef` field. This is something that is present
|
|
// in actual SVG ref object.
|
|
//
|
|
// In order to make sure that node passed into this function is in fact SVG element, first we check if its constructor name
|
|
// corresponds to one of the possible SVG elements. Then we also check if `elementRef` field exists.
|
|
// By doing both steps we decrease probability of detecting situations where, for example, user makes custom `Circle` and
|
|
// we treat it as SVG.
|
|
function isRNSVGElement(viewRef) {
|
|
const componentClassName = Object.getPrototypeOf(viewRef).constructor.name;
|
|
return RNSVGElements.has(componentClassName) && Object.hasOwn(viewRef, 'elementRef');
|
|
}
|
|
|
|
// This function checks if given node is SVGElement. Unlike the function above, this one
|
|
// operates on React Nodes, not DOM nodes.
|
|
//
|
|
// Second condition was introduced to handle case where SVG element was wrapped with
|
|
// `createAnimatedComponent` from Reanimated.
|
|
function isRNSVGNode(node) {
|
|
// If `ref` has `rngh` field, it means that component comes from Gesture Handler. This is a special case for
|
|
// `Text` component, which is present in `RNSVGElements` set, yet we don't want to treat it as SVG.
|
|
if (node.ref?.rngh) {
|
|
return false;
|
|
}
|
|
return Object.getPrototypeOf(node?.type)?.name === 'WebShape' || RNSVGElements.has(node?.type?.displayName);
|
|
}
|
|
//# sourceMappingURL=utils.js.map
|