blob: 7ef471e429493a4e9ed2120e573afc262454a52a [file] [log] [blame]
import env from './env';
import { buildTransformer } from './fourPointsTransform';
var EVENT_SAVED_PROP = '___zrEVENTSAVED';
var _calcOut = [];
/**
* Transform "local coord" from `elFrom` to `elTarget`.
* "local coord": the coord based on the input `el`. The origin point is at
* the position of "left: 0; top: 0;" in the `el`.
*
* Support when CSS transform is used.
*
* Having the `out` (that is, `[outX, outY]`), we can create an DOM element
* and set the CSS style as "left: outX; top: outY;" and append it to `elTarge`
* to locate the element.
*
* For example, this code below positions a child of `document.body` on the event
* point, no matter whether `body` has `margin`/`paddin`/`transfrom`/... :
* ```js
* transformLocalCoord(out, container, document.body, event.offsetX, event.offsetY);
* if (!eqNaN(out[0])) {
* // Then locate the tip element on the event point.
* var tipEl = document.createElement('div');
* tipEl.style.cssText = 'position: absolute; left:' + out[0] + ';top:' + out[1] + ';';
* document.body.appendChild(tipEl);
* }
* ```
*
* Notice: In some env this method is not supported. If called, `out` will be `[NaN, NaN]`.
*
* @param {Array.<number>} out [inX: number, inY: number] The output..
* If can not transform, `out` will not be modified but return `false`.
* @param {HTMLElement} elFrom The `[inX, inY]` is based on elFrom.
* @param {HTMLElement} elTarget The `out` is based on elTarget.
* @param {number} inX
* @param {number} inY
* @return {boolean} Whether transform successfully.
*/
export function transformLocalCoord(out, elFrom, elTarget, inX, inY) {
return transformCoordWithViewport(_calcOut, elFrom, inX, inY, true) && transformCoordWithViewport(out, elTarget, _calcOut[0], _calcOut[1]);
}
/**
* Transform between a "viewport coord" and a "local coord".
* "viewport coord": the coord based on the left-top corner of the viewport
* of the browser.
* "local coord": the coord based on the input `el`. The origin point is at
* the position of "left: 0; top: 0;" in the `el`.
*
* Support the case when CSS transform is used on el.
*
* @param {Array.<number>} out [inX: number, inY: number] The output. If `inverse: false`,
* it represents "local coord", otherwise "vireport coord".
* If can not transform, `out` will not be modified but return `false`.
* @param {HTMLElement} el The "local coord" is based on the `el`, see comment above.
* @param {number} inX If `inverse: false`,
* it represents "vireport coord", otherwise "local coord".
* @param {number} inY If `inverse: false`,
* it represents "vireport coord", otherwise "local coord".
* @param {boolean} [inverse=false]
* `true`: from "viewport coord" to "local coord".
* `false`: from "local coord" to "viewport coord".
* @return {boolean} Whether transform successfully.
*/
export function transformCoordWithViewport(out, el, inX, inY, inverse) {
if (el.getBoundingClientRect && env.domSupported && !isCanvasEl(el)) {
var saved = el[EVENT_SAVED_PROP] || (el[EVENT_SAVED_PROP] = {});
var markers = prepareCoordMarkers(el, saved);
var transformer = preparePointerTransformer(markers, saved, inverse);
if (transformer) {
transformer(out, inX, inY);
return true;
}
}
return false;
}
function prepareCoordMarkers(el, saved) {
var markers = saved.markers;
if (markers) {
return markers;
}
markers = saved.markers = [];
var propLR = ['left', 'right'];
var propTB = ['top', 'bottom'];
for (var i = 0; i < 4; i++) {
var marker = document.createElement('div');
var stl = marker.style;
var idxLR = i % 2;
var idxTB = (i >> 1) % 2;
stl.cssText = ['position: absolute', 'visibility: hidden', 'padding: 0', 'margin: 0', 'border-width: 0', 'user-select: none', 'width:0', 'height:0', // 'width: 5px',
// 'height: 5px',
propLR[idxLR] + ':0', propTB[idxTB] + ':0', propLR[1 - idxLR] + ':auto', propTB[1 - idxTB] + ':auto', ''].join('!important;');
el.appendChild(marker);
markers.push(marker);
}
return markers;
}
function preparePointerTransformer(markers, saved, inverse) {
var transformerName = inverse ? 'invTrans' : 'trans';
var transformer = saved[transformerName];
var oldSrcCoords = saved.srcCoords;
var oldCoordTheSame = true;
var srcCoords = [];
var destCoords = [];
for (var i = 0; i < 4; i++) {
var rect = markers[i].getBoundingClientRect();
var ii = 2 * i;
var x = rect.left;
var y = rect.top;
srcCoords.push(x, y);
oldCoordTheSame = oldCoordTheSame && oldSrcCoords && x === oldSrcCoords[ii] && y === oldSrcCoords[ii + 1];
destCoords.push(markers[i].offsetLeft, markers[i].offsetTop);
} // Cache to avoid time consuming of `buildTransformer`.
return oldCoordTheSame && transformer ? transformer : (saved.srcCoords = srcCoords, saved[transformerName] = inverse ? buildTransformer(destCoords, srcCoords) : buildTransformer(srcCoords, destCoords));
}
export function isCanvasEl(el) {
return el.nodeName.toUpperCase() === 'CANVAS';
}