Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE html>
<meta charset="UTF-8">
<title>shape-outside interpolation</title>
<meta name="assert" content="shape-outside supports animation as <basic-shape> or discrete">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/interpolation-testcommon.js"></script>
<style>
.parent {
shape-outside: circle(80% at 30% 10%);
}
.target {
shape-outside: circle(60% at 10% 30%);
}
</style>
<body></body>
<script>
/* TODO: add inset test once blend() works for it */
test_interpolation({
property: 'shape-outside',
from: neutralKeyframe,
to: 'circle(40% at 20% 20%)',
}, [
{at: -0.3, expect: 'circle(66% at 7% 33%)'},
{at: 0, expect: 'circle(60% at 10% 30%)'},
{at: 0.3, expect: 'circle(54% at 13% 27%)'},
{at: 0.6, expect: 'circle(48% at 16% 24%)'},
{at: 1, expect: 'circle(40% at 20% 20%)'},
{at: 1.5, expect: 'circle(30% at 25% 15%)'},
]);
test_no_interpolation({
property: 'shape-outside',
from: 'initial',
to: 'circle(40% at 20% 20%)',
});
test_interpolation({
property: 'shape-outside',
from: 'inherit',
to: 'circle(40% at 20% 20%)',
}, [
{at: -0.3, expect: 'circle(92% at 33% 7%)'},
{at: 0, expect: 'circle(80% at 30% 10%)'},
{at: 0.3, expect: 'circle(68% at 27% 13%)'},
{at: 0.6, expect: 'circle(56% at 24% 16%)'},
{at: 1, expect: 'circle(40% at 20% 20%)'},
{at: 1.5, expect: 'circle(20% at 15% 25%)'},
]);
test_no_interpolation({
property: 'shape-outside',
from: 'unset',
to: 'circle(40% at 20% 20%)',
});
test_interpolation({
property: 'shape-outside',
from: 'circle(100% at 0% 0%)',
to: 'circle(50% at 25% 25%)',
}, [
{at: -0.3, expect: 'circle(115% at -7.5% -7.5%)'},
{at: 0, expect: 'circle(100% at 0% 0%)'},
{at: 0.3, expect: 'circle(85% at 7.5% 7.5%)'},
{at: 0.6, expect: 'circle(70% at 15% 15%)'},
{at: 1, expect: 'circle(50% at 25% 25%)'},
{at: 1.5, expect: 'circle(25% at 37.5% 37.5%)'}
]);
test_interpolation({
property: 'shape-outside',
from: 'ellipse(100% 100% at 0% 0%)',
to: 'ellipse(50% 50% at 25% 25%)',
}, [
{at: -0.3, expect: 'ellipse(115% 115% at -7.5% -7.5%)'},
{at: 0, expect: 'ellipse(100% 100% at 0% 0%)'},
{at: 0.3, expect: 'ellipse(85% 85% at 7.5% 7.5%)'},
{at: 0.6, expect: 'ellipse(70% 70% at 15% 15%)'},
{at: 1, expect: 'ellipse(50% 50% at 25% 25%)'},
{at: 1.5, expect: 'ellipse(25% 25% at 37.5% 37.5%)'}
]);
test_interpolation({
property: 'shape-outside',
from: 'polygon(nonzero, 0px 0px, 25px 25px, 50px 50px)',
to: 'polygon(nonzero, 25px 25px, 50px 50px, 75px 75px)',
}, [
{at: -0.3, expect: 'polygon(nonzero, -7.5px -7.5px, 17.5px 17.5px, 42.5px 42.5px)'},
{at: 0, expect: 'polygon(nonzero, 0px 0px, 25px 25px, 50px 50px)'},
{at: 0.3, expect: 'polygon(nonzero, 7.5px 7.5px, 32.5px 32.5px, 57.5px 57.5px)'},
{at: 0.6, expect: 'polygon(nonzero, 15px 15px, 40px 40px, 65px 65px)'},
{at: 1, expect: 'polygon(nonzero, 25px 25px, 50px 50px, 75px 75px)'},
{at: 1.5, expect: 'polygon(nonzero, 37.5px 37.5px, 62.5px 62.5px, 87.5px 87.5px)'}
]);
test_no_interpolation({
property: 'shape-outside',
from: 'polygon(evenodd, 0px 0px, 25px 25px, 50px 50px)',
to: 'polygon(nonzero, 25px 25px, 50px 50px, 75px 75px)',
});
test_interpolation({
property: 'shape-outside',
from: 'polygon(round 10px, 0px 0px, 100px 0px, 100px 100px)',
to: 'polygon(round 30px, 50px 50px, 150px 50px, 150px 150px)',
}, [
{at: -0.3, expect: 'polygon(round 4px, -15px -15px, 85px -15px, 85px 85px)'},
{at: 0, expect: 'polygon(round 10px, 0px 0px, 100px 0px, 100px 100px)'},
{at: 0.3, expect: 'polygon(round 16px, 15px 15px, 115px 15px, 115px 115px)'},
{at: 0.6, expect: 'polygon(round 22px, 30px 30px, 130px 30px, 130px 130px)'},
{at: 1, expect: 'polygon(round 30px, 50px 50px, 150px 50px, 150px 150px)'},
{at: 1.5, expect: 'polygon(round 40px, 75px 75px, 175px 75px, 175px 175px)'},
]);
test_interpolation({
property: 'shape-outside',
from: 'polygon(round 0px, 0px 0px, 100px 0px, 100px 100px)',
to: 'polygon(round 20px, 0px 0px, 100px 0px, 100px 100px)',
}, [
{at: 0, expect: 'polygon(0px 0px, 100px 0px, 100px 100px)'},
{at: 0.5, expect: 'polygon(round 10px, 0px 0px, 100px 0px, 100px 100px)'},
{at: 1, expect: 'polygon(round 20px, 0px 0px, 100px 0px, 100px 100px)'},
]);
test_interpolation({
property: 'shape-outside',
from: 'polygon(0px 0px, 100px 0px, 100px 100px)',
to: 'polygon(round 20px, 0px 0px, 100px 0px, 100px 100px)',
}, [
{at: 0, expect: 'polygon(0px 0px, 100px 0px, 100px 100px)'},
{at: 0.5, expect: 'polygon(round 10px, 0px 0px, 100px 0px, 100px 100px)'},
{at: 1, expect: 'polygon(round 20px, 0px 0px, 100px 0px, 100px 100px)'},
]);
test_interpolation({
property: 'shape-outside',
from: 'inset(100%)',
to: 'inset(120%)',
}, [
{at: -0.3, expect: 'inset(94%)'},
{at: 0, expect: 'inset(100%)'},
{at: 0.3, expect: 'inset(106%)'},
{at: 0.6, expect: 'inset(112%)'},
{at: 1, expect: 'inset(120%)'},
{at: 1.5, expect: 'inset(130%)'},
]);
// All <basic-shape-rect> functions (inset, rect, xywh) compute to the
// equivalent inset() and interpolate identically. Exercise every
// from/to combination of the three syntaxes, with and without a
// matching <shape-box>. A and B below describe the same rectangle in
// all three forms (per side: A = top 10%, right 20%, bottom 30%,
// left 40%; B = top 50%, right 20%, bottom 30%, left 20%).
for (const box of ['', ' border-box']) {
const A = {
inset: 'inset(10% 20% 30% 40%)' + box,
rect: 'rect(10% 80% 70% 40%)' + box,
xywh: 'xywh(40% 10% 40% 60%)' + box,
};
const B = {
inset: 'inset(50% 20% 30% 20%)' + box,
rect: 'rect(50% 80% 70% 20%)' + box,
xywh: 'xywh(20% 50% 60% 20%)' + box,
};
const expectations = [
{at: 0, expect: 'inset(10% 20% 30% 40%)' + box},
{at: 0.5, expect: 'inset(30% 20% 30% 30%)' + box},
{at: 1, expect: 'inset(50% 20% 30% 20%)' + box},
];
for (const fromForm of ['inset', 'rect', 'xywh']) {
for (const toForm of ['inset', 'rect', 'xywh']) {
test_interpolation({
property: 'shape-outside',
from: A[fromForm],
to: B[toForm],
}, expectations);
}
}
}
// Mixed forms with a round radius (not covered by the loop above).
test_interpolation({
property: 'shape-outside',
from: 'xywh(0px 10% 100px 40% round 20px)',
to: 'rect(20% 50% 200px 20px)',
}, [
{at: 0, expect: 'inset(10% calc(100% - 100px) 50% 0px round 20px)'},
{at: 0.5, expect: 'inset(15% calc(75% - 50px) calc(75% - 100px) 10px round 10px)'},
{at: 1, expect: 'inset(20% 50% calc(100% - 200px) 20px)'},
]);
test_no_interpolation({
property: 'shape-outside',
from: 'none',
to: 'ellipse(100% 100% at 0% 0%)',
});
// TODO: add intermediate keyframe tests when CSS shapes position computed values are cleaned up
test_interpolation({
property: 'shape-outside',
from: 'circle(25% at right 5% bottom 15px)',
to: 'circle(45% at right 25% bottom 35px)',
}, [
{at: 0.25, expect: 'circle(30% at 90% calc(-20px + 100%))'},
{at: 0.5, expect: 'circle(35% at 85% calc(-25px + 100%))'},
{at: 0.75, expect: 'circle(40% at 80% calc(-30px + 100%))'},
]);
// Other shapes also interpolate when both endpoints carry the same <shape-box>.
test_interpolation({
property: 'shape-outside',
from: 'circle(20% at 10% 10%) border-box',
to: 'circle(40% at 30% 30%) border-box',
}, [
{at: 0, expect: 'circle(20% at 10% 10%) border-box'},
{at: 0.5, expect: 'circle(30% at 20% 20%) border-box'},
{at: 1, expect: 'circle(40% at 30% 30%) border-box'},
]);
// Mismatched or missing <shape-box> is discrete.
test_no_interpolation({
property: 'shape-outside',
from: 'inset(10px) border-box',
to: 'inset(30px) padding-box',
});
test_no_interpolation({
property: 'shape-outside',
from: 'inset(10px)',
to: 'inset(30px) border-box',
});
test_no_interpolation({
property: 'shape-outside',
from: 'rect(10px 90px 90px 10px) border-box',
to: 'rect(30px 70px 70px 30px) padding-box',
});
test_no_interpolation({
property: 'shape-outside',
from: 'xywh(10px 10px 80px 80px) padding-box',
to: 'xywh(30px 30px 40px 40px) content-box',
});
</script>