motion 12.8.0 → 12.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +92 -89
- package/dist/cjs/mini.js +6 -1
- package/dist/cjs/react-client.js +106 -184
- package/dist/cjs/react-m.js +17 -141
- package/dist/cjs/react-mini.js +6 -1
- package/dist/es/framer-motion/dist/es/animation/interfaces/visual-element-target.mjs +11 -0
- package/dist/es/framer-motion/dist/es/motion/utils/use-visual-state.mjs +1 -10
- package/dist/es/framer-motion/dist/es/render/VisualElement.mjs +1 -3
- package/dist/es/framer-motion/dist/es/render/svg/SVGVisualElement.mjs +0 -12
- package/dist/es/framer-motion/dist/es/render/svg/config-motion.mjs +0 -45
- package/dist/es/framer-motion/dist/es/render/svg/utils/build-attrs.mjs +16 -11
- package/dist/es/framer-motion/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/motion/lib/index.mjs +1 -0
- package/dist/es/motion/lib/react.mjs +1 -0
- package/dist/es/motion-dom/dist/es/animation/AsyncMotionValueAnimation.mjs +2 -7
- package/dist/es/motion-dom/dist/es/animation/JSAnimation.mjs +1 -1
- package/dist/es/motion-dom/dist/es/animation/NativeAnimation.mjs +6 -1
- package/dist/es/motion-dom/dist/es/effects/style-effect.mjs +36 -0
- package/dist/es/motion-dom/dist/es/value/index.mjs +5 -7
- package/dist/motion.dev.js +92 -89
- package/dist/motion.js +1 -1
- package/package.json +3 -3
- package/dist/es/framer-motion/dist/es/render/svg/utils/measure.mjs +0 -19
- package/dist/es/framer-motion/dist/es/render/svg/utils/transform-origin.mjs +0 -18
package/dist/cjs/react-m.js
CHANGED
|
@@ -30,9 +30,6 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
30
30
|
|
|
31
31
|
const MotionGlobalConfig = {};
|
|
32
32
|
|
|
33
|
-
/*#__NO_SIDE_EFFECTS__*/
|
|
34
|
-
const noop = (any) => any;
|
|
35
|
-
|
|
36
33
|
const LayoutGroupContext = react.createContext({});
|
|
37
34
|
|
|
38
35
|
const LazyContext = react.createContext({ strict: false });
|
|
@@ -352,8 +349,6 @@ function createRenderBatcher(scheduleNextBatch, allowKeepAlive) {
|
|
|
352
349
|
return { schedule, cancel, state, steps };
|
|
353
350
|
}
|
|
354
351
|
|
|
355
|
-
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
|
|
356
|
-
|
|
357
352
|
const checkStringStartsWith = (token) => (key) => typeof key === "string" && key.startsWith(token);
|
|
358
353
|
const isCSSVariableName =
|
|
359
354
|
/*@__PURE__*/ checkStringStartsWith("--");
|
|
@@ -1111,25 +1106,10 @@ function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true
|
|
|
1111
1106
|
attrs[keys.array] = `${pathLength} ${pathSpacing}`;
|
|
1112
1107
|
}
|
|
1113
1108
|
|
|
1114
|
-
function calcOrigin(origin, offset, size) {
|
|
1115
|
-
return typeof origin === "string"
|
|
1116
|
-
? origin
|
|
1117
|
-
: px.transform(offset + size * origin);
|
|
1118
|
-
}
|
|
1119
|
-
/**
|
|
1120
|
-
* The SVG transform origin defaults are different to CSS and is less intuitive,
|
|
1121
|
-
* so we use the measured dimensions of the SVG to reconcile these.
|
|
1122
|
-
*/
|
|
1123
|
-
function calcSVGTransformOrigin(dimensions, originX, originY) {
|
|
1124
|
-
const pxOriginX = calcOrigin(originX, dimensions.x, dimensions.width);
|
|
1125
|
-
const pxOriginY = calcOrigin(originY, dimensions.y, dimensions.height);
|
|
1126
|
-
return `${pxOriginX} ${pxOriginY}`;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
1109
|
/**
|
|
1130
1110
|
* Build SVG visual attrbutes, like cx and style.transform
|
|
1131
1111
|
*/
|
|
1132
|
-
function buildSVGAttrs(state, { attrX, attrY, attrScale,
|
|
1112
|
+
function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0,
|
|
1133
1113
|
// This is object creation, which we try to avoid per-frame.
|
|
1134
1114
|
...latest }, isSVGTag, transformTemplate) {
|
|
1135
1115
|
buildHTMLStyles(state, latest, transformTemplate);
|
|
@@ -1145,20 +1125,26 @@ function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathL
|
|
|
1145
1125
|
}
|
|
1146
1126
|
state.attrs = state.style;
|
|
1147
1127
|
state.style = {};
|
|
1148
|
-
const { attrs, style
|
|
1128
|
+
const { attrs, style } = state;
|
|
1149
1129
|
/**
|
|
1150
|
-
* However, we apply transforms as CSS transforms.
|
|
1151
|
-
* and copy it into style.
|
|
1130
|
+
* However, we apply transforms as CSS transforms.
|
|
1131
|
+
* So if we detect a transform, transformOrigin we take it from attrs and copy it into style.
|
|
1152
1132
|
*/
|
|
1153
1133
|
if (attrs.transform) {
|
|
1154
|
-
|
|
1155
|
-
style.transform = attrs.transform;
|
|
1134
|
+
style.transform = attrs.transform;
|
|
1156
1135
|
delete attrs.transform;
|
|
1157
1136
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1137
|
+
if (style.transform || attrs.transformOrigin) {
|
|
1138
|
+
style.transformOrigin = attrs.transformOrigin ?? "50% 50%";
|
|
1139
|
+
delete attrs.transformOrigin;
|
|
1140
|
+
}
|
|
1141
|
+
if (style.transform) {
|
|
1142
|
+
/**
|
|
1143
|
+
* SVG's element transform-origin uses its own median as a reference.
|
|
1144
|
+
* Therefore, transformBox becomes a fill-box
|
|
1145
|
+
*/
|
|
1146
|
+
style.transformBox = "fill-box";
|
|
1147
|
+
delete attrs.transformBox;
|
|
1162
1148
|
}
|
|
1163
1149
|
// Render attrX/attrY/attrScale as attributes
|
|
1164
1150
|
if (attrX !== undefined)
|
|
@@ -1281,20 +1267,11 @@ function resolveMotionValue(value) {
|
|
|
1281
1267
|
return isMotionValue(value) ? value.get() : value;
|
|
1282
1268
|
}
|
|
1283
1269
|
|
|
1284
|
-
function makeState({ scrapeMotionValuesFromProps, createRenderState,
|
|
1270
|
+
function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) {
|
|
1285
1271
|
const state = {
|
|
1286
1272
|
latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps),
|
|
1287
1273
|
renderState: createRenderState(),
|
|
1288
1274
|
};
|
|
1289
|
-
if (onUpdate) {
|
|
1290
|
-
/**
|
|
1291
|
-
* onMount works without the VisualElement because it could be
|
|
1292
|
-
* called before the VisualElement payload has been hydrated.
|
|
1293
|
-
* (e.g. if someone is using m components <m.circle />)
|
|
1294
|
-
*/
|
|
1295
|
-
state.onMount = (instance) => onUpdate({ props, current: instance, ...state });
|
|
1296
|
-
state.onUpdate = (visualElement) => onUpdate(visualElement);
|
|
1297
|
-
}
|
|
1298
1275
|
return state;
|
|
1299
1276
|
}
|
|
1300
1277
|
const makeUseVisualState = (config) => (props, isStatic) => {
|
|
@@ -1381,68 +1358,6 @@ const htmlMotionConfig = {
|
|
|
1381
1358
|
}),
|
|
1382
1359
|
};
|
|
1383
1360
|
|
|
1384
|
-
function updateSVGDimensions(instance, renderState) {
|
|
1385
|
-
try {
|
|
1386
|
-
renderState.dimensions =
|
|
1387
|
-
typeof instance.getBBox === "function"
|
|
1388
|
-
? instance.getBBox()
|
|
1389
|
-
: instance.getBoundingClientRect();
|
|
1390
|
-
}
|
|
1391
|
-
catch (e) {
|
|
1392
|
-
// Most likely trying to measure an unrendered element under Firefox
|
|
1393
|
-
renderState.dimensions = {
|
|
1394
|
-
x: 0,
|
|
1395
|
-
y: 0,
|
|
1396
|
-
width: 0,
|
|
1397
|
-
height: 0,
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
|
-
function renderHTML(element, { style, vars }, styleProp, projection) {
|
|
1403
|
-
Object.assign(element.style, style, projection && projection.getProjectionStyles(styleProp));
|
|
1404
|
-
// Loop over any CSS variables and assign those.
|
|
1405
|
-
for (const key in vars) {
|
|
1406
|
-
element.style.setProperty(key, vars[key]);
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
/**
|
|
1411
|
-
* A set of attribute names that are always read/written as camel case.
|
|
1412
|
-
*/
|
|
1413
|
-
const camelCaseAttributes = new Set([
|
|
1414
|
-
"baseFrequency",
|
|
1415
|
-
"diffuseConstant",
|
|
1416
|
-
"kernelMatrix",
|
|
1417
|
-
"kernelUnitLength",
|
|
1418
|
-
"keySplines",
|
|
1419
|
-
"keyTimes",
|
|
1420
|
-
"limitingConeAngle",
|
|
1421
|
-
"markerHeight",
|
|
1422
|
-
"markerWidth",
|
|
1423
|
-
"numOctaves",
|
|
1424
|
-
"targetX",
|
|
1425
|
-
"targetY",
|
|
1426
|
-
"surfaceScale",
|
|
1427
|
-
"specularConstant",
|
|
1428
|
-
"specularExponent",
|
|
1429
|
-
"stdDeviation",
|
|
1430
|
-
"tableValues",
|
|
1431
|
-
"viewBox",
|
|
1432
|
-
"gradientTransform",
|
|
1433
|
-
"pathLength",
|
|
1434
|
-
"startOffset",
|
|
1435
|
-
"textLength",
|
|
1436
|
-
"lengthAdjust",
|
|
1437
|
-
]);
|
|
1438
|
-
|
|
1439
|
-
function renderSVG(element, renderState, _styleProp, projection) {
|
|
1440
|
-
renderHTML(element, renderState, undefined, projection);
|
|
1441
|
-
for (const key in renderState.attrs) {
|
|
1442
|
-
element.setAttribute(!camelCaseAttributes.has(key) ? camelToDash(key) : key, renderState.attrs[key]);
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
1361
|
function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
1447
1362
|
const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
|
|
1448
1363
|
for (const key in props) {
|
|
@@ -1457,49 +1372,10 @@ function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
|
1457
1372
|
return newValues;
|
|
1458
1373
|
}
|
|
1459
1374
|
|
|
1460
|
-
const layoutProps = ["x", "y", "width", "height", "cx", "cy", "r"];
|
|
1461
1375
|
const svgMotionConfig = {
|
|
1462
1376
|
useVisualState: makeUseVisualState({
|
|
1463
1377
|
scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
|
|
1464
1378
|
createRenderState: createSvgRenderState,
|
|
1465
|
-
onUpdate: ({ props, prevProps, current, renderState, latestValues, }) => {
|
|
1466
|
-
if (!current)
|
|
1467
|
-
return;
|
|
1468
|
-
let hasTransform = !!props.drag;
|
|
1469
|
-
if (!hasTransform) {
|
|
1470
|
-
for (const key in latestValues) {
|
|
1471
|
-
if (transformProps.has(key)) {
|
|
1472
|
-
hasTransform = true;
|
|
1473
|
-
break;
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
if (!hasTransform)
|
|
1478
|
-
return;
|
|
1479
|
-
let needsMeasure = !prevProps;
|
|
1480
|
-
if (prevProps) {
|
|
1481
|
-
/**
|
|
1482
|
-
* Check the layout props for changes, if any are found we need to
|
|
1483
|
-
* measure the element again.
|
|
1484
|
-
*/
|
|
1485
|
-
for (let i = 0; i < layoutProps.length; i++) {
|
|
1486
|
-
const key = layoutProps[i];
|
|
1487
|
-
if (props[key] !==
|
|
1488
|
-
prevProps[key]) {
|
|
1489
|
-
needsMeasure = true;
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
if (!needsMeasure)
|
|
1494
|
-
return;
|
|
1495
|
-
frame.read(() => {
|
|
1496
|
-
updateSVGDimensions(current, renderState);
|
|
1497
|
-
frame.render(() => {
|
|
1498
|
-
buildSVGAttrs(renderState, latestValues, isSVGTag(current.tagName), props.transformTemplate);
|
|
1499
|
-
renderSVG(current, renderState);
|
|
1500
|
-
});
|
|
1501
|
-
});
|
|
1502
|
-
},
|
|
1503
1379
|
}),
|
|
1504
1380
|
};
|
|
1505
1381
|
|
package/dist/cjs/react-mini.js
CHANGED
|
@@ -231,7 +231,7 @@ class NativeAnimation extends WithPromise {
|
|
|
231
231
|
this.isStopped = false;
|
|
232
232
|
if (!options)
|
|
233
233
|
return;
|
|
234
|
-
const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, } = options;
|
|
234
|
+
const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
|
|
235
235
|
this.isPseudoElement = Boolean(pseudoElement);
|
|
236
236
|
this.allowFlatten = allowFlatten;
|
|
237
237
|
this.options = options;
|
|
@@ -257,8 +257,13 @@ class NativeAnimation extends WithPromise {
|
|
|
257
257
|
}
|
|
258
258
|
this.animation.cancel();
|
|
259
259
|
}
|
|
260
|
+
onComplete?.();
|
|
260
261
|
this.notifyFinished();
|
|
261
262
|
};
|
|
263
|
+
/**
|
|
264
|
+
* TODO: In a breaking change, we should replace this with `.notifyCancel()`
|
|
265
|
+
*/
|
|
266
|
+
this.animation.oncancel = () => this.notifyFinished();
|
|
262
267
|
}
|
|
263
268
|
play() {
|
|
264
269
|
if (this.isStopped)
|
|
@@ -37,6 +37,17 @@ function animateTarget(visualElement, targetAndTransition, { delay = 0, transiti
|
|
|
37
37
|
delay,
|
|
38
38
|
...getValueTransition(transition || {}, key),
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* If the value is already at the defined target, skip the animation.
|
|
42
|
+
*/
|
|
43
|
+
const currentValue = value.get();
|
|
44
|
+
if (currentValue !== undefined &&
|
|
45
|
+
!value.isAnimating &&
|
|
46
|
+
!Array.isArray(valueTarget) &&
|
|
47
|
+
valueTarget === currentValue &&
|
|
48
|
+
!valueTransition.velocity) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
40
51
|
/**
|
|
41
52
|
* If this is the first time a value is being animated, check
|
|
42
53
|
* to see if we're handling off from an existing animation.
|
|
@@ -7,20 +7,11 @@ import { resolveVariantFromProps } from '../../render/utils/resolve-variants.mjs
|
|
|
7
7
|
import { useConstant } from '../../utils/use-constant.mjs';
|
|
8
8
|
import { resolveMotionValue } from '../../value/utils/resolve-motion-value.mjs';
|
|
9
9
|
|
|
10
|
-
function makeState({ scrapeMotionValuesFromProps, createRenderState,
|
|
10
|
+
function makeState({ scrapeMotionValuesFromProps, createRenderState, }, props, context, presenceContext) {
|
|
11
11
|
const state = {
|
|
12
12
|
latestValues: makeLatestValues(props, context, presenceContext, scrapeMotionValuesFromProps),
|
|
13
13
|
renderState: createRenderState(),
|
|
14
14
|
};
|
|
15
|
-
if (onUpdate) {
|
|
16
|
-
/**
|
|
17
|
-
* onMount works without the VisualElement because it could be
|
|
18
|
-
* called before the VisualElement payload has been hydrated.
|
|
19
|
-
* (e.g. if someone is using m components <m.circle />)
|
|
20
|
-
*/
|
|
21
|
-
state.onMount = (instance) => onUpdate({ props, current: instance, ...state });
|
|
22
|
-
state.onUpdate = (visualElement) => onUpdate(visualElement);
|
|
23
|
-
}
|
|
24
15
|
return state;
|
|
25
16
|
}
|
|
26
17
|
const makeUseVisualState = (config) => (props, isStatic) => {
|
|
@@ -114,8 +114,7 @@ class VisualElement {
|
|
|
114
114
|
frame.render(this.render, false, true);
|
|
115
115
|
}
|
|
116
116
|
};
|
|
117
|
-
const { latestValues, renderState
|
|
118
|
-
this.onUpdate = onUpdate;
|
|
117
|
+
const { latestValues, renderState } = visualState;
|
|
119
118
|
this.latestValues = latestValues;
|
|
120
119
|
this.baseTarget = { ...latestValues };
|
|
121
120
|
this.initialValues = props.initial ? { ...latestValues } : {};
|
|
@@ -317,7 +316,6 @@ class VisualElement {
|
|
|
317
316
|
if (this.handleChildMotionValue) {
|
|
318
317
|
this.handleChildMotionValue();
|
|
319
318
|
}
|
|
320
|
-
this.onUpdate && this.onUpdate(this);
|
|
321
319
|
}
|
|
322
320
|
getProps() {
|
|
323
321
|
return this.props;
|
|
@@ -4,12 +4,10 @@ import { camelToDash } from '../dom/utils/camel-to-dash.mjs';
|
|
|
4
4
|
import { buildSVGAttrs } from './utils/build-attrs.mjs';
|
|
5
5
|
import { camelCaseAttributes } from './utils/camel-case-attrs.mjs';
|
|
6
6
|
import { isSVGTag } from './utils/is-svg-tag.mjs';
|
|
7
|
-
import { updateSVGDimensions } from './utils/measure.mjs';
|
|
8
7
|
import { renderSVG } from './utils/render.mjs';
|
|
9
8
|
import { scrapeMotionValuesFromProps } from './utils/scrape-motion-values.mjs';
|
|
10
9
|
import { transformProps } from '../../../../../motion-dom/dist/es/render/utils/keys-transform.mjs';
|
|
11
10
|
import { getDefaultValueType } from '../../../../../motion-dom/dist/es/value/types/maps/defaults.mjs';
|
|
12
|
-
import { frame } from '../../../../../motion-dom/dist/es/frameloop/frame.mjs';
|
|
13
11
|
|
|
14
12
|
class SVGVisualElement extends DOMVisualElement {
|
|
15
13
|
constructor() {
|
|
@@ -17,11 +15,6 @@ class SVGVisualElement extends DOMVisualElement {
|
|
|
17
15
|
this.type = "svg";
|
|
18
16
|
this.isSVGTag = false;
|
|
19
17
|
this.measureInstanceViewportBox = createBox;
|
|
20
|
-
this.updateDimensions = () => {
|
|
21
|
-
if (this.current && !this.renderState.dimensions) {
|
|
22
|
-
updateSVGDimensions(this.current, this.renderState);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
18
|
}
|
|
26
19
|
getBaseTargetFromProps(props, key) {
|
|
27
20
|
return props[key];
|
|
@@ -37,11 +30,6 @@ class SVGVisualElement extends DOMVisualElement {
|
|
|
37
30
|
scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
38
31
|
return scrapeMotionValuesFromProps(props, prevProps, visualElement);
|
|
39
32
|
}
|
|
40
|
-
onBindTransform() {
|
|
41
|
-
if (this.current && !this.renderState.dimensions) {
|
|
42
|
-
frame.postRender(this.updateDimensions);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
33
|
build(renderState, latestValues, props) {
|
|
46
34
|
buildSVGAttrs(renderState, latestValues, this.isSVGTag, props.transformTemplate);
|
|
47
35
|
}
|
|
@@ -1,56 +1,11 @@
|
|
|
1
1
|
import { makeUseVisualState } from '../../motion/utils/use-visual-state.mjs';
|
|
2
|
-
import { buildSVGAttrs } from './utils/build-attrs.mjs';
|
|
3
2
|
import { createSvgRenderState } from './utils/create-render-state.mjs';
|
|
4
|
-
import { isSVGTag } from './utils/is-svg-tag.mjs';
|
|
5
|
-
import { updateSVGDimensions } from './utils/measure.mjs';
|
|
6
|
-
import { renderSVG } from './utils/render.mjs';
|
|
7
3
|
import { scrapeMotionValuesFromProps } from './utils/scrape-motion-values.mjs';
|
|
8
|
-
import { transformProps } from '../../../../../motion-dom/dist/es/render/utils/keys-transform.mjs';
|
|
9
|
-
import { frame } from '../../../../../motion-dom/dist/es/frameloop/frame.mjs';
|
|
10
4
|
|
|
11
|
-
const layoutProps = ["x", "y", "width", "height", "cx", "cy", "r"];
|
|
12
5
|
const svgMotionConfig = {
|
|
13
6
|
useVisualState: makeUseVisualState({
|
|
14
7
|
scrapeMotionValuesFromProps: scrapeMotionValuesFromProps,
|
|
15
8
|
createRenderState: createSvgRenderState,
|
|
16
|
-
onUpdate: ({ props, prevProps, current, renderState, latestValues, }) => {
|
|
17
|
-
if (!current)
|
|
18
|
-
return;
|
|
19
|
-
let hasTransform = !!props.drag;
|
|
20
|
-
if (!hasTransform) {
|
|
21
|
-
for (const key in latestValues) {
|
|
22
|
-
if (transformProps.has(key)) {
|
|
23
|
-
hasTransform = true;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (!hasTransform)
|
|
29
|
-
return;
|
|
30
|
-
let needsMeasure = !prevProps;
|
|
31
|
-
if (prevProps) {
|
|
32
|
-
/**
|
|
33
|
-
* Check the layout props for changes, if any are found we need to
|
|
34
|
-
* measure the element again.
|
|
35
|
-
*/
|
|
36
|
-
for (let i = 0; i < layoutProps.length; i++) {
|
|
37
|
-
const key = layoutProps[i];
|
|
38
|
-
if (props[key] !==
|
|
39
|
-
prevProps[key]) {
|
|
40
|
-
needsMeasure = true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (!needsMeasure)
|
|
45
|
-
return;
|
|
46
|
-
frame.read(() => {
|
|
47
|
-
updateSVGDimensions(current, renderState);
|
|
48
|
-
frame.render(() => {
|
|
49
|
-
buildSVGAttrs(renderState, latestValues, isSVGTag(current.tagName), props.transformTemplate);
|
|
50
|
-
renderSVG(current, renderState);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
},
|
|
54
9
|
}),
|
|
55
10
|
};
|
|
56
11
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { buildHTMLStyles } from '../../html/utils/build-styles.mjs';
|
|
2
2
|
import { buildSVGPath } from './path.mjs';
|
|
3
|
-
import { calcSVGTransformOrigin } from './transform-origin.mjs';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Build SVG visual attrbutes, like cx and style.transform
|
|
7
6
|
*/
|
|
8
|
-
function buildSVGAttrs(state, { attrX, attrY, attrScale,
|
|
7
|
+
function buildSVGAttrs(state, { attrX, attrY, attrScale, pathLength, pathSpacing = 1, pathOffset = 0,
|
|
9
8
|
// This is object creation, which we try to avoid per-frame.
|
|
10
9
|
...latest }, isSVGTag, transformTemplate) {
|
|
11
10
|
buildHTMLStyles(state, latest, transformTemplate);
|
|
@@ -21,20 +20,26 @@ function buildSVGAttrs(state, { attrX, attrY, attrScale, originX, originY, pathL
|
|
|
21
20
|
}
|
|
22
21
|
state.attrs = state.style;
|
|
23
22
|
state.style = {};
|
|
24
|
-
const { attrs, style
|
|
23
|
+
const { attrs, style } = state;
|
|
25
24
|
/**
|
|
26
|
-
* However, we apply transforms as CSS transforms.
|
|
27
|
-
* and copy it into style.
|
|
25
|
+
* However, we apply transforms as CSS transforms.
|
|
26
|
+
* So if we detect a transform, transformOrigin we take it from attrs and copy it into style.
|
|
28
27
|
*/
|
|
29
28
|
if (attrs.transform) {
|
|
30
|
-
|
|
31
|
-
style.transform = attrs.transform;
|
|
29
|
+
style.transform = attrs.transform;
|
|
32
30
|
delete attrs.transform;
|
|
33
31
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
if (style.transform || attrs.transformOrigin) {
|
|
33
|
+
style.transformOrigin = attrs.transformOrigin ?? "50% 50%";
|
|
34
|
+
delete attrs.transformOrigin;
|
|
35
|
+
}
|
|
36
|
+
if (style.transform) {
|
|
37
|
+
/**
|
|
38
|
+
* SVG's element transform-origin uses its own median as a reference.
|
|
39
|
+
* Therefore, transformBox becomes a fill-box
|
|
40
|
+
*/
|
|
41
|
+
style.transformBox = "fill-box";
|
|
42
|
+
delete attrs.transformBox;
|
|
38
43
|
}
|
|
39
44
|
// Render attrX/attrY/attrScale as attributes
|
|
40
45
|
if (attrX !== undefined)
|
|
@@ -17,7 +17,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
17
17
|
* and warn against mismatches.
|
|
18
18
|
*/
|
|
19
19
|
if (process.env.NODE_ENV === "development") {
|
|
20
|
-
warnOnce(nextValue.version === "12.
|
|
20
|
+
warnOnce(nextValue.version === "12.9.0", `Attempting to mix Motion versions ${nextValue.version} with 12.9.0 may not work as expected.`);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
else if (isMotionValue(prevValue)) {
|
|
@@ -39,6 +39,7 @@ export { supportsPartialKeyframes } from '../../motion-dom/dist/es/animation/waa
|
|
|
39
39
|
export { supportsBrowserAnimation } from '../../motion-dom/dist/es/animation/waapi/supports/waapi.mjs';
|
|
40
40
|
export { acceleratedValues } from '../../motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs';
|
|
41
41
|
export { generateLinearEasing } from '../../motion-dom/dist/es/animation/waapi/utils/linear.mjs';
|
|
42
|
+
export { styleEffect } from '../../motion-dom/dist/es/effects/style-effect.mjs';
|
|
42
43
|
export { createRenderBatcher } from '../../motion-dom/dist/es/frameloop/batcher.mjs';
|
|
43
44
|
export { cancelMicrotask, microtask } from '../../motion-dom/dist/es/frameloop/microtask.mjs';
|
|
44
45
|
export { time } from '../../motion-dom/dist/es/frameloop/sync-time.mjs';
|
|
@@ -138,6 +138,7 @@ export { supportsPartialKeyframes } from '../../motion-dom/dist/es/animation/waa
|
|
|
138
138
|
export { supportsBrowserAnimation } from '../../motion-dom/dist/es/animation/waapi/supports/waapi.mjs';
|
|
139
139
|
export { acceleratedValues } from '../../motion-dom/dist/es/animation/waapi/utils/accelerated-values.mjs';
|
|
140
140
|
export { generateLinearEasing } from '../../motion-dom/dist/es/animation/waapi/utils/linear.mjs';
|
|
141
|
+
export { styleEffect } from '../../motion-dom/dist/es/effects/style-effect.mjs';
|
|
141
142
|
export { createRenderBatcher } from '../../motion-dom/dist/es/frameloop/batcher.mjs';
|
|
142
143
|
export { cancelMicrotask, microtask } from '../../motion-dom/dist/es/frameloop/microtask.mjs';
|
|
143
144
|
export { time } from '../../motion-dom/dist/es/frameloop/sync-time.mjs';
|
|
@@ -52,7 +52,7 @@ class AsyncMotionValueAnimation extends WithPromise {
|
|
|
52
52
|
}
|
|
53
53
|
onKeyframesResolved(keyframes, finalKeyframe, options, sync) {
|
|
54
54
|
this.keyframeResolver = undefined;
|
|
55
|
-
const { name, type, velocity, delay, isHandoff, onUpdate
|
|
55
|
+
const { name, type, velocity, delay, isHandoff, onUpdate } = options;
|
|
56
56
|
this.resolvedAt = time.now();
|
|
57
57
|
/**
|
|
58
58
|
* If we can't animate this value with the resolved keyframes
|
|
@@ -102,12 +102,7 @@ class AsyncMotionValueAnimation extends WithPromise {
|
|
|
102
102
|
element: resolvedOptions.motionValue.owner.current,
|
|
103
103
|
})
|
|
104
104
|
: new JSAnimation(resolvedOptions);
|
|
105
|
-
animation.finished
|
|
106
|
-
.then(() => {
|
|
107
|
-
onComplete?.();
|
|
108
|
-
this.notifyFinished();
|
|
109
|
-
})
|
|
110
|
-
.catch(noop);
|
|
105
|
+
animation.finished.then(() => this.notifyFinished()).catch(noop);
|
|
111
106
|
if (this.pendingTimeline) {
|
|
112
107
|
this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
|
|
113
108
|
this.pendingTimeline = undefined;
|
|
@@ -302,7 +302,6 @@ class JSAnimation extends WithPromise {
|
|
|
302
302
|
this.holdTime = null;
|
|
303
303
|
}
|
|
304
304
|
finish() {
|
|
305
|
-
this.notifyFinished();
|
|
306
305
|
this.teardown();
|
|
307
306
|
this.state = "finished";
|
|
308
307
|
const { onComplete } = this.options;
|
|
@@ -315,6 +314,7 @@ class JSAnimation extends WithPromise {
|
|
|
315
314
|
this.teardown();
|
|
316
315
|
}
|
|
317
316
|
teardown() {
|
|
317
|
+
this.notifyFinished();
|
|
318
318
|
this.state = "idle";
|
|
319
319
|
this.stopDriver();
|
|
320
320
|
this.startTime = this.holdTime = null;
|
|
@@ -18,7 +18,7 @@ class NativeAnimation extends WithPromise {
|
|
|
18
18
|
this.isStopped = false;
|
|
19
19
|
if (!options)
|
|
20
20
|
return;
|
|
21
|
-
const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, } = options;
|
|
21
|
+
const { element, name, keyframes, pseudoElement, allowFlatten = false, finalKeyframe, onComplete, } = options;
|
|
22
22
|
this.isPseudoElement = Boolean(pseudoElement);
|
|
23
23
|
this.allowFlatten = allowFlatten;
|
|
24
24
|
this.options = options;
|
|
@@ -44,8 +44,13 @@ class NativeAnimation extends WithPromise {
|
|
|
44
44
|
}
|
|
45
45
|
this.animation.cancel();
|
|
46
46
|
}
|
|
47
|
+
onComplete?.();
|
|
47
48
|
this.notifyFinished();
|
|
48
49
|
};
|
|
50
|
+
/**
|
|
51
|
+
* TODO: In a breaking change, we should replace this with `.notifyCancel()`
|
|
52
|
+
*/
|
|
53
|
+
this.animation.oncancel = () => this.notifyFinished();
|
|
49
54
|
}
|
|
50
55
|
play() {
|
|
51
56
|
if (this.isStopped)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { resolveElements } from '../utils/resolve-elements.mjs';
|
|
2
|
+
import { frame, cancelFrame } from '../frameloop/frame.mjs';
|
|
3
|
+
|
|
4
|
+
function styleEffect(subject, values) {
|
|
5
|
+
const elements = resolveElements(subject);
|
|
6
|
+
const subscriptions = [];
|
|
7
|
+
for (let i = 0; i < elements.length; i++) {
|
|
8
|
+
const element = elements[i];
|
|
9
|
+
for (const key in values) {
|
|
10
|
+
const value = values[key];
|
|
11
|
+
/**
|
|
12
|
+
* TODO: Get specific setters for combined props (like x)
|
|
13
|
+
* or values with default types (like color)
|
|
14
|
+
*
|
|
15
|
+
* TODO: CSS variable support
|
|
16
|
+
*/
|
|
17
|
+
const updateStyle = () => {
|
|
18
|
+
element.style[key] = value.get();
|
|
19
|
+
};
|
|
20
|
+
const scheduleUpdate = () => frame.render(updateStyle);
|
|
21
|
+
const cancel = value.on("change", scheduleUpdate);
|
|
22
|
+
scheduleUpdate();
|
|
23
|
+
subscriptions.push(() => {
|
|
24
|
+
cancel();
|
|
25
|
+
cancelFrame(updateStyle);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return () => {
|
|
30
|
+
for (const cancel of subscriptions) {
|
|
31
|
+
cancel();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { styleEffect };
|
|
@@ -32,7 +32,7 @@ class MotionValue {
|
|
|
32
32
|
* This will be replaced by the build step with the latest version number.
|
|
33
33
|
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
34
34
|
*/
|
|
35
|
-
this.version = "12.
|
|
35
|
+
this.version = "12.9.0";
|
|
36
36
|
/**
|
|
37
37
|
* Tracks whether this value can output a velocity. Currently this is only true
|
|
38
38
|
* if the value is numerical, but we might be able to widen the scope here and support
|
|
@@ -58,12 +58,12 @@ class MotionValue {
|
|
|
58
58
|
this.prev = this.current;
|
|
59
59
|
this.setCurrent(v);
|
|
60
60
|
// Update update subscribers
|
|
61
|
-
if (this.current !== this.prev
|
|
62
|
-
this.events.change
|
|
61
|
+
if (this.current !== this.prev) {
|
|
62
|
+
this.events.change?.notify(this.current);
|
|
63
63
|
}
|
|
64
64
|
// Update render subscribers
|
|
65
|
-
if (render
|
|
66
|
-
this.events.renderRequest
|
|
65
|
+
if (render) {
|
|
66
|
+
this.events.renderRequest?.notify(this.current);
|
|
67
67
|
}
|
|
68
68
|
};
|
|
69
69
|
this.hasAnimated = false;
|
|
@@ -176,8 +176,6 @@ class MotionValue {
|
|
|
176
176
|
* @public
|
|
177
177
|
*/
|
|
178
178
|
set(v, render = true) {
|
|
179
|
-
if (v === "none")
|
|
180
|
-
console.trace();
|
|
181
179
|
if (!render || !this.passiveEffect) {
|
|
182
180
|
this.updateAndNotify(v, render);
|
|
183
181
|
}
|