import { Zoomooz } from "./Zoomooz"; import * as $ from 'jquery'; import { element } from "prop-types"; //**********************************// //*** Helpers ***// //**********************************// /* * jquery.zoomooz-helpers.js, part of: * http://janne.aukia.com/zoomooz * * LICENCE INFORMATION: * * Copyright (c) 2010 Janne Aukia (janne.aukia.com) * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL Version 2 (GPL-LICENSE.txt) licenses. * */ /*jslint sub: true */ export interface IelementTransform { sx: number, sy: number, tx: number, ty: number, r?: number, skeyx?: number, skeyy?: number, } export interface ICSSElementTransform{ scaleX?:number; scaleY?:number; translateX?:number; translateY?:number; rotate?:number; skewX?:number; skewY?:number; } export type transfromParams = {tx:number,ty:number,sx:number,sy:number,r:number,k:number }; export class Helpers{ static browser_prefixes = ["-moz-", "-webkit-", "-o-", "-ms-"]; static printFixedNumber(x) { return Number(x).toFixed(6); } static constructCssObject(elementTransform:IelementTransform):ICSSElementTransform{ return { scaleX:elementTransform.sx, scaleY:elementTransform.sy, translateX:elementTransform.tx, translateY:elementTransform.ty, rotate:elementTransform.r, skewX:elementTransform.skeyx, skewY:elementTransform.skeyy }; } static constructEasingCss(input:Array) { return "cubic-bezier(" + Helpers.roundNumber(input[0], 6) + "," + Helpers.roundNumber(input[1], 6) + "," + Helpers.roundNumber(input[2], 6) + "," + Helpers.roundNumber(input[3], 6) + ")"; } static constructBezierArray(input:Array|string|undefined){ if(!input){ input = "linear"; } var params = new Array(); if ((input instanceof Array)) { params = input; } else { switch (input) { case "linear": params = [0.0, 0.0, 1.0, 1.0]; break; case "ease": params = [0.25, 0.1, 0.25, 1.0]; break; case "ease-in": params = [0.42, 0.0, 1.0, 1.0]; break; case "ease-out": params = [0.0, 0.0, 0.58, 1.0]; break; case "ease-in-out": params = [0.42, 0.0, 0.58, 1.0]; break; } } return params; } static constructEasingFunction(input:Array|string|undefined, dur) { let params = this.constructBezierArray(input); var easingFunc = function (t) { return Helpers.cubicBezierAtTime(t, params[0], params[1], params[2], params[3], dur); }; return easingFunc; } static constructZoomRootCssTransform(trans:string, duration?:number, easing?:Array) { var propMap = {}; Helpers.forEachPrefix(function (prefix) { propMap[prefix + "transform"] = trans; }, true); if (duration) { var transdur = Helpers.roundNumber(duration / 1000, 6) + "s"; propMap["-webkit-transition-duration"] = transdur; propMap["-o-transition-duration"] = transdur; propMap["-moz-transition-duration"] = transdur; } if (easing) { var transtiming = Helpers.constructEasingCss(easing); propMap["-webkit-transition-timing-function"] = transtiming; propMap["-o-transition-timing-function"] = transtiming; propMap["-moz-transition-timing-function"] = transtiming; } return propMap; } static constructResetZoomRootCssTransform() { var propMap = {}; Helpers.forEachPrefix(function (prefix) { propMap[prefix + "transform"] = ''; }, true); propMap["-webkit-transition-duration"] = ''; propMap["-o-transition-duration"] = ''; propMap["-moz-transition-duration"] = ''; propMap["-webkit-transition-timing-function"] = ''; propMap["-o-transition-timing-function"] = ''; propMap["-moz-transition-timing-function"] = ''; return propMap; } static interpolateArrays(st, et, pos) { var it = {}; for (var i in st) { if (st.hasOwnProperty(i)) { it[i] = st[i] + (et[i] - st[i]) * pos; } } return it; } static roundNumber(number, precision) { precision = Math.abs(parseInt(precision, 10)) || 0; var coefficient = Math.pow(10, precision); return Math.round(number * coefficient) / coefficient; } static matrixCompose(ia:IelementTransform) { var ret = ""; /* this probably made safari 5.1.1. + os 10.6.8 + non-unibody mac? */ //ret += "translateZ(0) "; ret += "translate(" + this.roundNumber(ia.tx, 6) + "px," + this.roundNumber(ia.ty, 6) + "px) "; if(ia.r){ ret += "rotate(" + this.roundNumber(ia.r, 6) + "rad) "; } if(ia.skeyx){ ret += "skewX(" + this.roundNumber(ia.skeyx, 6) + "rad) "; } ret += "scale(" + this.roundNumber(ia.sx, 6) + "," + this.roundNumber(ia.sy, 6) + ")"; return ret; } static matrixComposeCSS(ia:ICSSElementTransform) { var ret = ""; /* this probably made safari 5.1.1. + os 10.6.8 + non-unibody mac? */ //ret += "translateZ(0) "; ret += "translate(" + this.roundNumber(ia.translateX, 6) + "px," + this.roundNumber(ia.translateY, 6) + "px) "; if(ia.rotate){ ret += "rotate(" + this.roundNumber(ia.rotate, 6) + "rad) "; } if(ia.skewX){ ret += "skewX(" + this.roundNumber(ia.skewX, 6) + "rad) "; } ret += "scale(" + this.roundNumber(ia.scaleX, 6) + "," + this.roundNumber(ia.scaleY, 6) + ")"; return ret; } static getElementTransform = function (elem) { let retVal; Helpers.forEachPrefix(function (prefix) { retVal = retVal || $(elem).css(prefix + "transform"); }, true); return retVal; }; static forEachPrefix = function (func, includeNoPrefix=false) { for (let i = 0; i < Helpers.browser_prefixes.length; i++) { func(Helpers.browser_prefixes[i]); } if (includeNoPrefix) { func(""); } } static constructTransformation(elem) { let rawTrans = Helpers.getElementTransform(elem); if (!rawTrans) { // @ts-ignore return new PureCSSMatrix(); } else { // @ts-ignore return new PureCSSMatrix(rawTrans); } } static getElemTransform(elem):IelementTransform { let regexp_filter_number = /([0-9.\-e]+)/g; let regexp_is_deg = /deg$/; let regexp_trans_splitter = /([a-zA-Z]+)\(([^\)]+)\)/g; function filterNumber(x) { return x.match(regexp_filter_number); } function rawRotationToRadians(raw) { let rot = parseFloat(filterNumber(raw)); if (raw.match(regexp_is_deg)) { rot = (2 * Math.PI) * rot / 360.0; } return rot; } let transString = Helpers.getElementTransform(elem); let elementTransform: IelementTransform = { sx: 1, sy: 1, tx: 0, ty: 0 }; let items; while ((items = regexp_trans_splitter.exec(transString)) !== null) { let action = items[1].toLowerCase(); let val = items[2].split(","), sx, sy; if (action == "matrix") { sx = parseFloat(val[0]); if (val.length > 1) { sy = parseFloat(val[3]); } else { sy = sx; } elementTransform.sx = sx; elementTransform.sy = sy; elementTransform.tx = parseFloat(filterNumber(val[4])); elementTransform.ty = parseFloat(filterNumber(val[5])); //TODO:Retrieve values from the matrix itself //trans = Matrix.create([[parseFloat(val[0]), parseFloat(val[2]), parseFloat(filterNumber(val[4]))], // [parseFloat(val[1]), parseFloat(val[3]), parseFloat(filterNumber(val[5]))], // [0, 0, 1]]); } else if (action == "translate") { elementTransform.tx = parseFloat(filterNumber(val[0])); elementTransform.ty = parseFloat(filterNumber(val[1])); } else if (action == "scale") { sx = parseFloat(val[0]); if (val.length > 1) { sy = parseFloat(val[1]); } else { sy = sx; } elementTransform.sx = sx; elementTransform.sy = sy; } else if (action == "rotate") { elementTransform.r = rawRotationToRadians(val[0]); } else if (action == "skew" || action == "skewx") { // TODO: supports only one parameter skew elementTransform.skeyx = Math.tan(rawRotationToRadians(val[0])); } else if (action == "skewy") { // TODO: test that this works (or unit test them all!) elementTransform.skeyy = Math.tan(rawRotationToRadians(val[0])); } else { console.log("Problem with setMatrixValue", action, val); } } return elementTransform; } static cubicBezierAtTime(t, p1x, p1y, p2x, p2y, duration) { var ax = 0, bx = 0, cx = 0, ay = 0, by = 0, cy = 0; // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule. function sampleCurveX(t) { return ((ax * t + bx) * t + cx) * t; } function sampleCurveY(t) { return ((ay * t + by) * t + cy) * t; } function sampleCurveDerivativeX(t) { return (3.0 * ax * t + 2.0 * bx) * t + cx; } // The epsilon value to pass given that the animation is going to run over |dur| seconds. The longer the // animation, the more precision is needed in the timing function result to avoid ugly discontinuities. function solveEpsilon(duration) { return 1.0 / (200.0 * duration); } function solve(x, epsilon) { return sampleCurveY(solveCurveX(x, epsilon)); } // Given an x value, find a parametric value it came from. function solveCurveX(x, epsilon) { var t0, t1, t2, x2, d2, i; function fabs(n) { if (n >= 0) { return n; } else { return 0 - n; } } // First try a few iterations of Newton's method -- normally very fast. for (t2 = x, i = 0; i < 8; i++) { x2 = sampleCurveX(t2) - x; if (fabs(x2) < epsilon) { return t2; } d2 = sampleCurveDerivativeX(t2); if (fabs(d2) < 1e-6) { break; } t2 = t2 - x2 / d2; } // Fall back to the bisection method for reliability. t0 = 0.0; t1 = 1.0; t2 = x; if (t2 < t0) { return t0; } if (t2 > t1) { return t1; } while (t0 < t1) { x2 = sampleCurveX(t2); if (fabs(x2 - x) < epsilon) { return t2; } if (x > x2) { t0 = t2; } else { t1 = t2; } t2 = (t1 - t0) * 0.5 + t0; } return t2; // Failure. } // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1). cx = 3.0 * p1x; bx = 3.0 * (p2x - p1x) - cx; ax = 1.0 - cx - bx; cy = 3.0 * p1y; by = 3.0 * (p2y - p1y) - cy; ay = 1.0 - cy - by; // Convert from input time to parametric value in curve, then from that to output time. return solve(t, solveEpsilon(duration)); } }