UNPKG

@antv/x6

Version:

JavaScript diagramming library that uses SVG and HTML for rendering

190 lines 8.12 kB
"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