addimated
Version:
An always interruptable, declarative animation library for React
163 lines (142 loc) • 4.91 kB
JavaScript
// Following are transform functions who accept arguments of type <length> or <length-percentage>.
// These functions won't work if we send them numbers, so we convert those numbers to px.
// Source: https://developer.mozilla.org/en-US/docs/Web/CSS/transform?v=b
var transformWithLengthUnits = {
translateX: true,
translateY: true,
translateZ: true,
perspective: true
}; // { translateY: 35 } => 'translateY(35px)'
// { scale: 2 } => 'scale(2)'
function mapTransform(t) {
var k = Object.keys(t)[0];
var unit = transformWithLengthUnits[k] && typeof t[k] === "number" ? "px" : "";
return "".concat(k, "(").concat(t[k]).concat(unit, ")");
}
var isUnitlessNumber = {
animationIterationCount: true,
borderImageOutset: true,
borderImageSlice: true,
borderImageWidth: true,
boxFlex: true,
boxFlexGroup: true,
boxOrdinalGroup: true,
columnCount: true,
columns: true,
flex: true,
flexGrow: true,
flexPositive: true,
flexShrink: true,
flexNegative: true,
flexOrder: true,
gridRow: true,
gridRowEnd: true,
gridRowSpan: true,
gridRowStart: true,
gridColumn: true,
gridColumnEnd: true,
gridColumnSpan: true,
gridColumnStart: true,
fontWeight: true,
lineClamp: true,
lineHeight: true,
opacity: true,
order: true,
orphans: true,
tabSize: true,
widows: true,
zIndex: true,
zoom: true,
// SVG-related properties
fillOpacity: true,
floodOpacity: true,
stopOpacity: true,
strokeDasharray: true,
strokeDashoffset: true,
strokeMiterlimit: true,
strokeOpacity: true,
strokeWidth: true
};
/**
* @param {string} prefix vendor-specific prefix, eg: Webkit
* @param {string} key style name, eg: transitionDuration
* @return {string} style name prefixed with `prefix`, properly camelCased, eg:
* WebkitTransitionDuration
*/
function prefixKey(prefix, key) {
return prefix + key.charAt(0).toUpperCase() + key.substring(1);
}
/**
* Support style names that may come passed in prefixed by adding permutations
* of vendor prefixes.
*/
var prefixes = ["Webkit", "ms", "Moz", "O"]; // Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
// infinite loop, because it iterates over the newly added props too.
Object.keys(isUnitlessNumber).forEach(function (prop) {
prefixes.forEach(function (prefix) {
isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
});
}); // NOTE(lmr):
// Since this is a hot code path, right now this is mutative...
// As far as I can tell, this shouldn't cause any unexpected behavior.
function mapStyle(style) {
if (style && style.transform && typeof style.transform !== "string") {
// TODO(lmr): this doesn't attempt to use vendor prefixed styles
style.transform = style.transform.map(mapTransform).join(" ");
}
return style;
}
function dangerousStyleValue(name, value, isCustomProperty) {
// Note that we've removed escapeTextForBrowser() calls here since the
// whole string will be escaped when the attribute is injected into
// the markup. If you provide unsafe user data here they can inject
// arbitrary CSS which may be problematic (I couldn't repro this):
// https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
// http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
// This is not an XSS hole but instead a potential CSS injection issue
// which has lead to a greater discussion about how we're going to
// trust URLs moving forward. See #2115901
var isEmpty = value == null || typeof value === "boolean" || value === "";
if (isEmpty) {
return "";
}
if (!isCustomProperty && typeof value === "number" && value !== 0 && !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])) {
return value + "px"; // Presumes implicit 'px' suffix for unitless numbers
}
return ("" + value).trim();
}
function setValueForStyles(node, styles) {
var style = node.style;
for (var styleName in styles) {
if (!styles.hasOwnProperty(styleName)) {
continue;
}
var isCustomProperty = styleName.indexOf("--") === 0;
var styleValue = dangerousStyleValue(styleName, styles[styleName], isCustomProperty);
if (styleName === "float") {
styleName = "cssFloat";
}
if (isCustomProperty) {
style.setProperty(styleName, styleValue);
} else {
style[styleName] = styleValue;
}
}
}
function ApplyAnimatedValues(instance, props) {
if (instance == null) return false;
if (typeof instance.setNativeProps === "function") {
instance.setNativeProps(props);
return true;
} else if (instance.nodeType && instance.setAttribute !== undefined) {
setValueForStyles(instance, mapStyle(props.style));
return true;
} else {
return false;
}
}
function transformStyles(style) {
return mapStyle(style);
}
export { ApplyAnimatedValues, transformStyles };
//# sourceMappingURL=ApplyAnimatedValues.js.map