UNPKG

framer-motion

Version:

An open source and production-ready motion library for React on the web

135 lines (118 loc) • 4.82 kB
import { MotionProps } from "../../../motion" import { ResolvedValues } from "../../types" import { LayoutState, TargetProjection } from "../../utils/state" import { valueScaleCorrection } from "../projection/scale-correction" import { DOMVisualElementOptions, HTMLMutableState } from "../types" import { buildLayoutProjectionTransform, buildLayoutProjectionTransformOrigin, buildTransform, buildTransformOrigin, } from "./build-transform" import { isCSSVariable } from "./is-css-variable" import { isTransformOriginProp, isTransformProp } from "./transform" import { getDefaultValueType, getValueAsType } from "./value-types" export function buildHTMLStyles( state: HTMLMutableState, latestValues: ResolvedValues, projection: TargetProjection, layoutState: LayoutState, options: DOMVisualElementOptions, transformTemplate?: MotionProps["transformTemplate"] ) { const { style, vars, transform, transformKeys, transformOrigin } = state // Empty the transformKeys array. As we're throwing out refs to its items // this might not be as cheap as suspected. Maybe using the array as a buffer // with a manual incrementation would be better. transformKeys.length = 0 // Track whether we encounter any transform or transformOrigin values. let hasTransform = false let hasTransformOrigin = false // Does the calculated transform essentially equal "none"? let transformIsNone = true /** * Loop over all our latest animated values and decide whether to handle them * as a style or CSS variable. * * Transforms and transform origins are kept seperately for further processing. */ for (const key in latestValues) { const value = latestValues[key] /** * If this is a CSS variable we don't do any further processing. */ if (isCSSVariable(key)) { vars[key] = value continue } // Convert the value to its default value type, ie 0 -> "0px" const valueType = getDefaultValueType(key) const valueAsType = getValueAsType(value, valueType) if (isTransformProp(key)) { // If this is a transform, flag to enable further transform processing hasTransform = true transform[key] = valueAsType transformKeys.push(key) // If we already know we have a non-default transform, early return if (!transformIsNone) continue // Otherwise check to see if this is a default transform if (value !== (valueType.default ?? 0)) transformIsNone = false } else if (isTransformOriginProp(key)) { transformOrigin[key] = valueAsType // If this is a transform origin, flag and enable further transform-origin processing hasTransformOrigin = true } else { /** * If layout projection is on, and we need to perform scale correction for this * value type, perform it. */ if (layoutState.isHydrated && valueScaleCorrection[key]) { const correctedValue = valueScaleCorrection[key].process( value, layoutState, projection ) /** * Scale-correctable values can define a number of other values to break * down into. For instance borderRadius needs applying to borderBottomLeftRadius etc */ const { applyTo } = valueScaleCorrection[key] if (applyTo) { const num = applyTo.length for (let i = 0; i < num; i++) { style[applyTo[i]] = correctedValue } } else { style[key] = correctedValue } } else { style[key] = valueAsType } } } if (projection.isEnabled && layoutState.isHydrated) { style.transform = buildLayoutProjectionTransform( layoutState.deltaFinal, layoutState.treeScale, hasTransform ? transform : undefined ) if (transformTemplate) { style.transform = transformTemplate(transform, style.transform) } style.transformOrigin = buildLayoutProjectionTransformOrigin( layoutState ) } else { if (hasTransform) { style.transform = buildTransform( state, options, transformIsNone, transformTemplate ) } if (hasTransformOrigin) { style.transformOrigin = buildTransformOrigin(transformOrigin) } } }