mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-13 11:38:16 +00:00
Add support for animation of stroke-dasharray to nsStyleAnimation. (Bug 523355) r=dholbert sr=bzbarsky
This commit is contained in:
parent
6b816ecdd6
commit
4d6a859d90
@ -2942,8 +2942,8 @@ CSS_PROP_SVG(
|
||||
mStrokeDasharray,
|
||||
eCSSType_ValueList,
|
||||
nsnull,
|
||||
CSS_PROP_NO_OFFSET,
|
||||
eStyleAnimType_None)
|
||||
CSS_PROP_NO_OFFSET, /* property stored in 2 separate members */
|
||||
eStyleAnimType_Custom)
|
||||
CSS_PROP_SVG(
|
||||
stroke-dashoffset,
|
||||
stroke_dashoffset,
|
||||
|
@ -86,6 +86,33 @@ GetCommonUnit(nsStyleAnimation::Unit aFirstUnit,
|
||||
return aFirstUnit;
|
||||
}
|
||||
|
||||
// Greatest Common Divisor
|
||||
static PRUint32
|
||||
gcd(PRUint32 a, PRUint32 b)
|
||||
{
|
||||
// Euclid's algorithm; O(N) in the worst case. (There are better
|
||||
// ways, but we don't need them for stroke-dasharray animation.)
|
||||
NS_ABORT_IF_FALSE(a > 0 && b > 0, "positive numbers expected");
|
||||
|
||||
while (a != b) {
|
||||
if (a > b) {
|
||||
a = a - b;
|
||||
} else {
|
||||
b = b - a;
|
||||
}
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// Least Common Multiple
|
||||
static PRUint32
|
||||
lcm(PRUint32 a, PRUint32 b)
|
||||
{
|
||||
// Divide first to reduce overflow risk.
|
||||
return (a / gcd(a, b)) * b;
|
||||
}
|
||||
|
||||
// CLASS METHODS
|
||||
// -------------
|
||||
|
||||
@ -153,6 +180,53 @@ nsStyleAnimation::ComputeDistance(const Value& aStartValue,
|
||||
diffG * diffG + diffB * diffB);
|
||||
break;
|
||||
}
|
||||
case eUnit_Dasharray: {
|
||||
// NOTE: This produces results on substantially different scales
|
||||
// for length values and percentage values, which might even be
|
||||
// mixed in the same property value. This means the result isn't
|
||||
// particularly useful for paced animation.
|
||||
|
||||
// Call AddWeighted to make us lists of the same length.
|
||||
Value normValue1, normValue2;
|
||||
if (!AddWeighted(1.0, aStartValue, 0.0, aEndValue, normValue1) ||
|
||||
!AddWeighted(0.0, aStartValue, 1.0, aEndValue, normValue2)) {
|
||||
success = PR_FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
double distance = 0.0;
|
||||
const nsCSSValueList *list1 = normValue1.GetCSSValueListValue();
|
||||
const nsCSSValueList *list2 = normValue2.GetCSSValueListValue();
|
||||
|
||||
NS_ABORT_IF_FALSE(!list1 == !list2, "lists should be same length");
|
||||
while (list1) {
|
||||
const nsCSSValue &val1 = list1->mValue;
|
||||
const nsCSSValue &val2 = list2->mValue;
|
||||
|
||||
NS_ABORT_IF_FALSE(val1.GetUnit() == val2.GetUnit(),
|
||||
"unit match should be assured by AddWeighted");
|
||||
double diff;
|
||||
switch (val1.GetUnit()) {
|
||||
case eCSSUnit_Percent:
|
||||
diff = val1.GetPercentValue() - val2.GetPercentValue();
|
||||
break;
|
||||
case eCSSUnit_Number:
|
||||
diff = val1.GetFloatValue() - val2.GetFloatValue();
|
||||
break;
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
|
||||
return PR_FALSE;
|
||||
}
|
||||
distance += diff * diff;
|
||||
|
||||
list1 = list1->mNext;
|
||||
list2 = list2->mNext;
|
||||
NS_ABORT_IF_FALSE(!list1 == !list2, "lists should be same length");
|
||||
}
|
||||
|
||||
aDistance = sqrt(distance);
|
||||
break;
|
||||
}
|
||||
case eUnit_Shadow: {
|
||||
// Call AddWeighted to make us lists of the same length.
|
||||
Value normValue1, normValue2;
|
||||
@ -166,7 +240,8 @@ nsStyleAnimation::ComputeDistance(const Value& aStartValue,
|
||||
const nsCSSValueList *shadow2 = normValue2.GetCSSValueListValue();
|
||||
|
||||
double distance = 0.0f;
|
||||
while (shadow1 && shadow2) {
|
||||
NS_ABORT_IF_FALSE(!shadow1 == !shadow2, "lists should be same length");
|
||||
while (shadow1) {
|
||||
nsCSSValue::Array *array1 = shadow1->mValue.GetArrayValue();
|
||||
nsCSSValue::Array *array2 = shadow2->mValue.GetArrayValue();
|
||||
for (PRUint32 i = 0; i < 4; ++i) {
|
||||
@ -179,10 +254,10 @@ nsStyleAnimation::ComputeDistance(const Value& aStartValue,
|
||||
distance += diff * diff;
|
||||
}
|
||||
|
||||
const nsCSSValue& color1 = array1->Item(4);
|
||||
const nsCSSValue& color2 = array2->Item(4);
|
||||
const nsCSSValue& inset1 = array1->Item(5);
|
||||
const nsCSSValue& inset2 = array2->Item(5);
|
||||
const nsCSSValue &color1 = array1->Item(4);
|
||||
const nsCSSValue &color2 = array2->Item(4);
|
||||
const nsCSSValue &inset1 = array1->Item(5);
|
||||
const nsCSSValue &inset2 = array2->Item(5);
|
||||
// There are only two possible states of the inset value:
|
||||
// (1) GetUnit() == eCSSUnit_Null
|
||||
// (2) GetUnit() == eCSSUnit_Enumerated &&
|
||||
@ -209,8 +284,8 @@ nsStyleAnimation::ComputeDistance(const Value& aStartValue,
|
||||
|
||||
shadow1 = shadow1->mNext;
|
||||
shadow2 = shadow2->mNext;
|
||||
NS_ABORT_IF_FALSE(!shadow1 == !shadow2, "lists should be same length");
|
||||
}
|
||||
NS_ABORT_IF_FALSE(!shadow1 && !shadow2, "lists of different lengths");
|
||||
aDistance = sqrt(distance);
|
||||
break;
|
||||
}
|
||||
@ -371,6 +446,64 @@ nsStyleAnimation::AddWeighted(double aCoeff1, const Value& aValue1,
|
||||
aResultValue.SetColorValue(resultColor);
|
||||
break;
|
||||
}
|
||||
case eUnit_Dasharray: {
|
||||
const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
|
||||
const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
|
||||
|
||||
PRUint32 len1 = 0, len2 = 0;
|
||||
for (const nsCSSValueList *v = list1; v; v = v->mNext) {
|
||||
++len1;
|
||||
}
|
||||
for (const nsCSSValueList *v = list2; v; v = v->mNext) {
|
||||
++len2;
|
||||
}
|
||||
NS_ABORT_IF_FALSE(len1 > 0 && len2 > 0, "unexpected length");
|
||||
|
||||
nsAutoPtr<nsCSSValueList> result;
|
||||
nsCSSValueList **resultTail = getter_Transfers(result);
|
||||
for (PRUint32 i = 0, i_end = lcm(len1, len2); i != i_end; ++i) {
|
||||
const nsCSSValue &v1 = list1->mValue;
|
||||
const nsCSSValue &v2 = list2->mValue;
|
||||
NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Number ||
|
||||
v1.GetUnit() == eCSSUnit_Percent, "unexpected");
|
||||
NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Number ||
|
||||
v2.GetUnit() == eCSSUnit_Percent, "unexpected");
|
||||
if (v1.GetUnit() != v2.GetUnit()) {
|
||||
// Can't animate between lengths and percentages (until calc()).
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsCSSValueList *item = new nsCSSValueList;
|
||||
if (!item) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
*resultTail = item;
|
||||
resultTail = &item->mNext;
|
||||
|
||||
if (v1.GetUnit() == eCSSUnit_Number) {
|
||||
item->mValue.SetFloatValue(aCoeff1 * v1.GetFloatValue() +
|
||||
aCoeff2 * v2.GetFloatValue(),
|
||||
eCSSUnit_Number);
|
||||
} else {
|
||||
NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Percent, "unexpected");
|
||||
item->mValue.SetPercentValue(aCoeff1 * v1.GetPercentValue() +
|
||||
aCoeff2 * v2.GetPercentValue());
|
||||
}
|
||||
|
||||
list1 = list1->mNext;
|
||||
if (!list1) {
|
||||
list1 = aValue1.GetCSSValueListValue();
|
||||
}
|
||||
list2 = list2->mNext;
|
||||
if (!list2) {
|
||||
list2 = aValue2.GetCSSValueListValue();
|
||||
}
|
||||
}
|
||||
|
||||
aResultValue.SetCSSValueListValue(result.forget(), eUnit_Dasharray,
|
||||
PR_TRUE);
|
||||
break;
|
||||
}
|
||||
case eUnit_Shadow: {
|
||||
// This is implemented according to:
|
||||
// http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
|
||||
@ -578,6 +711,7 @@ nsStyleAnimation::UncomputeValue(nsCSSProperty aProperty,
|
||||
SetColorValue(aComputedValue.GetColorValue());
|
||||
}
|
||||
break;
|
||||
case eUnit_Dasharray:
|
||||
case eUnit_Shadow:
|
||||
NS_ABORT_IF_FALSE(nsCSSProps::kTypeTable[aProperty] ==
|
||||
eCSSType_ValueList, "type mismatch");
|
||||
@ -754,6 +888,54 @@ nsStyleAnimation::ExtractComputedValue(nsCSSProperty aProperty,
|
||||
break;
|
||||
}
|
||||
|
||||
case eCSSProperty_stroke_dasharray: {
|
||||
const nsStyleSVG *svg = static_cast<const nsStyleSVG*>(styleStruct);
|
||||
NS_ABORT_IF_FALSE((svg->mStrokeDasharray != nsnull) ==
|
||||
(svg->mStrokeDasharrayLength != 0),
|
||||
"pointer/length mismatch");
|
||||
if (svg->mStrokeDasharray) {
|
||||
nsAutoPtr<nsCSSValueList> result;
|
||||
nsCSSValueList **resultTail = getter_Transfers(result);
|
||||
for (PRUint32 i = 0, i_end = svg->mStrokeDasharrayLength;
|
||||
i != i_end; ++i) {
|
||||
nsCSSValueList *item = new nsCSSValueList;
|
||||
if (!item) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
*resultTail = item;
|
||||
resultTail = &item->mNext;
|
||||
|
||||
const nsStyleCoord &coord = svg->mStrokeDasharray[i];
|
||||
nsCSSValue &value = item->mValue;
|
||||
switch (coord.GetUnit()) {
|
||||
case eStyleUnit_Coord:
|
||||
// Number means the same thing as length; we want to
|
||||
// animate them the same way. Normalize both to number
|
||||
// since it has more accuracy (float vs nscoord).
|
||||
value.SetFloatValue(nsPresContext::
|
||||
AppUnitsToFloatCSSPixels(coord.GetCoordValue()),
|
||||
eCSSUnit_Number);
|
||||
break;
|
||||
case eStyleUnit_Factor:
|
||||
value.SetFloatValue(coord.GetFactorValue(),
|
||||
eCSSUnit_Number);
|
||||
break;
|
||||
case eStyleUnit_Percent:
|
||||
value.SetPercentValue(coord.GetPercentValue());
|
||||
break;
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(PR_FALSE, "unexpected unit");
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
aComputedValue.SetCSSValueListValue(result.forget(),
|
||||
eUnit_Dasharray, PR_TRUE);
|
||||
} else {
|
||||
aComputedValue.SetNoneValue();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
NS_ABORT_IF_FALSE(PR_FALSE, "missing property implementation");
|
||||
return PR_FALSE;
|
||||
@ -912,6 +1094,7 @@ nsStyleAnimation::Value::operator=(const Value& aOther)
|
||||
case eUnit_Color:
|
||||
mValue.mColor = aOther.mValue.mColor;
|
||||
break;
|
||||
case eUnit_Dasharray:
|
||||
case eUnit_Shadow:
|
||||
mValue.mCSSValueList = aOther.mValue.mCSSValueList
|
||||
? aOther.mValue.mCSSValueList->Clone() : nsnull;
|
||||
@ -1016,6 +1199,7 @@ nsStyleAnimation::Value::operator==(const Value& aOther) const
|
||||
return mValue.mFloat == aOther.mValue.mFloat;
|
||||
case eUnit_Color:
|
||||
return mValue.mColor == aOther.mValue.mColor;
|
||||
case eUnit_Dasharray:
|
||||
case eUnit_Shadow:
|
||||
return nsCSSValueList::Equal(mValue.mCSSValueList,
|
||||
aOther.mValue.mCSSValueList);
|
||||
|
@ -83,6 +83,11 @@ public:
|
||||
/**
|
||||
* Calculates a measure of 'distance' between two values.
|
||||
*
|
||||
* This measure of Distance is guaranteed to be proportional to
|
||||
* portions passed to Interpolate, Add, or AddWeighted. However, for
|
||||
* some types of Value it may not produce sensible results for paced
|
||||
* animation.
|
||||
*
|
||||
* If this method succeeds, the returned distance value is guaranteed to be
|
||||
* non-negative.
|
||||
*
|
||||
@ -220,6 +225,7 @@ public:
|
||||
eUnit_Percent,
|
||||
eUnit_Float,
|
||||
eUnit_Color,
|
||||
eUnit_Dasharray, // nsCSSValueList* (never null)
|
||||
eUnit_Shadow // nsCSSValueList* (may be null)
|
||||
};
|
||||
|
||||
@ -302,7 +308,7 @@ public:
|
||||
void FreeValue();
|
||||
|
||||
static PRBool IsCSSValueListUnit(Unit aUnit) {
|
||||
return aUnit == eUnit_Shadow;
|
||||
return aUnit == eUnit_Dasharray || aUnit == eUnit_Shadow;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -95,6 +95,7 @@ var supported_properties = {
|
||||
"stop-color": [ test_color_transition ],
|
||||
"stop-opacity" : [ test_float_zeroToOne_transition ],
|
||||
"stroke": [ test_color_transition ],
|
||||
"stroke-dasharray": [ test_dasharray_transition ],
|
||||
"stroke-dashoffset": [ test_length_transition, test_percent_transition ],
|
||||
"stroke-miterlimit": [ test_float_aboveOne_transition ],
|
||||
"stroke-opacity" : [ test_float_zeroToOne_transition ],
|
||||
@ -320,6 +321,44 @@ function test_shadow_transition(prop) {
|
||||
"shadow-valued property " + prop + ": interpolation without color");
|
||||
}
|
||||
|
||||
function test_dasharray_transition(prop) {
|
||||
div.style.setProperty("-moz-transition-property", "none", "");
|
||||
div.style.setProperty(prop, "3", "");
|
||||
is(cs.getPropertyValue(prop), "3",
|
||||
"dasharray-valued property " + prop +
|
||||
": computed value before transition");
|
||||
div.style.setProperty("-moz-transition-property", prop, "");
|
||||
div.style.setProperty(prop, "9px", "");
|
||||
is(cs.getPropertyValue(prop), "6",
|
||||
"dasharray-valued property " + prop + ": interpolation of dasharray");
|
||||
div.style.setProperty(prop, "none", "");
|
||||
is(cs.getPropertyValue(prop), "none",
|
||||
"dasharray-valued property " + prop + ": non-interpolability of none");
|
||||
div.style.setProperty(prop, "6,8px,4,4", "");
|
||||
is(cs.getPropertyValue(prop), "6, 8px, 4, 4",
|
||||
"dasharray-valued property " + prop +
|
||||
": computed value before transition");
|
||||
div.style.setProperty(prop, "10, 10,10,10px", "");
|
||||
is(cs.getPropertyValue(prop), "8, 9, 7, 7",
|
||||
"dasharray-valued property " + prop + ": interpolation of dasharray");
|
||||
div.style.setProperty(prop, "none", "");
|
||||
is(cs.getPropertyValue(prop), "none",
|
||||
"dasharray-valued property " + prop + ": non-interpolability of none");
|
||||
div.style.setProperty(prop, "4,8,2", "");
|
||||
is(cs.getPropertyValue(prop), "4, 8, 2",
|
||||
"dasharray-valued property " + prop +
|
||||
": computed value before transition");
|
||||
div.style.setProperty(prop, "2,4,6,8", "");
|
||||
is(cs.getPropertyValue(prop), "3, 6, 4, 6, 5, 3, 5, 8, 2, 4, 7, 5",
|
||||
"dasharray-valued property " + prop + ": interpolation of dasharray");
|
||||
div.style.setProperty(prop, "2,50%,6,8", "");
|
||||
is(cs.getPropertyValue(prop), "2, 50%, 6, 8",
|
||||
"dasharray-valued property " + prop + ": non-interpolability of mixed units");
|
||||
div.style.setProperty(prop, "4,20%,2,2", "");
|
||||
is(cs.getPropertyValue(prop), "3, 35%, 4, 5",
|
||||
"dasharray-valued property " + prop + ": interpolation of dasharray");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
Loading…
x
Reference in New Issue
Block a user