@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering
190 lines • 8.12 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeyframeEffect = void 0;
const common_1 = require("../../common");
const util_1 = require("../../common/animation/util");
const utils_1 = require("./utils");
/**
* Web Animation API 的 KeyframeEffect 实现,功能完善实现中
* 参考: https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect
*/
class KeyframeEffect {
constructor(target, keyframes, options) {
// biome-ignore lint/suspicious/noExplicitAny: <属性类型存在多种可能>
this._originProps = {};
this._target = target;
this._options = common_1.NumberExt.isNumber(options)
? { duration: options }
: Object.assign({}, options);
this.setKeyframes(keyframes);
}
get target() {
return this._target;
}
getKeyframes() {
if (!this._keyframes || this._keyframes.length === 0)
return [];
// 标准化关键帧数据
let normalizedFrames = [];
if (Array.isArray(this._keyframes)) {
normalizedFrames = [...this._keyframes];
}
// 处理对象形式的关键帧,eg: {'position/x': [0, 100],'position/y': 100 }
if (common_1.ObjectExt.isPlainObject(this._keyframes)) {
const frameValues = Object.values(this._keyframes);
const frameValuesArr = frameValues.map((subArr) => common_1.ArrayExt.castArray(subArr).length);
const maxFramesLength = Math.max(...frameValuesArr);
for (let i = 0; i < maxFramesLength; i++) {
const frame = {};
Object.entries(this._keyframes).forEach(([prop, value]) => {
const v = common_1.ArrayExt.castArray(value)[i];
if ((0, utils_1.isNotReservedWord)(prop) && v != null) {
frame[prop] = v;
}
});
normalizedFrames.push(frame);
}
}
normalizedFrames = normalizedFrames.map((keyframe, index, arr) => {
var _a, _b, _c;
const frame = keyframe !== null && keyframe !== void 0 ? keyframe : {};
// biome-ignore lint/suspicious/noExplicitAny: <属性类型存在多种可能>
const normalized = {};
normalized.offset = frame.offset;
// 确保每个关键帧都有 easing
normalized.easing =
(_c = (_a = frame.easing) !== null && _a !== void 0 ? _a : (_b = arr[index - 1]) === null || _b === void 0 ? void 0 : _b.easing) !== null && _c !== void 0 ? _c : this.getComputedTiming().easing;
// 复制其他属性
Object.keys(frame).forEach((prop) => {
if ((0, utils_1.isNotReservedWord)(prop)) {
normalized[prop] = frame[prop];
}
});
return normalized;
});
// 计算 computedOffset
normalizedFrames = normalizedFrames.map((frame, index) => {
// 如果 offset 未定义或为 null,则自动计算
if (frame.offset == null) {
if (index === normalizedFrames.length - 1) {
frame.computedOffset = 1;
}
else if (index === 0) {
frame.computedOffset = 0;
}
else {
// 均匀分布中间关键帧
frame.computedOffset = index / (normalizedFrames.length - 1);
}
}
else {
frame.computedOffset = frame.offset;
}
return frame;
});
return normalizedFrames;
}
setKeyframes(keyframes) {
this._keyframes = keyframes;
this._computedKeyframes = this.getKeyframes();
// 收集动画属性的原始值
this._computedKeyframes.forEach((frame) => {
Object.keys(frame).forEach((prop) => {
if ((0, utils_1.isNotReservedWord)(prop) && this._originProps[prop] == null) {
this._originProps[prop] = this.target.getPropByPath(prop);
}
});
});
}
getTiming() {
return common_1.ObjectExt.defaults(this._options, defaultTiming);
}
getComputedTiming() {
const timing = this.getTiming();
const activeDuration = timing.duration * timing.iterations;
return Object.assign(Object.assign({}, timing), { activeDuration, endTime: activeDuration + timing.delay });
}
apply(iterationTime) {
var _a, _b, _c, _d;
if (!this._target || !this._computedKeyframes.length)
return;
// 参数为null则回到初始状态
if (iterationTime == null) {
Object.entries(this._originProps).forEach(([prop, value]) => {
this.target.setPropByPath(prop, value);
});
return;
}
const timing = this.getComputedTiming();
const duration = timing.duration;
if (duration < 0)
return;
// 计算进度 (0-1)
const progress = Math.min(iterationTime / duration, 1);
// 找到当前进度对应的关键帧
const frames = this._computedKeyframes;
if (frames.length === 0)
return;
let startFrame = { computedOffset: 0 };
let endFrame = { computedOffset: 1 };
for (const frame of frames) {
if (progress === 0 && frame.computedOffset === 0) {
startFrame = frame;
}
if (progress === 1 && frame.computedOffset === 1) {
endFrame = frame;
}
if (frame.computedOffset < progress) {
startFrame = frame;
}
if (frame.computedOffset > progress) {
endFrame = frame;
break;
}
}
// 计算两个关键帧之间的插值
const startOffset = startFrame.computedOffset;
const endOffset = endFrame.computedOffset;
const frameProgress = (progress - startOffset) / (endOffset - startOffset);
const kebabEasingName = (_a = startFrame.easing) !== null && _a !== void 0 ? _a : endFrame.easing;
const easingName = common_1.StringExt.camelCase(kebabEasingName);
const easingFn = (_b = common_1.Timing[easingName]) !== null && _b !== void 0 ? _b : common_1.Timing.linear;
// 应用插值后的样式
for (const prop in Object.assign(Object.assign({}, startFrame), endFrame)) {
if ((0, utils_1.isNotReservedWord)(prop) &&
(startFrame[prop] != null || endFrame[prop] != null)) {
const startValue = (_c = startFrame[prop]) !== null && _c !== void 0 ? _c : this._originProps[prop];
const endValue = (_d = endFrame[prop]) !== null && _d !== void 0 ? _d : this._originProps[prop];
let interpolation;
// TODO: rgb color
if (String(startValue).startsWith('#')) {
interpolation = common_1.Interp.color;
}
else if (prop.endsWith('transform') &&
!common_1.NumberExt.isNumber(startValue)) {
interpolation = common_1.Interp.transform;
}
else if (util_1.unitReg.test(String(startValue)) ||
util_1.unitReg.test(String(endValue))) {
interpolation = common_1.Interp.unit;
}
else {
interpolation = common_1.Interp.number;
}
const interpolationFn = interpolation(startValue, endValue);
const value = interpolationFn(easingFn(frameProgress));
this.target.setPropByPath(prop, value);
}
}
}
}
exports.KeyframeEffect = KeyframeEffect;
const defaultTiming = {
delay: 0,
direction: 'normal',
duration: 0,
easing: 'linear',
fill: 'none',
iterations: 1,
};
//# sourceMappingURL=keyframeEffect.js.map