Revert "[skottie] Fix text-on-path tracking"
This reverts commit ca973cbea0
.
Reason for revert: g3 image diffs
Original change's description:
> [skottie] Fix text-on-path tracking
>
> Tracking and line spacing computations require knowledge of cumulative
> values for the whole line => we need two passes:
>
> 1) compute cumulative values
> 2) compute per-fragment position adjustments
>
> Currently, #1 is implemented in the main onSync() loop (as we iterate
> to compute fragment props) and #2 is post-applied via adjustLineProps(),
> after the main loop.
>
> The problem is adjustLineProps() is executed after positioning glyphs on
> path, and tracking is not taken into account for path positioning
> (instead it moves glyphs horizontally, unrelated to the path).
>
> To fix this, we need tracking adjustments to be applied before
> positioning on path (which is performed in fragmentMatrix()).
>
> - move the cumulative tracking computation to a dedicate lambda
> (compute_linewide_props)
> - move the fragment position adjustments to the main onSync() loop
> (that way they participate in path positioning)
> - to avoid executing the first pass unnecessarily, add flags to detect
> the presence of tracking and line spacing animators.
>
>
> Change-Id: Ieef2afb53ffe14177eba0ef41dc5c71149cab070
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/518696
> Reviewed-by: Ben Wagner <bungeman@google.com>
> Commit-Queue: Florin Malita <fmalita@chromium.org>
> Commit-Queue: Florin Malita <fmalita@google.com>
Change-Id: Ia99fbb3d7d98eb6a59ff00d796bcc05bc6db63a3
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/519597
Auto-Submit: Florin Malita <fmalita@google.com>
Commit-Queue: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
This commit is contained in:
parent
b141e485d2
commit
5fe4b6faeb
@ -234,9 +234,8 @@ sk_sp<TextAdapter> TextAdapter::Make(const skjson::ObjectValue& jlayer,
|
|||||||
|
|
||||||
for (const skjson::ObjectValue* janimator : *janimators) {
|
for (const skjson::ObjectValue* janimator : *janimators) {
|
||||||
if (auto animator = TextAnimator::Make(janimator, abuilder, adapter.get())) {
|
if (auto animator = TextAnimator::Make(janimator, abuilder, adapter.get())) {
|
||||||
adapter->fHasBlurAnimator |= animator->hasBlur();
|
adapter->fHasBlurAnimator |= animator->hasBlur();
|
||||||
adapter->fRequiresAnchorPoint |= animator->requiresAnchorPoint();
|
adapter->fRequiresAnchorPoint |= animator->requiresAnchorPoint();
|
||||||
adapter->fRequiresLineAdjustments |= animator->requiresLineAdjustments();
|
|
||||||
|
|
||||||
adapter->fAnimators.push_back(std::move(animator));
|
adapter->fAnimators.push_back(std::move(animator));
|
||||||
}
|
}
|
||||||
@ -295,8 +294,7 @@ TextAdapter::TextAdapter(sk_sp<SkFontMgr> fontmgr, sk_sp<Logger> logger, AnchorP
|
|||||||
, fLogger(std::move(logger))
|
, fLogger(std::move(logger))
|
||||||
, fAnchorPointGrouping(apg)
|
, fAnchorPointGrouping(apg)
|
||||||
, fHasBlurAnimator(false)
|
, fHasBlurAnimator(false)
|
||||||
, fRequiresAnchorPoint(false)
|
, fRequiresAnchorPoint(false) {}
|
||||||
, fRequiresLineAdjustments(false) {}
|
|
||||||
|
|
||||||
TextAdapter::~TextAdapter() = default;
|
TextAdapter::~TextAdapter() = default;
|
||||||
|
|
||||||
@ -582,43 +580,16 @@ void TextAdapter::onSync() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t grouping_span_index = 0;
|
size_t grouping_span_index = 0;
|
||||||
SkV2 current_line_offset = { 0, 0 }; // cumulative line spacing
|
SkV2 line_offset = { 0, 0 }; // cumulative line spacing
|
||||||
|
|
||||||
auto compute_linewide_props = [this](const TextAnimator::ModulatorBuffer& buf,
|
|
||||||
const TextAnimator::DomainSpan& line_span) {
|
|
||||||
SkV2 total_spacing = {0,0};
|
|
||||||
float total_tracking = 0;
|
|
||||||
|
|
||||||
// Only compute these when needed.
|
|
||||||
if (fRequiresLineAdjustments) {
|
|
||||||
for (size_t i = line_span.fOffset; i < line_span.fOffset + line_span.fCount; ++i) {
|
|
||||||
const auto& props = buf[i].props;
|
|
||||||
total_spacing += props.line_spacing;
|
|
||||||
total_tracking += props.tracking;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first glyph does not contribute |before| tracking, and the last one does not
|
|
||||||
// contribute |after| tracking.
|
|
||||||
total_tracking -= 0.5f * (buf[line_span.fOffset].props.tracking +
|
|
||||||
buf[line_span.fOffset + line_span.fCount - 1].props.tracking);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_tuple(total_spacing, total_tracking);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Finally, push all props to their corresponding fragment.
|
// Finally, push all props to their corresponding fragment.
|
||||||
for (const auto& line_span : fMaps.fLinesMap) {
|
for (const auto& line_span : fMaps.fLinesMap) {
|
||||||
const auto [line_spacing, line_tracking] = compute_linewide_props(buf, line_span);
|
SkV2 line_spacing = { 0, 0 };
|
||||||
const auto align_offset = -line_tracking * align_factor(fText->fHAlign);
|
float line_tracking = 0;
|
||||||
|
bool line_has_tracking = false;
|
||||||
|
|
||||||
// line spacing of the first line is ignored (nothing to "space" against)
|
// Tracking requires special treatment: unlike other props, its effect is not localized
|
||||||
if (&line_span != &fMaps.fLinesMap.front() && line_span.fCount) {
|
// to a single fragment, but requires re-alignment of the whole line.
|
||||||
// For each line, the actual spacing is an average of individual fragment spacing
|
|
||||||
// (to preserve the "line").
|
|
||||||
current_line_offset += line_spacing / line_span.fCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
float tracking_acc = 0;
|
|
||||||
for (size_t i = line_span.fOffset; i < line_span.fOffset + line_span.fCount; ++i) {
|
for (size_t i = line_span.fOffset; i < line_span.fOffset + line_span.fCount; ++i) {
|
||||||
// Track the grouping domain span in parallel.
|
// Track the grouping domain span in parallel.
|
||||||
if (grouping_domain && i >= (*grouping_domain)[grouping_span_index].fOffset +
|
if (grouping_domain && i >= (*grouping_domain)[grouping_span_index].fOffset +
|
||||||
@ -630,28 +601,27 @@ void TextAdapter::onSync() {
|
|||||||
|
|
||||||
const auto& props = buf[i].props;
|
const auto& props = buf[i].props;
|
||||||
const auto& frag = fFragments[i];
|
const auto& frag = fFragments[i];
|
||||||
|
this->pushPropsToFragment(props, frag, fGroupingAlignment * .01f, // percentage
|
||||||
// AE tracking is defined per glyph, based on two components: |before| and |after|.
|
|
||||||
// BodyMovin only exports "balanced" tracking values, where before = after = tracking/2.
|
|
||||||
//
|
|
||||||
// Tracking is applied as a local glyph offset, and contributes to the line width for
|
|
||||||
// alignment purposes.
|
|
||||||
//
|
|
||||||
// No |before| tracking for the first glyph, nor |after| tracking for the last one.
|
|
||||||
const auto track_before = i > line_span.fOffset
|
|
||||||
? props.tracking * 0.5f : 0.0f,
|
|
||||||
track_after = i < line_span.fOffset + line_span.fCount - 1
|
|
||||||
? props.tracking * 0.5f : 0.0f;
|
|
||||||
|
|
||||||
const auto frag_offset = current_line_offset +
|
|
||||||
SkV2{align_offset + tracking_acc + track_before, 0};
|
|
||||||
|
|
||||||
tracking_acc += track_before + track_after;
|
|
||||||
|
|
||||||
this->pushPropsToFragment(props, frag, frag_offset, fGroupingAlignment * .01f, // %
|
|
||||||
grouping_domain ? &(*grouping_domain)[grouping_span_index]
|
grouping_domain ? &(*grouping_domain)[grouping_span_index]
|
||||||
: nullptr);
|
: nullptr);
|
||||||
|
|
||||||
|
line_tracking += props.tracking;
|
||||||
|
line_has_tracking |= !SkScalarNearlyZero(props.tracking);
|
||||||
|
|
||||||
|
line_spacing += props.line_spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// line spacing of the first line is ignored (nothing to "space" against)
|
||||||
|
if (&line_span != &fMaps.fLinesMap.front()) {
|
||||||
|
// For each line, the actual spacing is an average of individual fragment spacing
|
||||||
|
// (to preserve the "line").
|
||||||
|
line_offset += line_spacing / line_span.fCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line_offset != SkV2{0, 0} || line_has_tracking) {
|
||||||
|
this->adjustLineProps(buf, line_span, line_offset, line_tracking);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,10 +682,10 @@ SkV2 TextAdapter::fragmentAnchorPoint(const FragmentRec& rec,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SkM44 TextAdapter::fragmentMatrix(const TextAnimator::ResolvedProps& props,
|
SkM44 TextAdapter::fragmentMatrix(const TextAnimator::ResolvedProps& props,
|
||||||
const FragmentRec& rec, const SkV2& frag_offset) const {
|
const FragmentRec& rec, const SkV2& anchor_point) const {
|
||||||
const SkV3 pos = {
|
const SkV3 pos = {
|
||||||
props.position.x + rec.fOrigin.fX + frag_offset.x,
|
props.position.x + rec.fOrigin.fX + anchor_point.x,
|
||||||
props.position.y + rec.fOrigin.fY + frag_offset.y,
|
props.position.y + rec.fOrigin.fY + anchor_point.y,
|
||||||
props.position.z
|
props.position.z
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -745,13 +715,12 @@ SkM44 TextAdapter::fragmentMatrix(const TextAnimator::ResolvedProps& props,
|
|||||||
|
|
||||||
void TextAdapter::pushPropsToFragment(const TextAnimator::ResolvedProps& props,
|
void TextAdapter::pushPropsToFragment(const TextAnimator::ResolvedProps& props,
|
||||||
const FragmentRec& rec,
|
const FragmentRec& rec,
|
||||||
const SkV2& frag_offset,
|
|
||||||
const SkV2& grouping_alignment,
|
const SkV2& grouping_alignment,
|
||||||
const TextAnimator::DomainSpan* grouping_span) const {
|
const TextAnimator::DomainSpan* grouping_span) const {
|
||||||
const auto anchor_point = this->fragmentAnchorPoint(rec, grouping_alignment, grouping_span);
|
const auto anchor_point = this->fragmentAnchorPoint(rec, grouping_alignment, grouping_span);
|
||||||
|
|
||||||
rec.fMatrixNode->setMatrix(
|
rec.fMatrixNode->setMatrix(
|
||||||
this->fragmentMatrix(props, rec, anchor_point + frag_offset)
|
this->fragmentMatrix(props, rec, anchor_point)
|
||||||
* SkM44::Rotate({ 1, 0, 0 }, SkDegreesToRadians(props.rotation.x))
|
* SkM44::Rotate({ 1, 0, 0 }, SkDegreesToRadians(props.rotation.x))
|
||||||
* SkM44::Rotate({ 0, 1, 0 }, SkDegreesToRadians(props.rotation.y))
|
* SkM44::Rotate({ 0, 1, 0 }, SkDegreesToRadians(props.rotation.y))
|
||||||
* SkM44::Rotate({ 0, 0, 1 }, SkDegreesToRadians(props.rotation.z))
|
* SkM44::Rotate({ 0, 0, 1 }, SkDegreesToRadians(props.rotation.z))
|
||||||
@ -774,4 +743,44 @@ void TextAdapter::pushPropsToFragment(const TextAnimator::ResolvedProps& props,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextAdapter::adjustLineProps(const TextAnimator::ModulatorBuffer& buf,
|
||||||
|
const TextAnimator::DomainSpan& line_span,
|
||||||
|
const SkV2& line_offset,
|
||||||
|
float total_tracking) const {
|
||||||
|
SkASSERT(line_span.fCount > 0);
|
||||||
|
|
||||||
|
// AE tracking is defined per glyph, based on two components: |before| and |after|.
|
||||||
|
// BodyMovin only exports "balanced" tracking values, where before == after == tracking / 2.
|
||||||
|
//
|
||||||
|
// Tracking is applied as a local glyph offset, and contributes to the line width for alignment
|
||||||
|
// purposes.
|
||||||
|
|
||||||
|
// The first glyph does not contribute |before| tracking, and the last one does not contribute
|
||||||
|
// |after| tracking. Rather than spill this logic into applyAnimators, post-adjust here.
|
||||||
|
total_tracking -= 0.5f * (buf[line_span.fOffset].props.tracking +
|
||||||
|
buf[line_span.fOffset + line_span.fCount - 1].props.tracking);
|
||||||
|
|
||||||
|
const auto align_offset = -total_tracking * align_factor(fText->fHAlign);
|
||||||
|
|
||||||
|
float tracking_acc = 0;
|
||||||
|
for (size_t i = line_span.fOffset; i < line_span.fOffset + line_span.fCount; ++i) {
|
||||||
|
const auto& props = buf[i].props;
|
||||||
|
|
||||||
|
// No |before| tracking for the first glyph, nor |after| tracking for the last one.
|
||||||
|
const auto track_before = i > line_span.fOffset
|
||||||
|
? props.tracking * 0.5f : 0.0f,
|
||||||
|
track_after = i < line_span.fOffset + line_span.fCount - 1
|
||||||
|
? props.tracking * 0.5f : 0.0f,
|
||||||
|
fragment_offset = align_offset + tracking_acc + track_before;
|
||||||
|
|
||||||
|
const auto& frag = fFragments[i];
|
||||||
|
const auto m = SkM44::Translate(line_offset.x + fragment_offset,
|
||||||
|
line_offset.y) *
|
||||||
|
frag.fMatrixNode->getMatrix();
|
||||||
|
frag.fMatrixNode->setMatrix(m);
|
||||||
|
|
||||||
|
tracking_acc += track_before + track_after;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace skottie::internal
|
} // namespace skottie::internal
|
||||||
|
@ -69,8 +69,12 @@ private:
|
|||||||
void buildDomainMaps(const Shaper::Result&);
|
void buildDomainMaps(const Shaper::Result&);
|
||||||
|
|
||||||
void pushPropsToFragment(const TextAnimator::ResolvedProps&, const FragmentRec&,
|
void pushPropsToFragment(const TextAnimator::ResolvedProps&, const FragmentRec&,
|
||||||
const SkV2& frag_offset, const SkV2& grouping_alignment,
|
const SkV2&, const TextAnimator::DomainSpan*) const;
|
||||||
const TextAnimator::DomainSpan*) const;
|
|
||||||
|
void adjustLineProps(const TextAnimator::ModulatorBuffer&,
|
||||||
|
const TextAnimator::DomainSpan&,
|
||||||
|
const SkV2& line_offset,
|
||||||
|
float line_tracking) const;
|
||||||
|
|
||||||
SkV2 fragmentAnchorPoint(const FragmentRec&, const SkV2&,
|
SkV2 fragmentAnchorPoint(const FragmentRec&, const SkV2&,
|
||||||
const TextAnimator::DomainSpan*) const;
|
const TextAnimator::DomainSpan*) const;
|
||||||
@ -112,9 +116,8 @@ private:
|
|||||||
struct PathInfo;
|
struct PathInfo;
|
||||||
std::unique_ptr<PathInfo> fPathInfo;
|
std::unique_ptr<PathInfo> fPathInfo;
|
||||||
|
|
||||||
bool fHasBlurAnimator : 1,
|
bool fHasBlurAnimator : 1,
|
||||||
fRequiresAnchorPoint : 1,
|
fRequiresAnchorPoint : 1;
|
||||||
fRequiresLineAdjustments : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -182,14 +182,11 @@ TextAnimator::TextAnimator(std::vector<sk_sp<RangeSelector>>&& selectors,
|
|||||||
const AnimationBuilder* abuilder,
|
const AnimationBuilder* abuilder,
|
||||||
AnimatablePropertyContainer* acontainer)
|
AnimatablePropertyContainer* acontainer)
|
||||||
: fSelectors(std::move(selectors))
|
: fSelectors(std::move(selectors))
|
||||||
, fRequiresAnchorPoint(false)
|
, fRequiresAnchorPoint(false) {
|
||||||
, fRequiresLineAdjustments(false) {
|
|
||||||
|
|
||||||
acontainer->bind(*abuilder, jprops["p" ], fTextProps.position);
|
acontainer->bind(*abuilder, jprops["p" ], fTextProps.position);
|
||||||
|
acontainer->bind(*abuilder, jprops["t" ], fTextProps.tracking);
|
||||||
// Tracking and line spacing affect all line fragments.
|
acontainer->bind(*abuilder, jprops["ls"], fTextProps.line_spacing);
|
||||||
fRequiresLineAdjustments |= acontainer->bind(*abuilder, jprops["t" ], fTextProps.tracking);
|
|
||||||
fRequiresLineAdjustments |= acontainer->bind(*abuilder, jprops["ls"], fTextProps.line_spacing);
|
|
||||||
|
|
||||||
// Scale and rotation are anchor-point-dependent.
|
// Scale and rotation are anchor-point-dependent.
|
||||||
fRequiresAnchorPoint |= acontainer->bind(*abuilder, jprops["s"], fTextProps.scale);
|
fRequiresAnchorPoint |= acontainer->bind(*abuilder, jprops["s"], fTextProps.scale);
|
||||||
|
@ -87,8 +87,7 @@ public:
|
|||||||
|
|
||||||
bool hasBlur() const { return fHasBlur; }
|
bool hasBlur() const { return fHasBlur; }
|
||||||
|
|
||||||
bool requiresAnchorPoint() const { return fRequiresAnchorPoint; }
|
bool requiresAnchorPoint() const { return fRequiresAnchorPoint; }
|
||||||
bool requiresLineAdjustments() const { return fRequiresLineAdjustments; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TextAnimator(std::vector<sk_sp<RangeSelector>>&&,
|
TextAnimator(std::vector<sk_sp<RangeSelector>>&&,
|
||||||
@ -101,14 +100,13 @@ private:
|
|||||||
const std::vector<sk_sp<RangeSelector>> fSelectors;
|
const std::vector<sk_sp<RangeSelector>> fSelectors;
|
||||||
|
|
||||||
AnimatedProps fTextProps;
|
AnimatedProps fTextProps;
|
||||||
bool fHasFillColor : 1,
|
bool fHasFillColor : 1,
|
||||||
fHasStrokeColor : 1,
|
fHasStrokeColor : 1,
|
||||||
fHasFillOpacity : 1,
|
fHasFillOpacity : 1,
|
||||||
fHasStrokeOpacity : 1,
|
fHasStrokeOpacity : 1,
|
||||||
fHasOpacity : 1,
|
fHasOpacity : 1,
|
||||||
fHasBlur : 1,
|
fHasBlur : 1,
|
||||||
fRequiresAnchorPoint : 1, // animator sensitive to transform origin?
|
fRequiresAnchorPoint : 1; // animator sensitive to transform origin?
|
||||||
fRequiresLineAdjustments : 1; // animator effects line-wide fragment adjustments
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -1 +0,0 @@
|
|||||||
{"v":"5.8.3","fr":60,"ip":0,"op":301,"w":500,"h":500,"nm":"tpath tracking","ddd":0,"assets":[],"fonts":{"list":[{"origin":0,"fPath":"","fClass":"","fFamily":"Google Sans","fWeight":"","fStyle":"Bold","fName":"GoogleSans-Bold","ascent":75.6476929411292}]},"layers":[{"ddd":0,"ind":1,"ty":5,"nm":"Foo Bar Baz","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"n","pt":{"a":0,"k":{"i":[[66.274,0],[0,-66.274],[-66.274,0],[0,66.274]],"o":[[-66.274,0],[0,66.274],[66.274,0],[0,-66.274]],"v":[[0,-120],[-120,0],[0,120],[120,0]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"t":{"d":{"k":[{"s":{"s":72,"f":"GoogleSans-Bold","t":"Foo Bar Baz","ca":0,"j":2,"tr":0,"lh":96,"ls":0,"fc":[0,0.522,0]},"t":0}]},"p":{"m":0,"f":{"a":0,"k":377,"ix":5},"l":{"a":0,"k":0,"ix":6},"a":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":1,"ix":3},"r":{"a":0,"k":1,"ix":2}},"m":{"g":1,"a":{"a":0,"k":[0,0],"ix":2}},"a":[{"nm":"Animator 1","s":{"t":0,"xe":{"a":0,"k":0,"ix":7},"ne":{"a":0,"k":0,"ix":8},"a":{"a":0,"k":100,"ix":4},"b":1,"rn":0,"sh":1,"sm":{"a":0,"k":100,"ix":6},"r":1},"a":{"t":{"a":1,"k":[{"i":{"x":[0.336],"y":[0.999]},"o":{"x":[0.669],"y":[0.001]},"t":0,"s":[-35]},{"i":{"x":[0.336],"y":[0.997]},"o":{"x":[0.665],"y":[-0.001]},"t":150,"s":[50]},{"t":300,"s":[-35]}],"ix":89}}}]},"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[240,240],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,0,0,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false}],"ip":0,"op":301,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"Medium Yellow Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[600,600,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"sw":1200,"sh":1200,"sc":"#ffffbd","ip":0,"op":301,"st":0,"bm":0}],"markers":[]}
|
|
Loading…
Reference in New Issue
Block a user