Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 38x 38x 38x 38x 38x 10x 38x 10x 38x 7x 7x 7x 28x 28x 28x 28x 7x 7x 7x 7x 5x 5x 3x 2x 7x 56x 38x 38x 76x | import { circOut, linear, mix, progress as calcProgress } from "popmotion" import { percent, px } from "style-value-types" import { ResolvedValues } from "../../render/types" import { EasingFunction } from "../../types" const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"] const numBorders = borders.length const asNumber = (value: string | number) => typeof value === "string" ? parseFloat(value) : value const isPx = (value: string | number) => typeof value === "number" || px.test(value) export function mixValues( target: ResolvedValues, follow: ResolvedValues, lead: ResolvedValues, progress: number, shouldCrossfadeOpacity: boolean, isOnlyMember: boolean ) { Iif (shouldCrossfadeOpacity) { target.opacity = mix( 0, // (follow?.opacity as number) ?? 0, // TODO Reinstate this if only child (lead.opacity as number) ?? 1, easeCrossfadeIn(progress) ) target.opacityExit = mix( (follow.opacity as number) ?? 1, 0, easeCrossfadeOut(progress) ) } else Iif (isOnlyMember) { target.opacity = mix( (follow.opacity as number) ?? 1, (lead.opacity as number) ?? 1, progress ) } /** * Mix border radius */ for (let i = 0; i < numBorders; i++) { const borderLabel = `border${borders[i]}Radius` let followRadius = getRadius(follow, borderLabel) let leadRadius = getRadius(lead, borderLabel) if (followRadius === undefined && leadRadius === undefined) continue followRadius ||= 0 leadRadius ||= 0 const canMix = followRadius === 0 || leadRadius === 0 || isPx(followRadius) === isPx(leadRadius) if (canMix) { target[borderLabel] = Math.max( mix(asNumber(followRadius), asNumber(leadRadius), progress), 0 ) if (percent.test(leadRadius) || percent.test(followRadius)) { target[borderLabel] += "%" } } else { target[borderLabel] = leadRadius } } /** * Mix rotation */ Iif (follow.rotate || lead.rotate) { target.rotate = mix( (follow.rotate as number) || 0, (lead.rotate as number) || 0, progress ) } } function getRadius(values: ResolvedValues, radiusName: string) { return values[radiusName] ?? values.borderRadius } // /** // * We only want to mix the background color if there's a follow element // * that we're not crossfading opacity between. For instance with switch // * AnimateSharedLayout animations, this helps the illusion of a continuous // * element being animated but also cuts down on the number of paints triggered // * for elements where opacity is doing that work for us. // */ // if ( // !hasFollowElement && // latestLeadValues.backgroundColor && // latestFollowValues.backgroundColor // ) { // /** // * This isn't ideal performance-wise as mixColor is creating a new function every frame. // * We could probably create a mixer that runs at the start of the animation but // * the idea behind the crossfader is that it runs dynamically between two potentially // * changing targets (ie opacity or borderRadius may be animating independently via variants) // */ // leadState.backgroundColor = followState.backgroundColor = mixColor( // latestFollowValues.backgroundColor as string, // latestLeadValues.backgroundColor as string // )(p) // } const easeCrossfadeIn = compress(0, 0.5, circOut) const easeCrossfadeOut = compress(0.5, 0.95, linear) function compress( min: number, max: number, easing: EasingFunction ): EasingFunction { return (p: number) => { // Could replace ifs with clamp if (p < min) return 0 if (p > max) return 1 return easing(calcProgress(min, max, p)) } } |