Bug 1253493 - Part2: Show iterationStart to the GUI. r=pbro

This commit is contained in:
Daisuke Akatsuka 2016-03-23 13:06:43 +09:00
Родитель 459706a9ee
Коммит a096e3264f
6 изменённых файлов: 148 добавлений и 36 удалений

Просмотреть файл

@ -57,15 +57,22 @@ AnimationTimeBlock.prototype = {
let {x, iterationW, delayX, delayW, negativeDelayW} =
TimeScale.getAnimationDimensions(animation);
// background properties for .iterations element
let backgroundIterations = TimeScale.getIterationsBackgroundData(animation);
createNode({
parent: this.containerEl,
attributes: {
"class": "iterations" + (state.iterationCount ? "" : " infinite"),
// Individual iterations are represented by setting the size of the
// repeating linear-gradient.
// The background-size, background-position, background-repeat represent
// iterationCount and iterationStart.
"style": `left:${x}%;
width:${iterationW}%;
background-size:${100 / (state.iterationCount || 1)}% 100%;`
background-size:${backgroundIterations.size}% 100%;
background-position:${backgroundIterations.position}% 0;
background-repeat:${backgroundIterations.repeat};`
}
});

Просмотреть файл

@ -43,13 +43,19 @@ Keyframes.prototype = {
this.propertyName = propertyName;
this.animation = animation;
let iterationStartOffset =
animation.state.iterationStart % 1 == 0
? 0
: 1 - animation.state.iterationStart % 1;
this.keyframesEl.classList.add(animation.state.type);
for (let frame of this.keyframes) {
let offset = frame.offset + iterationStartOffset;
createNode({
parent: this.keyframesEl,
attributes: {
"class": "frame",
"style": `left:${frame.offset * 100}%;`,
"style": `left:${offset * 100}%;`,
"data-offset": frame.offset,
"data-property": propertyName,
"title": frame.value

Просмотреть файл

@ -4,33 +4,81 @@
"use strict";
// Check iteration start.
// Check that the iteration start is displayed correctly in time blocks.
add_task(function*() {
yield addTab(URL_ROOT + "doc_script_animation.html");
let {panel, controller} = yield openAnimationInspector();
let {panel} = yield openAnimationInspector();
let timelineComponent = panel.animationsTimelineComponent;
let timeBlockComponents = timelineComponent.timeBlocks;
let detailsComponents = timelineComponent.details;
info("Getting the animation element by script");
let timelineEl = panel.animationsTimelineComponent.rootWrapperEl;
let timeBlockNameEl = timelineEl.querySelector(".time-block .name");
for (let i = 0; i < timeBlockComponents.length; i++) {
info(`Expand time block ${i} so its keyframes are visible`);
yield clickOnAnimation(panel, i);
let state = controller.animationPlayers[0].state;
let title = timeBlockNameEl.getAttribute("title");
if (state.iterationStart !== 0) {
let iterationStartTime = state.iterationStart * state.duration / 1000;
let iterationStartTimeString =
iterationStartTime.toLocaleString(undefined,
{ maximumFractionDigits: 2,
minimumFractionDigits: 2 })
.replace(".", "\\.");
let iterationStartString =
state.iterationStart.toString().replace(".", "\\.");
let regex = new RegExp("Iteration start: " + iterationStartString +
" \\(" + iterationStartTimeString + "s\\)");
ok(title.match(regex), "The tooltip shows the iteration start");
} else {
ok(!title.match(/Iteration start:/),
"The tooltip doesn't show the iteration start");
info(`Check the state of time block ${i}`);
let {containerEl, animation: {state}} = timeBlockComponents[i];
checkAnimationTooltip(containerEl, state);
checkIterationBackground(containerEl, state);
// Get the first set of keyframes (there's only one animated property
// anyway), and the first frame element from there, we're only interested in
// its offset.
let keyframeComponent = detailsComponents[i].keyframeComponents[0];
let frameEl = keyframeComponent.keyframesEl.querySelector(".frame");
checkKeyframeOffset(containerEl, frameEl, state);
}
});
function checkAnimationTooltip(el, {iterationStart, duration}) {
info("Check an animation's iterationStart data in its tooltip");
let title = el.querySelector(".name").getAttribute("title");
let iterationStartTime = iterationStart * duration / 1000;
let iterationStartTimeString = iterationStartTime.toLocaleString(undefined, {
maximumFractionDigits: 2,
minimumFractionDigits: 2
}).replace(".", "\\.");
let iterationStartString = iterationStart.toString().replace(".", "\\.");
let regex = new RegExp("Iteration start: " + iterationStartString +
" \\(" + iterationStartTimeString + "s\\)");
ok(title.match(regex), "The tooltip shows the expected iteration start");
}
function checkIterationBackground(el, {iterationCount, iterationStart}) {
info("Check the background-image used to display iterations is offset " +
"correctly to represent the iterationStart");
let iterationsEl = el.querySelector(".iterations");
let start = getIterationStartFromBackground(iterationsEl, iterationCount);
is(start, iterationStart % 1,
"The right background-position for iteration start");
}
function getIterationStartFromBackground(el, iterationCount) {
if (iterationCount == 1) {
let size = parseFloat(/([.\d]+)%/.exec(el.style.backgroundSize)[1]);
return 1 - size / 100;
}
let size = parseFloat(/([.\d]+)%/.exec(el.style.backgroundSize)[1]);
let position = parseFloat(/([-\d]+)%/.exec(el.style.backgroundPosition)[1]);
let iterationStartW = -position / size * (100 - size);
let rounded = Math.round(iterationStartW * 100);
return rounded / 10000;
}
function checkKeyframeOffset(timeBlockEl, frameEl, {iterationStart}) {
info("Check that the first keyframe is offset correctly");
let start = getIterationStartFromLeft(frameEl);
is(start, iterationStart % 1, "The frame offset for iteration start");
}
function getIterationStartFromLeft(el) {
let left = 100 - parseFloat(/(\d+)%/.exec(el.style.left)[1]);
return left / 100;
}

Просмотреть файл

@ -3,7 +3,19 @@
<head>
<meta charset="UTF-8">
<style>
#target {
#target1 {
width: 50px;
height: 50px;
background: red;
}
#target2 {
width: 50px;
height: 50px;
background: green;
}
#target3 {
width: 50px;
height: 50px;
background: blue;
@ -11,17 +23,33 @@
</style>
</head>
<body>
<div id="target"></div>
<div id="target1"></div>
<div id="target2"></div>
<div id="target3"></div>
<script>
"use strict";
var el = document.getElementById("target");
el.animate(
[{ opacity: 0 }, { opacity: 1 }],
{ duration: 1000,
iterationStart: 0.5,
fill: "both" }
);
document.getElementById("target1").animate([{ opacity: 0 }, { opacity: 1 }],
{ duration: 100,
iterations: 2,
iterationStart: 0.25,
fill: "both" }
);
document.getElementById("target2").animate([{ opacity: 0 }, { opacity: 1 }],
{ duration: 100,
iterations: 1,
iterationStart: 0.25,
fill: "both" }
);
document.getElementById("target3").animate([{ opacity: 0 }, { opacity: 1 }],
{ duration: 100,
iterations: 1.5,
iterationStart: 2.5,
fill: "both" }
);
</script>
</body>
</html>

Просмотреть файл

@ -309,6 +309,31 @@ var TimeScale = {
let negativeDelayW = delay < 0 ? delayW : 0;
return {x, w, iterationW, delayX, delayW, negativeDelayW};
},
/**
* Given an animation, get the background data for .iterations element.
* This background represents iterationCount and iterationStart.
* Returns three properties.
* 1. size: x of background-size (%)
* 2. position: x of background-position (%)
* 3. repeat: background-repeat (string)
*/
getIterationsBackgroundData: function({state}) {
let iterationCount = state.iterationCount || 1;
let iterationStartW = state.iterationStart % 1 * 100;
let background = {};
if (iterationCount == 1) {
background.size = 100 - iterationStartW;
background.position = 0;
background.repeat = "no-repeat";
} else {
background.size = 1 / iterationCount * 100;
background.position = -iterationStartW * background.size /
(100 - background.size);
background.repeat = "repeat-x";
}
return background;
}
};

Просмотреть файл

@ -330,13 +330,11 @@ body {
borders between each iteration. These borders must have the same color as
the border of this element */
background-image:
linear-gradient(to right,
linear-gradient(to left,
var(--timeline-border-color) 0,
var(--timeline-border-color) 1px,
transparent 1px,
transparent 2px);
background-repeat: repeat-x;
background-position: -1px 0;
border: 1px solid var(--timeline-border-color);
/* Border-right is already handled by the gradient */
border-width: 1px 0 1px 1px;